<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://alsuren.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="http://alsuren.github.io/" rel="alternate" type="text/html" /><updated>2026-03-11T11:14:28+00:00</updated><id>http://alsuren.github.io/feed.xml</id><title type="html">alsuren.github.io</title><subtitle>GitHub Pages for alsuren</subtitle><entry><title type="html">A Love Letter to Grist</title><link href="http://alsuren.github.io/2026/03/09/a-love-letter-to-grist.html" rel="alternate" type="text/html" title="A Love Letter to Grist" /><published>2026-03-09T00:00:00+00:00</published><updated>2026-03-09T00:00:00+00:00</updated><id>http://alsuren.github.io/2026/03/09/a-love-letter-to-grist</id><content type="html" xml:base="http://alsuren.github.io/2026/03/09/a-love-letter-to-grist.html"><![CDATA[<p>My previous company was called <a href="https://www.opvia.io/">Opvia</a>. It was basically <a href="https://www.notion.so/">Notion</a>, but with stronger guarantees around audit logging and version management, so it was easier to sell into regulated industries. I was pretty proud of a lot of the things I built there, but I wasn’t so okay with the perverse incentives that come with being a SaaS system of record company. Specifically, I was haunted by the question “are we sticky with our customers because we make their lives better than using excel, or are we sticky because once you’ve spent all this time getting data into the system in a nice relational way, and it’s a massive pain to get the data out again?”</p>

<p>After leaving Opvia, I wanted to make something that did all of the same stuff but without the data lock-in and perverse incentives that we had. I had stumbled across the <a href="https://www.inkandswitch.com/local-first/">local-first movement</a>, and all the crazy things people were doing with SQLite. In my vision, I wanted something that worked like Excel from my childhood (where you pass around .xls files) but each file contained the whole database. I decided that I would solve the multiplayer story using something like <a href="https://github.com/vlcn-io/cr-sqlite">cr-sqlite</a>, and was blissfully naive about the conflict resolution piece. I suppose what I was building in my head was Microsoft Access.</p>

<p>Recently, I was browsing around the <a href="https://www.emfcamp.org/">EMF Camp</a> site and stumbled upon <a href="https://www.getgrist.com/">Grist</a>. It is open source and uses sqlite as the file format. I think this is basically what I would have built at the end of that summer, if I was not in danger of running out of money. I suddenly became really excited about this, and told everyone who would listen.</p>

<p>It is basically <a href="https://www.airtable.com/">Airtable</a>, but open source and with data sovereignty. Each column directly maps to a column in an sqlite database, and each row to a row. There are a few reserved metadata tables for configuring widgets and views, but if you fetch the .grist file from the cloud server (or <a href="https://github.com/gristlabs/grist-static">grist-static</a>) then you can open it in the sqlite3 CLI and <code class="language-plaintext highlighter-rouge">select * from mytable</code> will do exactly what you expect.</p>

<p>In the last few years I have gone all in on sqlite and local-first (including going to <a href="https://www.localfirstconf.com/">Local-First Conf</a> in Berlin and the local-first devroom at <a href="https://fosdem.org/">FOSDEM</a>, and even joining the <a href="https://developers.cloudflare.com/d1/">D1</a> managed sqlite database team at <a href="https://www.cloudflare.com/">Cloudflare</a>).</p>

<p>I was surprised not to see more hype from Grist around local-first. I had stumbled across grist-static (which is a fully in-browser version of Grist that can be hosted on <a href="https://pages.github.com/">GitHub Pages</a>). I assumed that they were probably doing pretty well against most of the <a href="https://www.inkandswitch.com/local-first/">local-first ideals from the paper</a>:</p>

<h3 id="1-no-spinners-your-work-at-your-fingertips">1. No spinners: your work at your fingertips</h3>

<p>If grist-static exists then surely everything can be done in the browser completely offline, right?</p>

<p>The future looks like it could be pulled in this direction, but unfortunately that is not the primary architecture right now. As it stands, when you open a Grist document, it fetches the sqlite file from S3 into a server and starts a Python sandbox to serve the backend for your database. If you lose connection then it will show a toast for a second telling you it is in read-only mode before it reconnects.</p>

<p>10 out of 10 for grist-static, but 0 out of 10 for grist-proper.</p>

<h3 id="2-your-work-is-not-trapped-on-one-device">2. Your work is not trapped on one device</h3>

<p>Everything is done on the server, but you can access it from anywhere and the format that the server stores your work in is the same sqlite .grist file format that you are given when you download the database for local use.</p>

<p>10 out of 10.</p>

<h3 id="3-the-network-is-optional">3. The network is optional</h3>

<p>8 out of 10 for grist-static but 0 out of 10 for grist-proper. See point 1.</p>

<h3 id="4-seamless-collaboration-with-your-colleagues">4. Seamless collaboration with your colleagues</h3>

<p>I haven’t been using grist to collaborate with anyone so far, but it has public projects and you can invite people to your team. They even have a fork and merge ui, which I’m looking for an excuse to play with. Grist also got a bunch of contributions from the French government, so I expect it to work reasonably well for government department sized teams.</p>

<p>If I could change anything here, I would make it easier to export a .grist file, edit it in some external program and then use the fork and merge ui to compare changes before overwriting the original on the server. It currently defaults to creating a new document, which is <em>technically</em> nondestructive, but not very satisfying if you have public links that point into the original and you don’t want to break them by archiving it. I get that this kind of reconciliation problem is <em>extremely hard</em>, and an area of <a href="https://www.inkandswitch.com/upwelling">ongoing</a> <a href="https://www.inkandswitch.com/patchwork/notebook/">research</a>. I also understand that getting it wrong can be <em>disastrous</em>, so I understand why they’ve done it like they have. A boy can dream though, right?</p>

<p>0 out of 10 for grist-static but 9 out of 10 for grist-proper.</p>

<h3 id="5-the-long-now">5. The Long Now</h3>

<p>This is the whole fucking deal. I can’t emphasize this enough. This is important especially right now, and especially for people who <a href="https://fosdem.org/2026/schedule/event/SFKNTZ-welcome_to_fosdem_2026/">paid attention in history class</a>.</p>

<p>When I got bitten by the local-first bug during my summer after Opvia, it felt like going back to <a href="https://www.collabora.co.uk/">my Freedesktop roots</a>. People were taking principled stances on things, and talking about <a href="https://www.inkandswitch.com/local-first/">certain ideals</a> in the same way that Free Software people talk about <a href="https://en.wikipedia.org/wiki/The_Free_Software_Definition#The_Four_Essential_Freedoms">certain freedoms</a>. This ideal is the place where the two communities overlap.</p>

<p>So how does Grist do here?</p>

<p>Grist’s backing store and interchange format are .grist files. These are sqlite databases under the hood, which is what your browser uses, and also most iphone and android apps. The sqlite file format is basically infrastructure at this point, and grist uses it in a way that I would have never dreamed of when I was working at Opvia. A .grist file’s database structure closely mirrors what you see in the UI (with some extra book-keeping tables off on the side). Whenever you make a change to your table structure, grist runs ALTER TABLE on the sqlite database to make the change. This means that you can drop into the sqlite3 cli or tableplus and do <em>whatever you want</em> to the underlying data without any grist-specific tooling in sight, and without any fear of data getting silently omitted in the export process. For a multi-tenant system running on top of postgres, this would be unheard of. Really nice.</p>

<p>To give another comparison, the other local-first Notion clone that I tried out in my summer off was <a href="https://affine.pro/">AFFiNE</a>. It can also run entirely in the browser, and has an offline-capable CRDT-based sync engine. On the surface of it, .grist files and .affine files are both sqlite databases, but .affine’s tables are just a bunch of opaque binary CRDT nonsense. I would not expect to be able to edit a .affine file in any tool other than affine itself.</p>

<p>AFFiNE is AGPL licensed, so in theory you can keep it going in the long now. I don’t expect anyone to build a hosting business around it if affine folds though. When I tried to contribute to it, their issue tracker was in a closed linear project, and it was an uphill struggle because I couldn’t see whether my contributions were aligned with their vision at all. It seems like their github issue tracker is more active now, so maybe that changed since then.</p>

<p>Grist’s core is Apache licensed. This is a solid license that smells like Internet Infrastructure. Issues are public, and there are a bunch of contributions from external organisations, including the french government.</p>

<p>10 out of 10.</p>

<h3 id="6-security-and-privacy-by-default">6. Security and privacy by default</h3>

<p>I was in the radical openness camp until Facebook made that deeply unfashionable. I used to keep a <a href="https://trello.com/b/oUj099Rh">public trello board</a> of all of my project ideas (which at some point got <a href="https://airtable.com/shrxzaWek4fIuLFys/tbl6uWspa4WBsm3Nr/viwA6Pty74btc1Ltm?blocks=hide">moved to airtable</a> and then maybe affine before being abandoned). I’m now I’m starting to build that up again <a href="https://alsuren.getgrist.com/sAknM8Q9Z4cB/Public-Projects/p/5">in grist</a> (one day I will build a kanban view that I actually like, I promise). I also have a private database for tracking house things that I share only with people I live with. I do not expect to be served adverts about the contents of either of these databases.</p>

<p>Grist also has some fancy <a href="https://support.getgrist.com/access-rules/">row level security</a> things that are enforced server-side, but fundamentally the server needs to be able to read the whole database, and retrofitting end to end encryption into the thing would be quite tricky.</p>

<p>[Note: when I shared this post with folks on the grist discord, they mentioned that grist started out as a desktop app with end-to-end encrypted sync. You can still feel those roots in a bunch of places, even though the architecture has changed a lot since then.]</p>

<p>7 out of 10?</p>

<h3 id="7-you-retain-ultimate-ownership-and-control">7. You retain ultimate ownership and control</h3>

<p>I fundamentally trust Grist to act in my interests more than I trust Airtable or Notion. The fact that a self hosted version is just a single docker command away, and the statically hosted version is so capable, gives me confidence that I will be able to eject onto <a href="https://fly.io/">fly.io</a> if I need to. I would love to say <a href="https://developers.cloudflare.com/durable-objects/">Durable Objects</a> here, but the lack of ability to fetch/post the underlying sqlite backing store for a Durable Object makes some things hard.</p>

<p>One thing to note is that widgets are basically iframes, and can point basically anywhere. I didn’t mention it in The Long Now, but I wonder if there might be some scope for golang-style caching proxies, or even a system for vendoring widgets into your .grist file (grist has a jsfiddle-like UI for authoring widgets if you want, and they do get stored in the .grist file). Honestly though, if I lose a couple of widgets over the decades, but my data is in the shape that <strong>I</strong> specified in the UI, it won’t be much work to build replacements.</p>

<p>Bonus: if you consider Grist widgets to be apps and Grist to be some data infrastructure/container then maybe it is already approaching <a href="https://bsky.app/profile/orionreed.com/post/3mgb6vig4b22m">the vision that Orion Reed is describing in this post</a>.</p>

<p>10 out of 10 again.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I would not say that Grist has all of the local-first ideals nailed. I don’t think anyone does. I realised as I was using it that the data sovereignty piece is the one that I really care about though, and that is why I am all in on Grist at the moment. I would much rather have Grist with its long-now-proof sqlite storage than AFFiNE with its magical real time sync engine and no trust that I will be able to get my data out in 10 years time.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[My previous company was called Opvia. It was basically Notion, but with stronger guarantees around audit logging and version management, so it was easier to sell into regulated industries. I was pretty proud of a lot of the things I built there, but I wasn’t so okay with the perverse incentives that come with being a SaaS system of record company. Specifically, I was haunted by the question “are we sticky with our customers because we make their lives better than using excel, or are we sticky because once you’ve spent all this time getting data into the system in a nice relational way, and it’s a massive pain to get the data out again?”]]></summary></entry><entry><title type="html">Local-First Notes #1: Riffle</title><link href="http://alsuren.github.io/2025/05/17/local-first-notes-1-riffle.html" rel="alternate" type="text/html" title="Local-First Notes #1: Riffle" /><published>2025-05-17T00:00:00+00:00</published><updated>2025-05-17T00:00:00+00:00</updated><id>http://alsuren.github.io/2025/05/17/local-first-notes-1-riffle</id><content type="html" xml:base="http://alsuren.github.io/2025/05/17/local-first-notes-1-riffle.html"><![CDATA[<p>I’m starting a series of notes as I prepare for <a href="https://www.localfirstconf.com/">Local First Conference</a>. As I read through related literature, I’ll share my thoughts and questions here. This post is inspired by <a href="https://riffle.systems/essays/prelude/">Riffle’s Prelude essay</a>.</p>

<h3 id="short-circuiting-query-re-runs-on-the-block-level">Short-circuiting query re-runs on the block level</h3>

<p>For reactive queries, could we:</p>

<ul>
  <li>submit initial query to query engine</li>
  <li>receive and store:
    <ul>
      <li>results</li>
      <li>merkle tree of index-related pages that have been read to satisfy the query</li>
      <li>merkle tree of data-related pages that have been read to satisfy the query</li>
    </ul>
  </li>
</ul>

<p>When an update comes in:</p>

<ul>
  <li>re-run query, passing in merkle trees</li>
  <li>if none of the pages have changed, return 304 Not Modified</li>
  <li>if index pages have changed, re-run up to the point where you know which data pages you will read. If none of these have changed, return 304 Not Modified</li>
</ul>

<p>This means that if you have a massive song list and you’re looking at the top page, a change to a song in the bottom page won’t cause your UI to re-render.</p>

<p>I have no idea whether this kind of low-level control of SQLite is even possible, or whether it is possible to split its pages into index and data at the VFS layer (or whether it even makes VFS read calls if it’s already got the page in its cache).</p>

<p>I’d be interested in hearing thoughts from others who have experience with SQLite internals or similar reactive query optimizations with sqlite. Reach out on <a href="https://bsky.app/profile/alsuren.bsky.social">bluesky</a>, <a href="https://mastodon.me.uk/@alsuren">mastodon</a>, or discord.</p>

<h3 id="nested-results">Nested results</h3>

<blockquote>
  <p>Standard SQL doesn’t support nesting, even in the projection step (i.e., what describes the shape of the results). We’re big fans of data normalization, but it’s very convenient to nest data when producing outputs.</p>

  <p>There are various extensions to SQL that support nesting, but many of them are not that good and the good ones are not widely available.</p>
</blockquote>

<p>I am reminded of postgraphile’s query builder, which generates a bunch of nested json_agg() calls and <code class="language-plaintext highlighter-rouge">with</code> statements like this:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">with</span> <span class="n">__local_0__</span> <span class="k">as</span> <span class="p">(</span><span class="k">select</span> <span class="nv">"user"</span><span class="p">.</span><span class="nv">"name"</span><span class="p">,</span> <span class="nv">"user"</span><span class="p">.</span><span class="nv">"age"</span><span class="p">,</span> <span class="nv">"user"</span><span class="p">.</span><span class="nv">"height"</span> <span class="k">from</span> <span class="nv">"user"</span> <span class="k">where</span> <span class="n">created_at</span> <span class="o">&gt;</span> <span class="n">NOW</span><span class="p">()</span> <span class="o">-</span> <span class="n">interval</span> <span class="s1">'3 years'</span> <span class="k">and</span> <span class="n">age</span> <span class="o">&gt;</span> <span class="err">$</span><span class="mi">1</span><span class="p">)</span>
<span class="k">select</span>
  <span class="p">(</span><span class="k">select</span> <span class="n">json_agg</span><span class="p">(</span><span class="n">row_to_json</span><span class="p">(</span><span class="n">__local_0__</span><span class="p">))</span> <span class="k">from</span> <span class="n">__local_0__</span><span class="p">)</span> <span class="k">as</span> <span class="n">all_data</span><span class="p">,</span>
  <span class="p">(</span><span class="k">select</span> <span class="k">max</span><span class="p">(</span><span class="n">age</span><span class="p">)</span> <span class="k">from</span> <span class="n">__local_0__</span><span class="p">)</span> <span class="k">as</span> <span class="n">max_age</span>
</code></pre></div></div>
<p>(example from <a href="https://github.com/graphile/crystal/tree/main/utils/pg-sql2">pg-sql2</a> but many of the postgraphile queries take a similar form).</p>

<p>I wonder if this approach would be valuable in a reactive sqlite browser datastore.</p>

<h3 id="stay-tuned">Stay Tuned</h3>

<p>Stay tuned on RSS or <a href="https://bsky.app/profile/alsuren.bsky.social">bluesky</a> or <a href="https://mastodon.me.uk/@alsuren">mastodon</a> for more half-formed thoughts.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[I’m starting a series of notes as I prepare for Local First Conference. As I read through related literature, I’ll share my thoughts and questions here. This post is inspired by Riffle’s Prelude essay.]]></summary></entry><entry><title type="html">FOSDEM Highlights</title><link href="http://alsuren.github.io/2025/02/07/fosdem-highlights.html" rel="alternate" type="text/html" title="FOSDEM Highlights" /><published>2025-02-07T00:00:00+00:00</published><updated>2025-02-07T00:00:00+00:00</updated><id>http://alsuren.github.io/2025/02/07/fosdem-highlights</id><content type="html" xml:base="http://alsuren.github.io/2025/02/07/fosdem-highlights.html"><![CDATA[<p>I was at FOSDEM last weekend, after ~15 years away. I though I should probably write up my highlights while they’re fresh in my head.</p>

<p>They should be in chronological order.</p>

<h4 id="the-road-to-open-source-general-purpose-humanoids-with-dora-rs-by-tao-xavier"><a href="https://fosdem.sojourner.rocks/2025/event/dff0c251-8b99-5e7b-a1b4-309ac341a92d">The road to open source General Purpose Humanoids with dora-rs</a> by Tao xavier</h4>

<p>This talk is about a replacement for the ROS framework. It is explicitly trying to be a more Machine-Learning-Researcher-friendly alternative to ROS. Rather than having to have a separate machine/vm running linux + CMake, you can just pip install everything on Windows/Mac/Linux, and it will download the precompiled (typically rust or python) code. It seems to have some funding from huggingface, so from a money point of view, it could be considered a distant cousin of lerobot.</p>

<p>The core seems to be a message bus written with shared memory and apache arrow, which can be used to connect bits of a robot together. This is very similar to ROS.</p>

<p>It also does orchestration by reading yaml files which describe how the bits of the robot fit together. I find it interesting that lerobot previously relied heavily on yaml configurations (using hydra), and recently switched to using straight up python files for its configuration. The reason given by lerobot was that IDE completion support was better if everything is in one language. I guess this doesn’t apply to dora, because it is already in two (python and rust).</p>

<p>They finished with a demo involving a reachy robot and some voice commands, using rerun.io for all of the UI elements.</p>

<h4 id="zap-the-flakes-leveraging-ai-to-combat-flaky-tests-with-cannier-by-daniel-hiller"><a href="https://fosdem.sojourner.rocks/2025/event/197ed0a3-5fb7-54e9-874f-031f3c707cde">Zap the Flakes! Leveraging AI to Combat Flaky Tests with CANNIER</a> by Daniel Hiller</h4>

<p>This one was an interesting one. I feel like a lot of projects I’ve worked on would benefit from a tool that can parse github actions logs to spot flakey tests, but that’s not what this is about. This is about something that tries to stop flakey tests getting merged to main in the first place. It does this by running the test once and doing a bunch of static+dynamic analysis and then uses the resulting features to predict the probability of it being flakey (using a random forest model). The original implementation is in python, and the talk was about a WIP port to Go for Kubernetes tests.</p>

<p>I caught Daniel after the talk and had a great discussion about the inverted testing pyramid approach. It is definitely much easier to write integration tests and satisfy yourself and stakeholders that they actually test the thing you care about, but 1 hour CI test runs are no fun at all.</p>

<p>I wonder whether it would be possible to write a tool that can collect coverage information from an end-to-end integration tests suite and help you pick targets to distill into a <a href="https://matklad.github.io/2021/05/31/how-to-test.html">sans-io data driven integration test suite</a>. I’ve heard that LLMs are quite good at writing tests these days, so this coverage-driven test distillation might be something that is already within reach.</p>

<h4 id="how-a-city-platform-became-a-global-community-by-carolina-romero-cruz"><a href="https://fosdem.sojourner.rocks/2025/event/1e97a5f5-3b7b-5f27-b789-125a7943236f">How a City Platform Became a Global Community</a> by Carolina Romero Cruz</h4>

<p>I went to this wondering whether they would be talking about something like <a href="https://pol.is/">Polis</a>. It seems like <a href="https://decidim.org/">Decidim</a> is a much more traditional and multi-faceted collaboration tool than this. It started out in Barcelona and then exploded all over the place during lockdowns, with a bunch of twists and turns in its internal governance and funding journey. One of the takeaways for me was that government procurement is a nightmare.</p>

<h4 id="how-i-optimized-zbus-by-95-by-zeeshan-ali-khan"><a href="https://fosdem.sojourner.rocks/2025/event/c6b5c626-f143-5de3-ad39-607212b13f99">How I optimized zbus by 95%</a> by Zeeshan Ali Khan</h4>

<p>I arrived late to this one, because I was actually interested in <a href="https://fosdem.sojourner.rocks/2025/event/704b1ba0-5a17-5785-9367-1f840432d40a">Programming ROS 2 with Rust</a> by Júlia Marsal Perendreu, which was afterwards. This talk made my day because right at the very end, he mentions that <a href="https://varlink.org/">varlink</a> is poised to replace dbus in a bunch of places. I’m not currently using desktop linux, so I will probably watch this one from a distance. I was part of the Telepathy core team back at Collabora, and <a href="https://github.com/alsuren/mijia-homie">mijia-homie</a> is built on top of the BlueZ dbus interface, so I have first hand experience of how funky it can be.</p>

<p>Zeeshan has done a fantastic job with zbus, and I expect him to do the same with varlink.</p>

<p>As a side note, I am hoping that I can watch the video for <a href="https://fosdem.sojourner.rocks/2025/event/90ee45c4-a33d-56a1-9435-751f9549dfc3">Adopting BlueZ in production: challenges and caveats</a> and see how it compares with our experience on mijia-homie.</p>

<h4 id="programming-ros-2-with-rust-by-júlia-marsal-perendreu"><a href="https://fosdem.sojourner.rocks/2025/event/704b1ba0-5a17-5785-9367-1f840432d40a">Programming ROS 2 with Rust</a> by Júlia Marsal Perendreu</h4>

<p>I got the impression from this talk that <a href="https://crates.io/crates/rclrs">rclrs</a> is a pretty good way to make robust production robotics applications. I mostly care about fucking around in python with desktop robot arms though, so I am more excited about lerobot and dora-rs.</p>

<h4 id="could-we-actually-replace-containers-by-dan-phillips"><a href="https://fosdem.sojourner.rocks/2025/event/eccfef06-ff0c-59a7-a1dc-c84b1cb84c9b">Could we actually replace containers?</a> by Dan Phillips</h4>

<p>This talk was a little piece of gold. The whole project is pure audacity and I love it.</p>

<p>The jist is “Why do we need to wait for this WASI thing to be specified and stabilize? We already have POSIX, that’s been stable for decades.”</p>

<p>The project provides a libc implementation that can be used to compile basically-unmodified Dockerfile descriptions to WASM targets. They have demos that can run python code in their runtime, and then use basically-standard WASM pre-execution techniques to bring the cold start times way way down compared to native. They seem to have a runtime that works in the browser, bit I suspect the networking implementation needs to call out to an external proxy, because posix sockets are quite a lot more low level than you normally have access to in the browser (I’ve not checked).</p>

<p>I caught up with Dan afterwards to congratulate him on his audacity. We had a bit of a chin-wag about how server side wasm projects are doing (we both independently know the wasmCloud people).</p>

<h4 id="a-long-short-history-of-realtime-ai-agents-by-rob-pickering"><a href="https://fosdem.sojourner.rocks/2025/event/38b00b90-0ef8-50f9-911f-e082836f28bf">A long, short history of realtime AI agents</a> by Rob Pickering</h4>

<p>The AV wasn’t working for this one, which is a shame, but it was interesting to get an insight into how modern realtime speech agents work. The general jist is that you don’t try to do speech to text and then pass text into the LLM. What you do instead is tokenize the speech directly and pass the tokens into the LLM. This is a bit like how multimodal models work with images. I get the impression that you also skip the text-to-speech step, but then I’m not sure how you turn the output voice tokens into a text transcript.</p>

<h4 id="kites-for-future---airborne-wind-energy-for-everyone-by-marc-de-laporte-benjamin-kutschan"><a href="https://fosdem.sojourner.rocks/2025/event/44e4bfb1-f99b-5a47-9899-e119f9d330a8">Kites for Future - Airborne Wind Energy for everyone</a> by Marc de Laporte, Benjamin Kutschan</h4>

<p>This one holds a special place in my heart because of <a href="https://github.com/hoverkite/hoverkite">a lockdown project</a> that I worked on with my housemate. The kitesforfuture.be team are using an architecture that’s halfway between <a href="https://x.company/projects/makani/">Google’s Makani project</a> and <a href="https://skysails-power.com/kite-power-for-mauritius/">the SkySails Mauritius project</a>. They use a rigid kite with flaps for control and propellors for launch, but they generate power at the ground station by alternating between using the crosswind effect in the power zone of the wind window and gliding with minimal tension at the edge of the window.</p>

<p>They include a bunch of cool hacks, like:</p>
<ul>
  <li>abusing the magnetic compass sensor on the esp32 for tracking line angles</li>
  <li>starting off with esp32’s wifi protocol, and switching to lora to get better range</li>
  <li>a fishing-rod-like rig for catching the kite and suspending it in mid-air for automatic re-launch
    <ul>
      <li>now that I’m having another look at the SkySails marketing site, I’m realising that they also have something similar, but <strong>much</strong> more heavyweight.</li>
    </ul>
  </li>
</ul>

<p>They also showed a video of <a href="https://www.youtube.com/watch?v=9IuRIYftyb0">a soft kite based demo at burning man</a> that uses their power plant ground station, but otherwise has a very similar architecture to SkySails. I’m very interested in this, because it sounds like the teleoperation setup could brought into the field without a ground station, and that would be <strong>much</strong> lighter than the full hoverkite setup. While I’m quite fond of our “take these COTS hardware components and hack them together” pluckiness in the hoverkite project, I do admit that adding a steering box up near the kite makes a lot of sense, especially as your lines get longer.</p>

<h4 id="privacy-first-architecture-alternatives-to-gdpr-popup-and-local-first-by-andrey-sitnik"><a href="https://fosdem.sojourner.rocks/2025/event/28cbbf35-9f71-5dc0-8d54-11731b576921">Privacy-first architecture: alternatives to GDPR popup and local-first</a> by Andrey Sitnik</h4>

<p>This was another big highlight for me. The talk abstract mentions <a href="https://dev.slowreader.app/">slowreader</a> as an example, but the talk was mostly about motivating you to actually consider privacy when building modern web apps.</p>

<p>Genuinely entertaining and very approachable.</p>

<p>He called out that most web developers only consider top-down government-based privacy threats: it is also worth considering threats from family, religious groups, local officials and local ISPs in many locales. Concretely, if you don’t trust your users’ local ISPs then it might be a good idea to proxy their requests for them, to hide them from the ISP.</p>

<p>He also called out current data hording tendencies, and suggested practical ways to be good to your users, like advocating for analytics tools that track events rather than users (and therefore don’t need GDPR popups). He suggested a good technique for advocating for the removal of user-tracking info: ask your product manager if they’ve actually used this piece of information to make a decision in the last 6 months, and when they say no, propose deleting it.</p>

<p>As an aside, I guess the FOSDEM schedule webapp that I am using could be considered privacy-first. It has a similar architecture to slowreader, storing your bookmarks locally in the browser with no cross-device syncronisation, and if you want to share your bookmarks with others (or yourself), you send <a href="https://fosdem.sojourner.rocks/2025/shared?eventIds=dff0c251-8b99-5e7b-a1b4-309ac341a92d,197ed0a3-5fb7-54e9-874f-031f3c707cde,1e97a5f5-3b7b-5f27-b789-125a7943236f,c6b5c626-f143-5de3-ad39-607212b13f99,eccfef06-ff0c-59a7-a1dc-c84b1cb84c9b,38b00b90-0ef8-50f9-911f-e082836f28bf,44e4bfb1-f99b-5a47-9899-e119f9d330a8,28cbbf35-9f71-5dc0-8d54-11731b576921,09fd9904-eadf-54b4-88d8-4c70e2273350">a url with a comma-separated list with bookmark ids</a>.</p>

<h4 id="row-level-security-sucks-can-we-make-it-usable-by-jimmy-angelakos"><a href="https://fosdem.sojourner.rocks/2025/event/09fd9904-eadf-54b4-88d8-4c70e2273350">Row-Level Security sucks. Can we make it usable?</a> by Jimmy Angelakos</h4>

<p>I went to this one because we used RLS at my previous company (<a href="https://www.graphile.org/postgraphile/why-nullable/#relations-rls-visibility">this is the recommended approach with postgraphile</a>). The scheme that he ended up with ended up with was very similar to ours, but he used a GIN index to make the <code class="language-plaintext highlighter-rouge">&amp;&amp;</code> set overlap operation fast.</p>

<h3 id="where-to-next">Where to next?</h3>

<p>I think the next thing I’m going to is <a href="https://www.localfirstconf.com/">local-first conf</a>. Give me a shout if you’re going and want to meet up.</p>

<!-- TODO: patch sojourner so that it can add a link to the upstream schedule (which sometimes contains slides etc) -->]]></content><author><name></name></author><summary type="html"><![CDATA[I was at FOSDEM last weekend, after ~15 years away. I though I should probably write up my highlights while they’re fresh in my head.]]></summary></entry><entry><title type="html">Why cargo-quickbuild?</title><link href="http://alsuren.github.io/2022/07/30/why-cargo-quickbuild.html" rel="alternate" type="text/html" title="Why cargo-quickbuild?" /><published>2022-07-30T00:00:00+00:00</published><updated>2022-07-30T00:00:00+00:00</updated><id>http://alsuren.github.io/2022/07/30/why-cargo-quickbuild</id><content type="html" xml:base="http://alsuren.github.io/2022/07/30/why-cargo-quickbuild.html"><![CDATA[<p>In <a href="/2022/07/10/cargo-quickinstall">my previous post</a>, I reviewed the history of <code class="language-plaintext highlighter-rouge">cargo-quickinstall</code> and introduced my <code class="language-plaintext highlighter-rouge">cargo-quickbuild</code> idea. When reviewing it with workmates, we thought it might be useful compare it against the other solutions that already exist in this space. I will attempt to do so in this blog post.</p>

<blockquote>
  <p>Why not use <a href="https://github.com/mozilla/sccache"><code class="language-plaintext highlighter-rouge">sccache</code></a>, <a href="https://nixos.org"><code class="language-plaintext highlighter-rouge">nix</code></a>, <a href="https://bazel.build"><code class="language-plaintext highlighter-rouge">bazel</code></a> or <a href="https://github.com/lukemathwalker/cargo-chef"><code class="language-plaintext highlighter-rouge">cargo-chef</code></a>?</p>
</blockquote>

<p>These tools are all in a similar space, and I will definitely be stealing ideas from all of these projects.</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">sccache</code> is intended to be a thin wrapper around <code class="language-plaintext highlighter-rouge">rustc</code>, and is easy to integrate into your workflow. In order to calculate its cache keys, it reads the rust source code. It has the ability to use a shared cache on s3, but it is all a single cache, so you need to trust everyone who has write access to it, and all of the crates that they ever compile. There is also no mechanism (that I know of) for identifying cache misses and farming them off to a background worker, because it is assumes that the user will also have write access to the cache, and will upload the result when they’re done. This is okay for teams in a large company like Mozilla, but less good if you are in a team of one, hacking on projects in your spare time.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">nix</code> is promising, and its <a href="https://nixos.wiki/wiki/Binary_Cache">cache</a> architecture is very powerful. It is not very portable though (no native windows support, and getting it set up on macos has traditionally been very painful). Integrating it into a crate with a large dependency tree also requires a lot of boilerplate.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">bazel</code> is more portable, but also requires a lot of boilerplate. It is made by Google, and the trust model for its shared cache appears to be similar to that of sccache.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">cargo-chef</code> is a docker-specific tool. If you need to build a docker image today, and you’re not able to just pre-build your binary outside of docker and COPY it in, I recommend <code class="language-plaintext highlighter-rouge">cargo-chef</code> (my first Rust London Hack and Learn was spent working on <code class="language-plaintext highlighter-rouge">cargo-chef</code>, so I might be biassed). Its key innovation is turn your dependency tree into a separate layer that is independent of the rest of your source code. This means that you can skip rebuilding dependencies if you are only making changes to your source code. The downside of this approach is that if you bump any dependencies then the whole layer gets thrown away and built from scratch (this similar to the behaviour that you will find with <a href="https://github.com/actions/cache/blob/main/examples.md#user-content-rust---cargo">GitHub Actions’ recommended cache configuration</a>). Sharing individual docker layers between build servers and developers is also a pain, so your developers will probably not feel the benefit of the massive CI bill that you pay every month.</p>
  </li>
</ul>

<p>Rust’s tooling excellence owes a lot to the unifying influence of <code class="language-plaintext highlighter-rouge">cargo</code> for build + docs + testing. Its major shortcoming is long build times. My aim with quickbuild is to meet users where they are, because <code class="language-plaintext highlighter-rouge">cargo</code> is an excellent place to be. I’m hoping to produce meaningful speed-ups of from-scratch builds, without requiring configuration changes for the user’s computer/project.</p>

<p>I also aim to build on shared infrastructure, available to all, so you don’t need any involvement from finance or your ops team. I will be making use of free “open source tier” compute resources for building my packages, but they will be available for use by <em>anyone</em> to reduce their build times and CI costs, as long as they are happy to share their rust flags, and the list of dependencies from their Cargo.toml.</p>

<p>I have come to the end of my time at <a href="https://tably.com">Tably</a>, and I plan to spend August house hunting and working on quickbuild, before looking for my next job. If you would like to become the first sponsor this work, please go to my <a href="https://github.com/sponsors/alsuren">GitHub Sponsors page</a>.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[In my previous post, I reviewed the history of cargo-quickinstall and introduced my cargo-quickbuild idea. When reviewing it with workmates, we thought it might be useful compare it against the other solutions that already exist in this space. I will attempt to do so in this blog post.]]></summary></entry><entry><title type="html">The cargo-quickinstall journey - how I made a thing for installing rust programs quickly</title><link href="http://alsuren.github.io/2022/07/10/cargo-quickinstall.html" rel="alternate" type="text/html" title="The cargo-quickinstall journey - how I made a thing for installing rust programs quickly" /><published>2022-07-10T00:00:00+00:00</published><updated>2022-07-10T00:00:00+00:00</updated><id>http://alsuren.github.io/2022/07/10/cargo-quickinstall</id><content type="html" xml:base="http://alsuren.github.io/2022/07/10/cargo-quickinstall.html"><![CDATA[<p><img src="/images/quickinstall/quickinstall-arch-pretty.exalidraw.png" alt="Full Architecture diagram for cargo-quickinstall" /></p>

<p>I made a thing.</p>

<p>I made a thing that I threw together in a week.</p>

<p>I made a thing that is horrifically complicated, and held together with hot glue and string.</p>

<p>I made a thing that people <em>seem</em> to be using.</p>

<p>Halp!</p>

<h2 id="pre-built-binaries-of-rust-programs">Pre-built binaries of Rust programs</h2>

<p>Back when I was working at <a href="https://red-badger.com">Red Badger</a>, we had some <a href="https://github.com/features/actions">GitHub Actions</a> pipelines that <a href="https://www.reddit.com/r/rust/comments/m2vp2o/comment/gqm2ncg/?utm_source=reddit&amp;utm_medium=web2x&amp;context=3">relied on some tools</a> that were written in <a href="https://www.rust-lang.org">Rust</a>. We had our <a href="https://github.com/actions/cache">GitHub actions cache</a> set up correctly and everything, but every so often we would blast away the cache, by some innocent-looking operation, like bumping a dependency. This would result in a dog-slow build, as it rebuilt all of the tools that we were using, before even starting to compiling our own project.</p>

<p>When that project wound down, I had some bench time between projects, so I decided try doing something about it. I decided to build a service that would pre-build your Rust tools for you. That way, whenever you would usually write something like:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cargo <span class="nb">install </span>ripgrep
</code></pre></div></div>

<p>you could write:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cargo quickinstall ripgrep
</code></pre></div></div>

<p>This would install pre-compiled versions of any binaries in the crate. If we did’t have a pre-compiled version, it would fallback to cargo install automatically.</p>

<p>The initial implementation of cargo-quickinstall was hacked together in less than a week. I also took the opportunity to make as many terrible architectural decisions as possible. Proper resume-driven development. Good times.</p>

<h2 id="do-the-thing">Do the thing</h2>

<!-- "build the right thing, build the thing right" - is that how they say it?
? why is it building the right thing - make it clear that it is a joke?

capitalisation of Rust (always the same in the same document) and Wrangler, postgres, heroku, Vercel, Sematext, elasticsearch, api , Rust London, ... 
 -->

<p>At Red Badger, there is a saying <a href="https://red-badger.com/what-we-do/">“Do the right thing. Do the thing right”</a>.</p>

<p>“Do the right thing” is about finding ways to make sure you are actually making something that people find useful. “Do the thing right” is about getting products into the hands of users in a sustainable way, so that we can gather feedback and and iterate quickly.</p>

<p>I decided to start by building the feedback bit first. If my system knows which packages most people want, it can “do the right thing” without my intervention, by making sure that those packages get built. Having download counts for my packages would also let me know valuable my thing is, and whether I should keep doing it.</p>

<h3 id="stats-server">Stats Server</h3>

<!-- Looking through `git log --reverse --stat` to write this post has been really interesting. -->

<p>I knew that the requirements for the stats server were pretty simple, and also that the rest of the system would function just fine without this piece of the puzzle. I optimised for ticking things off of my tech bucket list, rather than building a rock-solid server.</p>

<p>I started out by creating a <a href="https://workers.cloudflare.com">Cloudflare Workers</a> project in Rust, using their <a href="https://developers.cloudflare.com/workers/wrangler/get-started/">Wrangler</a> devtool, and their <a href="https://developers.cloudflare.com/workers/runtime-apis/kv/">KV store</a>. Rust support for cloudflare workers had only just come out at the time, and I quickly realised that taking this approach would be an uphill battle. There weren’t even official rust bindings to their KV store at the time. I had also read somewhere that their KV store was <em>heavily</em> read-optimised (as-in “please think of this as a configuration store, and don’t try to make more than 1 write per second”, or something). I had grand dreams that I might one-day receive more than 1 request per second, so I decided to switch tack.</p>

<p>The boring choice would be to spin up a Heroku app and write to PostgreSQL. That was <em>too</em> boring though. What other fun resume-expanding technology stack could I use?</p>

<p>One of the most fundamental requirements for cargo-quickinstall has always been that it shouldn’t cost me anything to maintain, so stringing together free-tier teaser offerings was the order of the day.</p>

<p>I remembered meeting with an ad-tech company at a careers fair a few years earlier, and they had a fun architecture. They didn’t have <em>any</em> of their own servers in the hot loop of serving customers. They would serve <em>everything</em> from CDN, including tracking pixels, and then have a cronjob that parsed the CDN logs and used that to generate invoices to their customers. Clever, right? Entirely too web-scale for my own good.</p>

<p>Following this piece of slightly inappropriate architectural inspiration, I span up an empty <a href="https://vercel.com">Vercel</a> project, and started spamming it with requests to random non-existent pages. I then hooked up the <a href="https://vercel.com/integrations/sematext-logs">log drain to sematext</a>. My client would make a request to a non-existent page, and immediately receive a 404 response. I would then periodically query the <a href="https://sematext.com/docs/logs/search-through-the-elasticsearch-api/">sematext Elasticsearch API</a>. No cold-start lambda delays to worry about <sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Brilliant.</p>

<h3 id="artifact-storage">Artifact Storage</h3>

<p>For artifact storage, I picked a service that I had <a href="https://github.com/alsuren/mijia-homie/commit/50e0bd50fff1fa20b9a2f90c967061f658431ba7">used before</a> for hosting debian packages, specifically<a href="https://www.jfrog.com/confluence/display/BT">JFrog’s Bintray</a> service.</p>

<p>I hacked up a script to build a package and upload it to Bintray from my laptop. I ran it on a single package to get me started, and moved on.</p>

<h3 id="client">Client</h3>

<p>Next on the <a href="https://github.com/alsuren/cargo-quickinstall/commit/be1f8e5d5df6f6f891c92307321f3053b972c2e2">list</a> was the <code class="language-plaintext highlighter-rouge">cargo-quickinstall</code> client.</p>

<p>This is basically a glorified bash script.</p>

<p>I wanted <code class="language-plaintext highlighter-rouge">cargo install cargo-quickinstall</code> to be as quick as possible, so I only used things that were in <a href="https://doc.rust-lang.org/stable/std/"><code class="language-plaintext highlighter-rouge">std</code></a>, and shelled out to the system’s <code class="language-plaintext highlighter-rouge">curl</code> and <code class="language-plaintext highlighter-rouge">tar</code> binaries to do the actual work. <a href="https://curl.se"><code class="language-plaintext highlighter-rouge">curl</code></a> and <a href="https://man7.org/linux/man-pages/man1/tar.1.html"><code class="language-plaintext highlighter-rouge">tar</code></a> are <a href="https://docs.microsoft.com/en-us/virtualization/community/team-blog/2017/20171219-tar-and-curl-come-to-windows">both available on modern Windows boxes by default</a>, so this turns out to be a surprisingly portable choice. I also initially did json parsing with <a href="https://stedolan.github.io/jq/"><code class="language-plaintext highlighter-rouge">jq</code></a>, but this has since been replaced with <a href="https://crates.io/crates/tinyjson"><code class="language-plaintext highlighter-rouge">tinyjson</code></a> because apparently nobody has <code class="language-plaintext highlighter-rouge">jq</code> installed (they don’t know what they’re missing).</p>

<p>The initial client basically did this:</p>

<p><img src="/images/quickinstall/quickinstall-blog-post-bintray-client.excalidraw.png" alt="" /></p>

<h3 id="automated-builder">Automated Builder</h3>

<p>The automated builder is responsible for this half of the architecture diagram:</p>

<p><img src="/images/quickinstall/quickinstall-blog-post-sematext-builder.excalidraw.png" alt="" /></p>

<p>The initial implementation got its list of requested crates from sematext’s Elasticsearch API. It was pretty simple - it would just make a list of all requested packages, and try to build + upload the first one that we didn’t already have a package of in Bintray. If there was nothing to do then it would just build <code class="language-plaintext highlighter-rouge">cargo-quickinstall</code> for good luck (which only takes a couple of seconds, so isn’t that much wasted work).</p>

<h4 id="security-and-trust">Security and Trust</h4>

<p>It’s worth digging into the <code class="language-plaintext highlighter-rouge">cargo-quickinstall</code> trust model at this point.</p>

<p>The trust model is currently:</p>

<ol>
  <li>You trust the author of the crate that you asked for, and its dependencies.</li>
  <li>You trust me to be acting in good faith, and to have configured GitHub actions and GitHub releases correctly, and my sandboxing to be adequate.</li>
  <li>You trust GitHub not to replace everyone’s released binaries with cryptomalware.</li>
</ol>

<p>This means that (assuming that you trust <code class="language-plaintext highlighter-rouge">cargo-quickinstall</code>, and that our sandboxing is solid) by running <code class="language-plaintext highlighter-rouge">cargo quickinstall $CRATE</code>, you’re not forced to trust anyone that you’re not already trusting by running <code class="language-plaintext highlighter-rouge">cargo install $CRATE</code>.</p>

<p><code class="language-plaintext highlighter-rouge">cargo-quickinstall</code> does not trust the author of <strong>any</strong> package on crates.io. As soon as we have run the crate’s <code class="language-plaintext highlighter-rouge">build.rs</code> or any proc macros, we must treat the build box as compromised. There is some gymnastics involved in achieving this, so bear with me.</p>

<h4 id="github-actions-gymnastics">GitHub Actions Gymnastics</h4>

<p>The <a href="https://github.com/alsuren/cargo-quickinstall/blob/main/.github/workflows/cronjob.yml">cronjob</a> works out which crate needs to be built next for each target architecture, and which runner OS we need to build it on.</p>

<p>The workflow that does the building is given <code class="language-plaintext highlighter-rouge">$CRATE</code> <code class="language-plaintext highlighter-rouge">$VERSION</code> <code class="language-plaintext highlighter-rouge">$BUILD_OS</code> and <code class="language-plaintext highlighter-rouge">$TARGET_ARCH</code>. We currently supply these variables by running <code class="language-plaintext highlighter-rouge">sed</code> over a <a href="https://github.com/alsuren/cargo-quickinstall/blob/main/.github/workflows/build-package.yml.template">template</a>, and committing the result to git. If I was writing it again today from scratch, I might revisit this decision, but this works well enough for now.</p>

<p>We spin up a runner with <code class="language-plaintext highlighter-rouge">$BUILD_OS</code> and <a href="https://github.com/alsuren/cargo-quickinstall/pull/86"><code class="language-plaintext highlighter-rouge">permissions: {}</code></a> on it, and do the build. This essentially runs <code class="language-plaintext highlighter-rouge">cargo install $crate</code> and then tars up the resulting binaries and uses <code class="language-plaintext highlighter-rouge">actions/upload-artifact</code> to upload it with a known filename, so that it is available for other jobs in the same build pipeline.</p>

<p><strong>Security notice:</strong> I’m assuming that all runners are able to use <code class="language-plaintext highlighter-rouge">actions/upload-artifact</code> without any extra creds. ~I’ve not really dug into it that much.~ If it turns out that the runner is being given some kind of god token, and that token is available to <code class="language-plaintext highlighter-rouge">$CRATE</code>’s untrusted <code class="language-plaintext highlighter-rouge">build.rs</code> for doing anything other than uploading build artifacts then we’re in big trouble. If you believe this to be the case, please email me so that I can stop building new packages and do a proper audit/redesign.</p>

<p>Once the builder is finished, we throw it in the bin, and spin up a new <code class="language-plaintext highlighter-rouge">ubuntu-20.04</code> runner. This downloads the tarball from <code class="language-plaintext highlighter-rouge">actions/upload-artifact</code><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> and uploads it to GitHub Releases (previously bintray).</p>

<p>By doing this whole dance, we ensure that a malicious crate author can only poison the tarball of their own crate, or any crates that depends on their crate. If you run <code class="language-plaintext highlighter-rouge">cargo install $CRATE</code> then you already trust every crate in <code class="language-plaintext highlighter-rouge">$CRATE</code>’s dependency tree, and you already trust GitHub for the crates.io index. Assuming that you trust <code class="language-plaintext highlighter-rouge">cargo-quickinstall</code> and that our sandboxing is solid, by using <code class="language-plaintext highlighter-rouge">cargo install $CRATE</code>, you’re not forced to trust anyone that you’re not already trusting by running <code class="language-plaintext highlighter-rouge">cargo install $CRATE</code>.</p>

<p><img src="/images/quickinstall/quickinstall-blog-post-ci-sequence-diagram.excalidraw.png" alt="Sequence diagram of GitHub Actions builders" /></p>

<p>There are probably massive holes in this logic. Even if it’s all sound, <code class="language-plaintext highlighter-rouge">cargo-quickinstall</code> has never been audited. If you work at Microsoft/GitHub and/or would like to sponsor a security researcher to help me audit this, please leave a comment on <a href="https://github.com/alsuren/cargo-quickinstall/issues/49">this issue</a> or contact me privately.</p>

<blockquote>
  <p>EDIT 2022-07-24: When pair-reviewing this post with <a href="https://github.com/alecmocatta">@alecmocatta</a>, he pointed me to a <a href="https://securitylab.github.com/research/github-actions-preventing-pwn-requests/">github security article</a> on the matter. It seems sensible to assume that the runner for the “build” is a VM that contains both the untrusted code and an orchestration process that has access to the GITHUB_TOKEN for the whole build.</p>

  <p>He also advised me not to trust any isolation boundary that’s less strong than full-on VM isolation.</p>

  <p>This stalled me for a while. I came up with <a href="https://github.com/alsuren/cargo-quickinstall/issues/49">an excessively complicated scheme</a>, and we agreed that it would probably work, but was very unsatisfying. When I sat down to implement it on the weekend, I stumbled on <a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idpermissions">a mechanism</a> to neuter the GITHUB_TOKEN that is sent to the runner. The fix ended up being <a href="https://github.com/alsuren/cargo-quickinstall/pull/86/files">a single line</a>.</p>

  <p>I have not yet found a way for a malicious crate to extract the secret and use it to do anything. I also don’t have any reason to believe that anyone else has done so. I will therefore be keeping all previously built packages published in the repo as-is. If you believe that this is unwise, and would like to help me implement a better security process, please comment on <a href="https://github.com/alsuren/cargo-quickinstall/issues/49">this issue</a> and I will set up a call to pair on it.</p>
</blockquote>

<!-- This is a good title. More like this plz -->
<h2 id="bootstrapping-the-package-list">Bootstrapping the package list</h2>

<p>There is a bit of a chicken-and-egg problem with the approach I have described so far. If you don’t have any users then you won’t have any idea which packages need to be built. New users will always find that we don’t have the packages that they want, so they will stop using our service. This means they will not tell us the names of any more packages that they want building.</p>

<p>To break this cycle, I made a list of popular packages by grabbing the html from <a href="https://lib.rs/command-line-utilities">https://lib.rs/command-line-utilities</a> and pulling out the package names into a flat text file using <a href="https://github.com/EricChiang/pup"><code class="language-plaintext highlighter-rouge">pup</code></a> and <code class="language-plaintext highlighter-rouge">jq</code>.</p>

<!-- This is a good title. More like this plz -->
<h3 id="skipping-broken-packages">Skipping broken packages</h3>

<p>Not all crates build on all platforms. Initially, my builder didn’t have any memory of what it had attempted to build, so when it came across a package that it couldn’t build, it would get stuck and attempt to rebuild it every hour until I manually excluded it. It also did each of the platforms in series, so a broken windows build would block progress on all platforms. It was <em>also</em> racey, so if a build took over an hour (or if I got impatient and triggered multiple builds in an hour) it would sometimes build the same package twice, and then crash when trying to upload it.</p>

<p>This is where the “sed the template and commit it to git” approach comes in. There is a branch for each target (<code class="language-plaintext highlighter-rouge">trigger/$TARGET</code>), and each time the cronjob builds, it checks out each <code class="language-plaintext highlighter-rouge">trigger/$TARGET</code> branch, using <code class="language-plaintext highlighter-rouge">git worktree</code>, and checks what it last attempted to build. It then walks down the list of popular/requested crates, and makes a new commit to trigger a build of the next crate <em>after</em> the one that was last attempted. We still do a lot of useless builds of packages that would never compile, but at least we weren’t <a href="https://en.wikipedia.org/wiki/Head-of-line_blocking">head-of-line blocking</a> anymore.</p>

<p>Later, when we started pushing tags for each successful build (as part of the switch to GitHub releases), we were able to detect repeatedly-failing builds and automatically add the offending packages to the exclude list. This process is a little fragile, and it currently errs on the side of building known-broken packages occasionally, but it’s better than nothing.</p>

<h2 id="free-tiers-dont-last-forever">Free Tiers Don’t Last Forever</h2>

<p>The danger of relying on free-tier stuff is that your provider is not beholden to you in any way. They may take away your service at any time.</p>

<!-- link to hack-and-learn on meetup; spotify-tui

reword end-of-life to made read-only || shut down
 -->
<p>The first service to fall was Bintray. Bintray was still serving my compiled crates read-only, and I had a bit of time before they would start deleting them entirely, so I wasn’t in too much of a rush, but if I didn’t find an alternative host eventually then I would have to put cargo-quickinstall in the bin.</p>

<p>Around this time, I was mentoring a <a href="https://www.meetup.com/rust-london-user-group/events/">Hack and Learn</a>, and the <a href="https://github.com/Rigellute/spotify-tui"><code class="language-plaintext highlighter-rouge">spotify-tui</code></a> maintainer pointed out that they use GitHub actions to make their releases, and that the release artifacts would show up with predictable URLs. I kicked off a new verson of the builder that could upload to GitHub Releases, and then made a release of the client which could fetch from both places.</p>

<p>The next free-tier service to go away was sematext. When sematext ended the free tier that my log pipeline was using, I decided that it was probably time to pick a more traditional architecture. I added <a href="https://github.com/alsuren/warehouse-clerk-tmp/tree/master/pages/api">a couple of typescript endpoints</a> to my Vercel site so I was no longer relying on logs of 404 errors. Boring. I like boring. Boring is good, especially for things that people are using, and that I need to actually maintain.</p>

<!-- thanks to my awesome external contributors. Emojis? -->
<h2 id="the-joy-of-working-with-other-people-">The joy of working with other people 🤝</h2>

<p>I have really enjoyed working with external contributors on cargo-quickinstall. The client is <em>super simple</em>, so it is reasonably approachable for beginners. After mentoring two Rust London Hack and Learn events, most of the low-hanging fruit has been picked, but there are approachable issues that show up from time to time. If you want to have a go at one, check out the <a href="https://github.com/alsuren/cargo-quickinstall/labels/good%20first%20issue">good first issue</a>.</p>

<h2 id="next-steps">Next Steps</h2>

<p>There are a few open issues <a href="https://github.com/alsuren/cargo-quickinstall/projects/1?fullscreen=true">on the board</a>, and I’m happy to mentor people on any of them. The issue that I’m especially interested in mentoring someone on is <a href="https://github.com/alsuren/cargo-quickinstall/issues/84">the one for building static binaries for non-ubuntu-20.04 support, and shelling out to <code class="language-plaintext highlighter-rouge">cargo-binstall</code> for the more complex fallback behaviour</a>.</p>

<p>At the moment, there are no time-critical issues on the board (no security issues, and nothing that represents a regression for existing users in CI), so I am mostly leaving things open and offering mentoring on them. It is more valuable at the moment to get more people familiar with the codebase, and improve the <a href="https://deviq.com/terms/bus-factor">bus-factor</a> of the project.</p>

<!-- better title plz -->
<h2 id="speeding-up-cargo-build-as-well">Speeding up <code class="language-plaintext highlighter-rouge">cargo build</code> as well</h2>

<p>The other reason for me taking this approach with <code class="language-plaintext highlighter-rouge">cargo-quickinstall</code> is <code class="language-plaintext highlighter-rouge">cargo-quickbuild</code>. This is a project idea to take <em>parts</em> of dependency trees, rather than just the end-result. I have started progress on this over in a new <a href="https://github.com/cargo-quick/cargo-quick">cargo-quick repo</a>. The idea is to have a tool to make from-scratch builds quicker, by providing a central repo of prebuilt crates (think <a href="https://www.ctl.io/developers/blog/post/caching-docker-images">docker layers</a> for your <a href="https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir">target dir</a>). It will have the same trust model as <code class="language-plaintext highlighter-rouge">cargo-quickinstall</code>, but a slightly more complex architecture. Once quickbuild has come along a bit further, I will port the quickinstall builder to use it, and then merge <code class="language-plaintext highlighter-rouge">cargo quickinstall</code> into the <code class="language-plaintext highlighter-rouge">cargo-quick</code> repo.</p>

<!--

(read 
https://hadean.com/blog/managing-rust-dependencies-with-nix-part-ii/ so that you don't say something obviously false about nix)

- [ ] find the reddit comment where someone suggests that I should make quickbuild

 -->
<p>This raises the question:</p>
<blockquote>
  <p>Why not use <a href="https://github.com/mozilla/sccache"><code class="language-plaintext highlighter-rouge">sccache</code></a>, <a href="https://nixos.org"><code class="language-plaintext highlighter-rouge">nix</code></a>, <a href="https://bazel.build"><code class="language-plaintext highlighter-rouge">bazel</code></a> or <a href="https://github.com/lukemathwalker/cargo-chef"><code class="language-plaintext highlighter-rouge">cargo-chef</code></a>?</p>
</blockquote>

<p>To avoid bloating the end of this post, I have <a href="/2022/07/30/why-cargo-quickbuild">split this out into its own post</a>. The conclusion of which is:</p>

<blockquote>
  <p>Rust’s tooling excellence owes a lot to the unifying influence of <code class="language-plaintext highlighter-rouge">cargo</code> for build + docs + testing. Its major shortcoming is long build times. My aim with quickbuild is to meet users where they are, because <code class="language-plaintext highlighter-rouge">cargo</code> is an excellent place to be. I’m hoping to produce meaningful speed-ups of from-scratch builds, without requiring configuration changes for the user’s computer/project.</p>

  <p>I also aim to build on shared infrastructure, available to all, so you don’t need any involvement from finance or your ops team. I will be making use of free “open source tier” compute resources for building my packages, but they will be available for use by <em>anyone</em> to reduce their build times and CI costs, as long as they are happy to share their rust flags, and the list of dependencies from their Cargo.toml.</p>
</blockquote>

<p>I am coming to the end of my time at <a href="https://tably.com">Tably</a> and I plan to spend August house hunting and working on quickbuild, before looking for my next job. If you would like to become the first sponsor this work, please go to my <a href="https://github.com/sponsors/alsuren">GitHub Sponsors page</a>.</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">

      <p>In practice, when I made the client, I made it do this request in a background thread anyway, so it doesn’t <em>really</em> matter how long my cold-start time is. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">

      <p>previously bintray <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Monitoring Temperature (with too many Bluetooth thermometers)</title><link href="http://alsuren.github.io/2022/02/19/mijia-homie-presentation.html" rel="alternate" type="text/html" title="Monitoring Temperature (with too many Bluetooth thermometers)" /><published>2022-02-19T00:00:00+00:00</published><updated>2022-02-19T00:00:00+00:00</updated><id>http://alsuren.github.io/2022/02/19/mijia-homie-presentation</id><content type="html" xml:base="http://alsuren.github.io/2022/02/19/mijia-homie-presentation.html"><![CDATA[<p><img src="/images/mijia-homie-presentation/title.jpg" alt="" /></p>

<p>This is the blog post form of a presentation given at Rust London - 27 April 2021.</p>

<p>A video of the talk is available <a href="https://www.youtube.com/watch?v=Xus85dOx3ns">on youtube</a>, and slides are available <a href="https://alsuren.github.io/mijia-homie/docs/presentation/">in the project’s repo</a>.</p>

<p>The slides are written in markdown using <a href="https://remarkjs.com/">remark</a>, and this blog is also markdown. Let’s see how well this translation job goes.</p>

<h2 id="outline">Outline</h2>

<ul>
  <li><a href="Backstory">Backstory</a></li>
  <li><a href="#system-overview">System Overview</a>
    <ul>
      <li><a href="#Rust">Rust</a></li>
      <li><a href="#MQTT">MQTT</a></li>
      <li><a href="#bluetooth-2020">Bluetooth when we started</a></li>
      <li><a href="#Concurrency">Concurrency</a></li>
      <li><a href="#bluetooth-developments">Bluetooth Developments</a></li>
    </ul>
  </li>
  <li><a href="#Results">Pretty Graphs</a></li>
  <li><a href="#closing">Closing Remarks</a></li>
  <li><a href="#Links">Links and Questions</a></li>
</ul>

<h2 id="backstory">Backstory</h2>

<p>We started with a few ESP32 dev-boards like this:</p>

<p><img src="/images/mijia-homie-presentation/inception-yun_hat_04.jpg" alt="" /></p>

<p>These cost around US$16 each, and don’t last more than about a day on battery power.</p>

<p>ESP32 is a super-cheap system on chip with bluetooth and wifi, but dev-boards will always be more expensive than commercial off-the-shelf hardware.</p>

<p>During lockdown, we were setting around the dinner table, and I asked my housemate “Wouldn’t it be nice to have a hundred temperature sensors? What could we do with that many sensors?”</p>

<p>So we bought 20 of these, at $3 each, and hooked them up to the internet.</p>

<p><img src="/images/mijia-homie-presentation/inception-order.png" alt="" /></p>

<!-- TODO: receipt for the other 80 -->

<h2 id="system-overview">System Overview</h2>

<p>This is what we built:</p>

<p><img src="/images/mijia-homie-presentation/system-overview.embed.svg" alt="" /></p>

<p>Let’s take a look at the decisions we made, how they turned out.</p>

<h2 id="rust">Rust</h2>

<p>We picked Rust because we were both starting to use Rust for work, so using Rust for a personal project was a good opportunity for learning, for both of us.</p>

<p>Andrew is working on <a href="https://chromium.googlesource.com/chromiumos/platform/crosvm/">crosvm</a> and <a href="https://android.googlesource.com/platform/packages/modules/Virtualization/+/refs/heads/master/virtmanager/">Virt Manager</a> for Android.</p>

<p>I was using Rust for the backend of the <a href="https://github.com/FutureNHS/futurenhs-platform/">FutureNHS</a> project (I have since worked on other Rust projects at Red Badger, and moved on to work at <a href="https://tably.com">Tably.com</a>, with backend and frontend written in Rust).</p>

<p>It was also a good chance to work on something together during lockdown.</p>

<p>I also found a <a href="https://dev.to/lcsfelix/using-rust-blurz-to-read-from-a-ble-device-gmb">blog post</a> describing how to connect to these sensors with Rust. This gave us the burst of momentum that we needed to start the project, but in the end, we outgrew its initial structure, and made our own project.</p>

<h2 id="mqtt">MQTT</h2>

<p>MQTT is the pubsub of choice for low-powered gadgets.</p>

<p>Homie is an auto-discovery convention built on MQTT.</p>

<p>In Rust, the <a href="https://crates.io/crates/rumqtt"><code class="language-plaintext highlighter-rouge">rumqttc</code></a> library is pretty good:</p>

<ul>
  <li>It works using channels, which is a nice interface.</li>
  <li>Andrew has submitted patches, and they were well received.</li>
</ul>

<h2 id="rust-bluetooth-in-2020">Rust Bluetooth in 2020</h2>

<p>The state of Rust Bluetooth in 2020 was a little underwhelming. The options were:</p>

<ul>
  <li><a href="https://crates.io/crates/blurz"><code class="language-plaintext highlighter-rouge">blurz</code></a> - “Bluetooth from before there was Tokio”
    <ul>
      <li>We started with this.</li>
      <li>Talks to BlueZ over D-Bus, but single-threaded and synchronous.</li>
      <li>Blocking <code class="language-plaintext highlighter-rouge">device.connect()</code> calls. 😧</li>
      <li>Unmaintained (for 2 years).</li>
    </ul>
  </li>
</ul>

<!-- prettier-ignore-start -->

<ul>
  <li><a href="https://crates.io/crates/btleplug"><code class="language-plaintext highlighter-rouge">btleplug</code></a> - “cross-platform jumble”
    <ul>
      <li>Theoretically cross platform, but many features not implemented.</li>
      <li>Linux implementation needed root access.</li>
      <li>Too many panics for us to use.</li>
    </ul>
  </li>
</ul>

<!-- prettier-ignore-end -->

<h2 id="aside-concurrency">Aside: Concurrency</h2>

<ul>
  <li>The main problem with <code class="language-plaintext highlighter-rouge">blurz</code> was that it exposed a single-threaded blocking library interface:
<img src="/images/mijia-homie-presentation/single-threaded-blocking.embed.svg" alt="" /></li>
</ul>

<p>We realised that there was a third approach:</p>

<ul>
  <li><a href="https://crates.io/crates/dbus"><code class="language-plaintext highlighter-rouge">dbus-rs</code></a> - aka “roll your own BlueZ wrapper”
    <ul>
      <li>We could generate a “-sys” crate from D-Bus introspection, using the tools provided by the dbus-rs project.</li>
      <li>The <code class="language-plaintext highlighter-rouge">dbus-rs</code> codegen produces syncronous or async interfaces, so you can pick whichever approach you want.</li>
    </ul>
  </li>
</ul>

<p>After switching to an async library, we got:</p>

<p><img src="/images/mijia-homie-presentation/single-threaded-async.embed.svg" alt="" /></p>

<p>This almost solves the problem, but not quite. In our case, everything lives in a big <code class="language-plaintext highlighter-rouge">Arc&lt;Mutex&lt;GlobalState&gt;&gt;</code>.</p>

<p><img src="/images/mijia-homie-presentation/single-threaded-mutex.embed.svg" alt="" /></p>

<p>The solution is to hold the Mutex for as little time as possible.</p>

<p><img src="/images/mijia-homie-presentation/single-threaded-mutex-final.embed.svg" alt="" /></p>

<p>This is much better.</p>

<p>These are the concurrency tools that we use:</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Arc&lt;Mutex&lt;GlobalState&gt;</code></p>

    <ul>
      <li>Used for all of our state.</li>
      <li>Easy refactor from <code class="language-plaintext highlighter-rouge">&amp;mut GlobalState</code>.</li>
      <li>Fine as long as you know where the lock contention is.</li>
      <li>Only hold the mutex when you <em>need</em> it, be careful of await points.</li>
    </ul>
  </li>
  <li>
    <p>Unbounded Channels</p>

    <ul>
      <li>Used for all bluetooth events, and all MQTT traffic.</li>
      <li>Fine if you know they’re not going to back up.</li>
    </ul>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Stream&lt;Item = Event&gt;</code></p>

    <ul>
      <li>Used as the consumption API of the Channels.</li>
      <li>Just the async version of Iterator.</li>
      <li><code class="language-plaintext highlighter-rouge">map()</code>, <code class="language-plaintext highlighter-rouge">filter()</code> and <code class="language-plaintext highlighter-rouge">select_all()</code> are easy to use.</li>
    </ul>
  </li>
</ul>

<h2 id="bluetooth-developments">Bluetooth Developments</h2>

<p>We ended up building on top of our “-sys” Bluetooth library, and created: <a href="https://crates.io/crates/bluez-async"><code class="language-plaintext highlighter-rouge">bluez-async</code></a></p>

<ul>
  <li>Linux only</li>
  <li>Typesafe async wrapper around BlueZ D-Bus interface.</li>
  <li>Sent patches upstream to <a href="https://crates.io/crates/dbus"><code class="language-plaintext highlighter-rouge">dbus-rs</code></a> to improve code generation and support for complex types.</li>
  <li>Didn’t announce it anywhere, but issues filed (and a PR) by two other users so far.</li>
</ul>

<p>Andrew has also been contributing to <a href="https://crates.io/crates/btleplug"><code class="language-plaintext highlighter-rouge">btleplug</code></a></p>

<ul>
  <li>Ported btleplug to use <code class="language-plaintext highlighter-rouge">bluez-async</code> on Linux.</li>
  <li>Exposes an async interface everywhere.</li>
  <li>There are a few bugs that need fixing before they make a release though.</li>
</ul>

<h2 id="results">Results</h2>

<p>We now have graphs like this, with inside and outside readings:</p>

<p><img src="/images/mijia-homie-presentation/grafana-temperature.png" alt="" /></p>

<p>and readings from our fridge:</p>

<p><img src="/images/mijia-homie-presentation/grafana-fridge.png" alt="" /></p>

<p>and we can plot trends using Pandas and Plotly:</p>

<p><img src="/images/mijia-homie-presentation/average-temperature-by-day.png" alt="" /></p>

<h2 id="wills-setup-with-miflora-sensors">Will’s setup, with MiFlora sensors</h2>

<p>I gave some to my workmate:</p>

<p><img src="/images/mijia-homie-presentation/will-system-overview.embed.svg" alt="" /></p>

<p>so you can tell when Will waters his plants:</p>

<p><img src="/images/mijia-homie-presentation/will_moisture.png" alt="" /></p>

<p>and when the dehumidifier kicks in in the cellar:</p>

<p><img src="/images/mijia-homie-presentation/will_dehumidifier.png" alt="" /></p>

<h2 id="cloudbbq">CloudBBQ</h2>

<p>We also got it working with a meat thermometer (backstory: one of the people who sent us patches was using it with a bbq meat thermometer, so I bought one for Andrew as a joke present):</p>

<p><img src="/images/mijia-homie-presentation/cloudbbq-system-overview.embed.svg" alt="" /></p>

<p>so now we have a graph of our roast:</p>

<p><img src="/images/mijia-homie-presentation/cloudbbq-lamb.png" alt="" /></p>

<h2 id="closing-remarks">Closing Remarks</h2>

<!-- FIXME: diagram for this, to mirror Stu's -->

<p>Separating things into layers (and crates) worked well:</p>

<ul>
  <li>App (<code class="language-plaintext highlighter-rouge">mijia-homie</code>) -&gt; Sensor (<code class="language-plaintext highlighter-rouge">mijia</code>) -&gt; Bluetooth (<code class="language-plaintext highlighter-rouge">bluez-async</code>) -&gt; D-Bus.</li>
  <li>App (<code class="language-plaintext highlighter-rouge">mijia-homie</code>) -&gt; Homie (<code class="language-plaintext highlighter-rouge">homie-device</code>) -&gt; MQTT.</li>
  <li>MQTT -&gt; Homie (<code class="language-plaintext highlighter-rouge">homie-controller</code>) -&gt; <code class="language-plaintext highlighter-rouge">homie-influx</code> -&gt; InfluxDB</li>
</ul>

<p>Deployment</p>

<ul>
  <li>Everything is supervised by systemd.</li>
  <li>Built with Github Actions and <a href="https://crates.io/crates/cross"><code class="language-plaintext highlighter-rouge">cross</code></a>, packaged with <a href="https://crates.io/crates/cargo-deb"><code class="language-plaintext highlighter-rouge">cargo-deb</code></a>.
<!-- , hosted on Bintray. -->
<!-- except it's not, is it, because bintray is dead? -->
<!-- cross compiling to ARM is a pain if you need c libs, but cross makes it okay -->
<!-- cross compiling to ARM v6 even more of is a pain, as Will can testify, but we got there in the end --></li>
  <li>Test coverage is a bit thin (blame me for this).</li>
</ul>

<p>One major limitation is that the Raspberry Pi only supports 10 connected BLE devices (10 « 100). The way to get around this problem would be to make the mijia sensors include the temperature and humidity data in their advertising broadcast packets, and then passively listen to them on the raspberry pi. There are a handful of projects that provide flash custom firmware for the mijia sensors, and many of them let you do exactly this. If anyone has done this to their sensors, we would be really interested to hear from you. Adding support for reading sensors in this way would allow us to deploy many more sensors, and would also drastically reduce how many cell batteries we go through in a year.</p>

<!-- Rust is probably not the **best** language for this:

- Bluetooth stack on Linux is quite dynamic in places, due to its C and D-Bus heritage.

- Cross-compiling with `cross` is okay to set up, but iteration is slow.

- We found a [Python project](https://github.com/JsBergbau/MiTemperature2) partway through, with
  similar objectives. -->

<h2 id="links">Links</h2>

<ul>
  <li>
    <p>GitHub: <a href="https://github.com/alsuren/mijia-homie">https://github.com/alsuren/mijia-homie (includes this presentation)</a></p>
  </li>
  <li>
    <p>Inspirational blog post <a href="https://dev.to/lcsfelix/using-rust-blurz-to-read-from-a-ble-device-gmb">https://dev.to/lcsfelix/using-rust-blurz-to-read-from-a-ble-device-gmb</a></p>
  </li>
  <li>
    <p>Homie spec <a href="https://homieiot.github.io/">https://homieiot.github.io/</a></p>
  </li>
  <li>
    <p>Homie helper library <a href="https://crates.io/crates/homie-device">https://crates.io/crates/homie-device</a></p>
  </li>
  <li>
    <p>Bluetooth library <a href="https://crates.io/crates/bluez-async">https://crates.io/crates/bluez-async</a></p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">btleplug</code> <a href="https://crates.io/crates/btleplug">https://crates.io/crates/btleplug</a></p>
  </li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Docker Yaks</title><link href="http://alsuren.github.io/2021/08/13/docker-yaks.html" rel="alternate" type="text/html" title="Docker Yaks" /><published>2021-08-13T00:00:00+00:00</published><updated>2021-08-13T00:00:00+00:00</updated><id>http://alsuren.github.io/2021/08/13/docker-yaks</id><content type="html" xml:base="http://alsuren.github.io/2021/08/13/docker-yaks.html"><![CDATA[<p>I have given myself a target of not installing Docker Desktop on this laptop since I did the reinstall. Let’s see how much pain that causes.</p>

<p>I also don’t want to install VirtualBox, because it feels like a dead end. For some reason, <code class="language-plaintext highlighter-rouge">brew install --cask vagrant</code> wants root access (maybe it’s just because they decided to dump files in /opt/vagrant? Maybe it bundles VirtualBox? At any rate, I decided against installing it)</p>

<p>Qemu is capable of installing without root, because it uses the MacOS Hypervisor.Framework. It is also what the Android AVD emulation system uses under the hood, last time I checked.</p>

<p>What can I install with qemu and no vagrant?</p>

<p>My first thought was to try packer, but all of the public packer templates that mention qemu are significantly out of date, and don’t build.</p>

<p>I started looking around for qcow2 images, and searching for “qcow2 debian” finds some images for use with OpenStack at <a href="https://cdimage.debian.org/cdimage/openstack/current/">https://cdimage.debian.org/cdimage/openstack/current/</a>. I don’t have any of the openstack tools installed on my mac, but maybe I can still use the images?</p>

<p>Fedora has a <code class="language-plaintext highlighter-rouge">cloud</code> spin, and also <code class="language-plaintext highlighter-rouge">coreos</code>. I downloaded both. CoreOS from <a href="https://getfedora.org/en/coreos/download?tab=metal_virtualized&amp;stream=stable">https://getfedora.org/en/coreos/download?tab=metal_virtualized&amp;stream=stable</a></p>

<p>The coreos installation requires virt-install, which requires python gobject-introspection bindings, which don’t seem easy to install.</p>

<p>Following <a href="https://docs.fedoraproject.org/en-US/fedora-coreos/producing-ign/">https://docs.fedoraproject.org/en-US/fedora-coreos/producing-ign/</a> to produce an ignition file, and then using the virt-install command on <a href="https://docs.fedoraproject.org/en-US/fedora-coreos/getting-started/">https://docs.fedoraproject.org/en-US/fedora-coreos/getting-started/</a> to guess what the underlying qemu command might look like, we get:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-x86_64  <span class="nt">-nographic</span> /Users/alsuren/Downloads/fedora-coreos-34.20210725.3.0-qemu.x86_64.qcow2 <span class="nt">-fw_cfg</span> <span class="nv">name</span><span class="o">=</span>opt/com.coreos/config,file<span class="o">=</span>coreos/docker-host.ign
</code></pre></div></div>

<p>which gets quite a long way, and then says:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[    1.680861] Trying to unpack rootfs image as initramfs...
[    1.681558] Initramfs unpacking failed: invalid magic at start of compressed archive
</code></pre></div></div>

<p>(<code class="language-plaintext highlighter-rouge">killall qemu-system-x86_64</code> is the only way to escape from this, and it puts the shell into a strange state, so readline editing of long commands stop working, so be ready to throw away a lot of shells)</p>

<p>They do have a page on using qemu directly: <a href="https://docs.fedoraproject.org/en-US/fedora-coreos/provisioning-qemu/">https://docs.fedoraproject.org/en-US/fedora-coreos/provisioning-qemu/</a></p>

<p>Adapting their example to our filenames, we get:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-x86_64 <span class="nt">-m</span> 2048 <span class="nt">-nographic</span> <span class="nt">-snapshot</span> <span class="se">\</span>
    <span class="nt">-drive</span> <span class="k">if</span><span class="o">=</span>virtio,file<span class="o">=</span>/Users/alsuren/Downloads/fedora-coreos-34.20210725.3.0-qemu.x86_64.qcow2 <span class="se">\</span>
    <span class="nt">-fw_cfg</span> <span class="nv">name</span><span class="o">=</span>opt/com.coreos/config,file<span class="o">=</span>coreos/docker-host.ign <span class="se">\</span>
    <span class="nt">-nic</span> user,model<span class="o">=</span>virtio,hostfwd<span class="o">=</span>tcp::2222-:22
</code></pre></div></div>

<p>After about 200 seconds, you end up with a box that you can ssh into, like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh core@localhost <span class="nt">-p</span> 2222
</code></pre></div></div>

<p>… but can I use it for doing docker-fwd things?</p>

<p>Dump this in ~/.ssh/config:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Host localhost
  User core
  HostName localhost
  Port 2222
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh localhost <span class="nb">mkdir</span> <span class="nt">-p</span> ./<span class="nv">$PWD</span>
ssh localhost git init ./<span class="nv">$PWD</span>
git remote add docker-fwd <span class="nt">--fetch</span> localhost:./<span class="nv">$PWD</span>

git push docker-fwd HEAD:incoming
ssh localhost <span class="s2">"cd ./</span><span class="nv">$PWD</span><span class="s2"> &amp;&amp; git reset --hard incoming"</span>

git commit
git push docker-fwd HEAD:incoming
ssh localhost <span class="s2">"cd ./</span><span class="nv">$PWD</span><span class="s2"> &amp;&amp; git merge --ff-only incoming"</span>
</code></pre></div></div>
<p>🎉</p>

<p>And then you can open vscode remote over ssh and keep hacking.</p>

<!-- in practice, vscode prompts you to add an entry to ~/.ssh/config, which makes the above a bit simpler -->

<p>Running docker is hella-slow though.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">--rm</span> <span class="se">\</span>
  <span class="nt">--volume</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PWD</span><span class="s2">:/srv/jekyll"</span> <span class="se">\</span>
  <span class="nt">-it</span> jekyll/jekyll:latest <span class="se">\</span>
  jekyll build
</code></pre></div></div>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />work out how to specify groups in the butane configs</li>
</ul>

<p>Once you’ve added yourself to the docker group
takes half an age, and then gives:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/usr/local/lib/ruby/2.7.0/fileutils.rb:250:in `mkdir': Permission denied @ dir_s_mkdir - /srv/jekyll/.jekyll-cache (Errno::EACCES)
</code></pre></div></div>

<p>It seems that I’m going to be fighting against half a decade of sloppy docker permissions</p>

<h3 id="saturday">Saturday</h3>

<p>persistent filesystem:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-img create <span class="nt">-f</span> qcow2 <span class="nt">-b</span> /Users/alsuren/Downloads/fedora-coreos-34.20210725.3.0-qemu.x86_64.qcow2
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-x86_64 <span class="nt">-m</span> <span class="k">$((</span><span class="m">1024</span><span class="o">*</span><span class="m">8</span><span class="k">))</span> <span class="nt">-nographic</span> <span class="se">\</span>
    <span class="nt">-drive</span> <span class="k">if</span><span class="o">=</span>virtio,file<span class="o">=</span>my-fcos-vm.qcow2 <span class="se">\</span>
    <span class="nt">-fw_cfg</span> <span class="nv">name</span><span class="o">=</span>opt/com.coreos/config,file<span class="o">=</span><span class="nv">$HOME</span>/src/docker-fwd/coreos/docker-host.ign <span class="se">\</span>
    <span class="nt">-nic</span> user,model<span class="o">=</span>virtio,hostfwd<span class="o">=</span>tcp::2222-:22
</code></pre></div></div>

<p>For some reason, vscode doesn’t like to connect to this today. I will try a distribution that I understand and come back to it.</p>

<p>Let’s try this first: <a href="https://fabianlee.org/2020/03/14/kvm-testing-cloud-init-locally-using-kvm-for-a-centos-cloud-image/">https://fabianlee.org/2020/03/14/kvm-testing-cloud-init-locally-using-kvm-for-a-centos-cloud-image/</a></p>

<p>… okay, maybe <a href="https://sumit-ghosh.com/articles/create-vm-using-libvirt-cloud-images-cloud-init/">https://sumit-ghosh.com/articles/create-vm-using-libvirt-cloud-images-cloud-init/</a></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>genisoimage <span class="nt">-output</span> cidata.iso <span class="nt">-V</span> cidata <span class="nt">-r</span> <span class="nt">-J</span> user-data meta-data
</code></pre></div></div>
<p>becomes (according to <a href="https://apple.stackexchange.com/questions/121491/equivalents-for-genisoimage-and-qemu-img-on-ubuntu">https://apple.stackexchange.com/questions/121491/equivalents-for-genisoimage-and-qemu-img-on-ubuntu</a>)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>cdrtools
mkisofs <span class="nt">-output</span> cidata.iso <span class="nt">-V</span> cidata <span class="nt">-r</span> <span class="nt">-J</span> user-data meta-data
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-img create <span class="nt">-f</span> qcow2 <span class="nt">-b</span>  ~/Downloads/debian-10-openstack-amd64.qcow2 debian-10-openstack-amd64.qcow2 30G
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-x86_64 <span class="nt">-m</span> <span class="k">$((</span><span class="m">1024</span><span class="o">*</span><span class="m">8</span><span class="k">))</span> <span class="nt">-nographic</span> <span class="se">\</span>
    <span class="nt">-drive</span> <span class="k">if</span><span class="o">=</span>virtio,file<span class="o">=</span>debian-10-openstack-amd64.qcow2 <span class="se">\</span>
	<span class="nt">-cdrom</span> cidata.iso <span class="se">\</span>
    <span class="nt">-nic</span> user,model<span class="o">=</span>virtio,hostfwd<span class="o">=</span>tcp::2222-:22
</code></pre></div></div>

<p>Each time you nuke the image, you need to run this to clear out the <code class="language-plaintext highlighter-rouge">[localhost]</code> entry in known_hosts, like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">''</span> <span class="nt">-n</span>  <span class="s1">'/^[^[]/p'</span> ~/.ssh/known_hosts
</code></pre></div></div>

<p>VSCode ssh seems a bit fucked. It starts up fine and then errors out a few seconds after opening a folder.</p>

<p>The green-and-blue debian prompt makes feel more powerful than the coreos monochrome one. Funny how our minds work.</p>

<h3 id="back-to-docker-again">Back to docker again</h3>

<p>I ended up adding this to my ssh config (dof being shorthand for docker-fwd):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Host dof
  HostName localhost
  Port 2222
</code></pre></div></div>
<p>I now have a vm that’s capable of running docker images, so now I can start debugging my theme.</p>

<h3 id="musings-about-synced-git-commits">Musings about synced git commits</h3>

<p>I think I want something like:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git commit <span class="nv">$file</span> <span class="nt">-m</span> <span class="s2">"auto-commit </span><span class="nv">$file</span><span class="s2">"</span>
git branch <span class="nt">-D</span> outgoing
git branch outgoing  <span class="c"># this will make a branch, but leave you on master</span>
git reset HEAD^  <span class="c"># without changing what's checked out</span>
git push dof outgoing:incoming
</code></pre></div></div>

<p>Open questions:</p>
<ul>
  <li>How do you go about running this in a watchexec loop? Do you need to have a local git worktree off to the side, that does <code class="language-plaintext highlighter-rouge">git restore --source=outgoing $file</code> each time you make a change, to get a fast-forward history for pushing to the remote?</li>
  <li>How do you deal with staged files?</li>
</ul>

<p>I think what I really want is a bare git repo living in another directory, that knows to ignore everything about the .git dir in the local repo.</p>

<p>In the case where you’ve just made a commit and want the remote to sync up, you might be able to add this to your post-commit hook:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>

git push docker-fwd HEAD:incoming
ssh localhost <span class="s2">"cd ./</span><span class="nv">$PWD</span><span class="s2"> &amp;&amp; git merge --ff-only incoming"</span>
</code></pre></div></div>]]></content><author><name></name></author><summary type="html"><![CDATA[I have given myself a target of not installing Docker Desktop on this laptop since I did the reinstall. Let’s see how much pain that causes.]]></summary></entry><entry><title type="html">Initial impressions of nushell</title><link href="http://alsuren.github.io/2021/07/26/nushell-impressions.html" rel="alternate" type="text/html" title="Initial impressions of nushell" /><published>2021-07-26T00:00:00+00:00</published><updated>2021-07-26T00:00:00+00:00</updated><id>http://alsuren.github.io/2021/07/26/nushell-impressions</id><content type="html" xml:base="http://alsuren.github.io/2021/07/26/nushell-impressions.html"><![CDATA[<p>Recently I’ve been recording my impressions of technologies as I start using them. This is so that I can remember what my pain points are when I’m onboarding other people later, or contributing to the project.</p>

<h2 id="why-am-i-doing-this">Why am I doing this?</h2>

<p>I am trying out nushell because I <em>really</em> don’t like zsh’s input system (zle). Readline is <em>much</em> better than zle. Zle doesn’t have a concept of short vs long words. If you set zle into “bash” word mode, it treats - like whitespace, so typing esc-backspace after <code class="language-plaintext highlighter-rouge">ls -- .</code> will delete everything (from memory). Nushell uses a rust-based readline clone for its text input, so my muscle memory is intact.</p>

<p>I had a go at teaching zle how to differentiate ^W from esc-backspace, but it’s a mix of C and shell horribleness. I suspect that bending nushell to my will will be easier than fixing zsh.</p>

<p>I also tried <code class="language-plaintext highlighter-rouge">fish</code> a while back. The thing that forced me back to bash was the lack of <code class="language-plaintext highlighter-rouge">!!</code>/<code class="language-plaintext highlighter-rouge">!$</code> support, I think. nushell also doesn’t have these things, but they haven’t explicitly ruled out supporting it, and maybe I could submit it as a patch.</p>

<p>I also really love apache arrow, and running a shell with polars/arrow dataframes built in makes me really excited.</p>

<h2 id="history">History</h2>

<p>The history recording seems completely broken. I was trying to look at the history of what I’ve done, and ended up with a bunch or repeated sequences with entries like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 201 │ n
 202 │ false
 203 │ ckout main
</code></pre></div></div>

<p>I wonder if it’s similar to the problem that bash had, where multiple shell processes confuse each other when they close. I quite like what apple did with per-session history files in a directory, and a cleanup job to consolidate them (this was also used to recover sessions with separate scrollback and history after a restart)</p>

<h3 id="table-rendering">Table rendering</h3>

<p>As I was pasting that, I noticed that it was padded with trailing whitespace. This means that when you resize your terminal, it will cause all sorts of problems.</p>

<h2 id="unix-command-compatibility">Unix command compatibility</h2>

<p>I get the impression that nushell is that it wants to be a data processing language like R, but doesn’t <em>really</em> care about being a capable unix shell.</p>

<h3 id="piping-things-into-unix-commands">Piping things into unix commands</h3>

<p>In bash, <code class="language-plaintext highlighter-rouge">echo</code> automatically separates argument words with spaces, and appends a newline at the end.</p>

<p>Nushell’s <code class="language-plaintext highlighter-rouge">echo</code> turns its arguments into list. Piping a list into a unix command will concatenate the contents without any separators, and won’t add a trailing newline.</p>

<p>I’ve taken to doing this, but it feels very sad:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo 'some/path' | fmt | tee -a .gitignore
</code></pre></div></div>

<h3 id="exit-codes">Exit codes</h3>

<p>By default, nushell ignores the exit code of unix programs, so running <code class="language-plaintext highlighter-rouge">/usr/bin/false</code> will not tell you that anything is wrong.</p>

<p>This is concerning, because it means that all pipeline failures are going to be ignored. It’s the philosophical opposite of <code class="language-plaintext highlighter-rouge">set -euo pipefail</code> at the top of your bash scripts.</p>

<p>If you set <code class="language-plaintext highlighter-rouge">nonzero_exit_errors = true</code> in your config, you end up with a slightly-too-verbose error:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ /usr/bin/false
error: External command failed
  ┌─ shell:1:1
  │
1 │ /usr/bin/false
  │ ^^^^^^^^^^^^^^ command failed
</code></pre></div></div>

<p>I’ve started trying to hack this up in https://github.com/nushell/nushell/pull/3840</p>

<h3 id="quoting-of-semicolons">Quoting of semicolons</h3>

<p>I wanted to write this:</p>

<p><code class="language-plaintext highlighter-rouge">git commit -am "do first thing; do second thing"</code></p>

<p>but the semicolon got parsed by the shell somehow. My commit message got truncated, and I got <code class="language-plaintext highlighter-rouge">sh:  do second thing: command not found</code> printed by the shell.</p>

<p>Interestingly, using single quotes avoids this problem, so I suspect that this is a bug. I like to write words like <code class="language-plaintext highlighter-rouge">don't</code> in my commit messages, so I prefer to use double-quotes for this.</p>

<h3 id="quoted-paste-mode">Quoted paste mode</h3>

<p>When setting up a git repo, you are told to:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git remote add origin git@github.com:alsuren/docker-fwd.git
git branch <span class="nt">-M</span> main
git push <span class="nt">-u</span> origin main
</code></pre></div></div>

<p>If you are using readline with quoted paste enabled, this will give you a multiline input, and you can hit enter once to execute the whole lot. nushell’s default behaviour is similar to the readline default settings, so it will execute the first command and only read the second and third lines once the first has finished executing (unless the first command reads from stdin, in which case, they may go missing entirely).</p>

<h2 id="thats-all-for-now">That’s all for now</h2>

<p>I will keep editing this post until I have run out of beginner papercuts.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Recently I’ve been recording my impressions of technologies as I start using them. This is so that I can remember what my pain points are when I’m onboarding other people later, or contributing to the project.]]></summary></entry><entry><title type="html">Early impressions of NATS</title><link href="http://alsuren.github.io/2021/07/06/nats-impressions.html" rel="alternate" type="text/html" title="Early impressions of NATS" /><published>2021-07-06T00:00:00+00:00</published><updated>2021-07-06T00:00:00+00:00</updated><id>http://alsuren.github.io/2021/07/06/nats-impressions</id><content type="html" xml:base="http://alsuren.github.io/2021/07/06/nats-impressions.html"><![CDATA[<p>First impressions of technologies are quite important for driving adoption, so I’ve started writing down my early impressions as I explore different technologies.</p>

<p>NATS is a distributed messaging system.</p>

<p>(Originally posted on the <a href="https://awesome.red-badger.com/davidlaban/">red-badger tech site</a>)</p>

<h2 id="protocol">Protocol</h2>

<p>NATS prides itself on its protocol’s simplicity. It is a text based protocol with length-encoded payloads and a very small number of verbs, a bit like http 1.0. My first introduction to NATS was the keynote video on their website though (https://www.youtube.com/watch?v=lHQXEqyH57U), which talks about private keys and JWTs, so this initial simplicity feels like a trap.</p>

<h2 id="reliability">Reliability</h2>

<blockquote>
  <p>If the client reaches this internal limit, it will drop messages and continue to process new messages. This is aligned with NATS at most once delivery. It is up to your application to detect the missing messages and recover from this condition.</p>
</blockquote>

<p>– https://docs.nats.io/nats-server/nats_admin/slow_consumers</p>

<p>This is a different approach from other queueing systems that I have used. I am a little bit wary.</p>

<h2 id="security">Security</h2>

<p>It’s slightly concerning to find out that <code class="language-plaintext highlighter-rouge">--routes=nats://ruser:T0pS3cr3t@nats:6222</code> is baked into the default nats image. I wonder how many installations are running with that default exposed to the public internet. (I have heard that the security doesn’t come from secrets stored in the cluster though, so this might not be important?)</p>

<h2 id="documentation">Documentation</h2>

<p>I started reading down the sidebar of the NATS docs from https://docs.nats.io/nats-server/installation and ended up encountering NATS 2.0 auth concepts at the same as JetStream. I can’t remember how that happened. I’m now cycling back around to the top of the docs sidebar, and seeing whether the learning concepts flow a bit better.</p>

<h2 id="request-reply">Request-Reply</h2>

<p>This feels like the way you do blocking-style function calls to other actors in Erlang (typically the message-sending and response-waiting logic is hidden away behind the actor’s public API interface in Erlang. I expect that you would do something similar when using Request-Reply in NATS. Maybe you would wrap up the interface in an IDL and do codegen to generate an ergonomic interface?).</p>

<p>This feels very positive. This pattern of communication isn’t really a thing in RabbitMQ-land, and I have seen people do highly questionable things like dispatching a message and then polling a database to ensure that the message was actioned before returning from a web request. Hopefully we won’t see anything like this in NATS-land. I’m sure we’ll find many other pathological anti-patterns though.</p>

<h2 id="queue-groups">Queue Groups</h2>

<p>Their <a href="https://docs.nats.io/nats-concepts/queue">concepts page on queue groups</a> says that you can do some magic with consumer groups, but doesn’t really say how they’re configured. I wonder whether misconfigured queue groups are a thing that you see in production. I guess I’ll find out when I read their tutorial.</p>

<h2 id="acknowledgements-and-sequence-numbers">Acknowledgements and Sequence Numbers</h2>

<p>These both fall into the “we’re providing you with an unreliable channel” bucket. I don’t think that I would want my business logic to be worrying about things like this. I wouldn’t be surprised if real-world NATS applications end up having multiple layers of abstraction on top of these concepts.</p>

<h2 id="acronyms-and-glossary">Acronyms and glossary</h2>

<p>The NATS ecosystem is full of bullshit acronyms. I think the maintainers think they’re funny. They are also big into truncating words in their command-line tools, rather than just writing proper bash completions for them. Mostly all it does is make it harder to keep track of what everything means.</p>

<p>So far, we have:</p>

<ul>
  <li>NATS: “Neural Autonomic Transport System” – https://docs.nats.io/faq#what-does-the-nats-acronym-stand-for</li>
  <li>NGS: ??? (maybe “NATS Global Service”?)</li>
  <li>From https://docs.nats.io/nats-tools/nats-tools:
    <ul>
      <li>nats - Interact with and manage NATS</li>
      <li>nk - Generate NKey - nsc - Configure Operators, Accounts and Users</li>
      <li>nsc - Configure Operators, Accounts and Users <strong>(What does NSC mean? This tool also appears to include functionality from the nats and nk tools. Feels like one or more of these tools should be deprecated)</strong></li>
      <li>nats account server - Serve Account JWTs <strong>(this is sometimes abbreviated as <code class="language-plaintext highlighter-rouge">nas</code> in the docs, which collides in my head with “network attached storage”. This also sounds like it is a deprecated way to do auth with nats, since it is inlined)</strong></li>
      <li>nats top - Monitor NATS Server</li>
      <li>nats-bench - Benchmark NATS Server</li>
      <li>prometheus-nats-exporter - Export NATS server metrics to Prometheus and a Grafana dashboard.</li>
    </ul>
  </li>
</ul>

<h1 id="ngs">NGS</h1>

<p>Following along with https://synadia.com/ngs/signup</p>

<p>I quite like the <code class="language-plaintext highlighter-rouge">curl | python</code> approach. I guess maybe this is more portable than <code class="language-plaintext highlighter-rouge">curl | sh</code>?</p>

<p><code class="language-plaintext highlighter-rouge">nsc init -o synadia -n First</code> - I suppose if you made the tool then you can make sure your company is in the list of preconfigured operators. I wonder who else is in that list. Would there be any pushback if a big player like AWS started offering the same service, and wanted to be included in the list of operators?</p>

<p>I wonder how the <code class="language-plaintext highlighter-rouge">ngs.echo</code> topic works (whether it’s patched into the server, or a process that has access to that topic on all accounts). Same goes for the other things in <code class="language-plaintext highlighter-rouge">ngs.*</code>.</p>

<h2 id="default-connections-and-hidden-context">Default connections and hidden context</h2>

<p><code class="language-plaintext highlighter-rouge">nsc tool pub</code> implicitly reaches into some configs somewhere and works out where it needs to connect. Legacy tools like <code class="language-plaintext highlighter-rouge">nats-pub</code> don’t do this, and neither do client libraries (or tools that use them, like wasmcloud).</p>

<h2 id="leaf-nodes">Leaf nodes</h2>

<p>It feels like everyone wants you to install a nats leaf node as a sidecar (wasmcloud’s wash cli tool doesn’t even have a way to configure it to connect directly to NGS as a control plane). Following along with https://docs.nats.io/nats-server/configuration/leafnodes#leaf-node-example-using-a-remote-global-service shows you how to do it. You need a verified email address and payment method in order to do this.</p>

<h2 id="messaging-vs-queueing-systems">Messaging vs Queueing systems</h2>

<p>In its most basic configuration, NATS is not a queueing system, because it doesn’t have persistence or retries. It is more reasonable to think of it as a messaging system. I suppose in some ways it’s like UDP, but wrapped in TLS+auth, and with <code class="language-plaintext highlighter-rouge">foo.bar.baz</code>-style broadcast addresses. It’s a good fit for writing your app cluster’s backplane in, but not necessarily your job scheduler.</p>

<p>There is a mode in NATS (<code class="language-plaintext highlighter-rouge">req</code>) where you can send a return-address with your message, and the receiver[s] can respond to that return address. This primitive can be used to build reliable systems (like how TCP is built on top of IP packets).</p>

<p>On top of this NATS backplane, you can add a bunch of applications, and connect them up. The NATS server comes bundled with a JetStream application, that you can enable with the <code class="language-plaintext highlighter-rouge">-js</code> flag. This is basically rabbitmq-over-NATS, but it can get away with being a lot simpler, because its communication protocol is NATS rather than TCP.</p>

<h2 id="conclusions">Conclusions</h2>

<p>I can see why WasmCloud chose NATS for its “lattice” backplane. The basic protocol feels simple and solid. It feels like the kind of protocol that you’d expect to see described in an RFC. Once you have this communication system in place, the work of doing inter-process communication feels like it will have a lot less friction. The difference between using HTTPS and NATS is like the difference between setting up unix domain sockets for each of your process vs using D-Bus (although D-Bus is terrible in ways that NATS manages to sidestep).</p>

<p>It is a reasonable initial reaction that the basic NATS protocol doesn’t quite get you all the way. I think that the JetStream covers some of those holes, but I’m not sure whether it belongs in the core NATS server implementation. I’m hoping that the nats-server implementation continues to gain traction without adding too much bloat (or if it does get bloated, that they release a tiny-nats-server binary that can be used as a low-overhead kubernetes sidecar).</p>]]></content><author><name></name></author><summary type="html"><![CDATA[First impressions of technologies are quite important for driving adoption, so I’ve started writing down my early impressions as I explore different technologies.]]></summary></entry><entry><title type="html">Early impressions of WasmCloud</title><link href="http://alsuren.github.io/2021/07/06/wasmcloud-impressions.html" rel="alternate" type="text/html" title="Early impressions of WasmCloud" /><published>2021-07-06T00:00:00+00:00</published><updated>2021-07-06T00:00:00+00:00</updated><id>http://alsuren.github.io/2021/07/06/wasmcloud-impressions</id><content type="html" xml:base="http://alsuren.github.io/2021/07/06/wasmcloud-impressions.html"><![CDATA[<p>First impressions of technologies are quite important for driving adoption, so I’ve started writing down my early impressions as I explore different technologies.</p>

<p>WasmCloud is an application platform built on WASM (like erlang’s BEAM, but you deploy WASM to it rather than erlang).</p>

<p>(Originally posted on the <a href="https://awesome.red-badger.com/davidlaban/">red-badger tech site</a>)</p>

<h2 id="documentation">Documentation</h2>

<p>You start at wasmcloud.dev (“Docs Home”), and can follow a getting started guide from there, but there is also a “Home” link in the top bar that takes you to wasmcloud.com, which links out to a bunch of commercial courses. I haven’t looked into the courses, but the fact that they exist feels reassuring.</p>

<p>The rest of the documentation is not particularly polished. There are broken links in a few places, and a handful of typos, including:</p>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">$ ctl link MCUDTMOOZCVAM5EBNN4U3X2OGHNIY3BKPEW66HY4RTCYYVWXOE7ESVDQ VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4A YJ5RKAX2UNSCAPHA5M wasmcloud:httpserver PORT=8080</code> in https://github.com/wasmcloud/examples/tree/main/echo (probably caused by copy-pasta out of the wash shell tui)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Both Architecture sections are missing (https://wasmcloud.dev/reference/host-runtime/architecture/ and https://wasmcloud.dev/reference/lattice/architecture/). This feels like a job for excalidraw.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />s/on-premise/on-premises/ - https://wasmcloud.dev/reference/lattice/leaf-nodes/</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />https://crates.io/crates/wash-cli “control-interface” links to https://github.com/wasmcloud/wasmcloud/tree/main/crates/control-interface, which is a broken link.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Get some CI to check that they don’t have any broken links?</li>
</ul>

<h2 id="wash"><code class="language-plaintext highlighter-rouge">wash</code></h2>

<p>The getting started guide recommends that you have wash set to 250 characters wide. This is a bit ridiculous. It also has a “REPL (Standalone)” thing that flashes in the top-left corner. Why? Can you please just not. (I ended up cloning the repo and patching this out)</p>

<p>Can I just run wash in a daemon mode, and inject commands into it from bash? When looking into this, I went to https://github.com/wasmCloud/wash and found a broken link to <code class="language-plaintext highlighter-rouge">control-interface</code> (looks like this has been split into the wasmCloud repo).</p>

<h2 id="wasmcloud">wasmcloud</h2>

<p>My instinct is always to live in the root of a git repo, so when following along with https://github.com/wasmcloud/examples/tree/main/echo, I did <code class="language-plaintext highlighter-rouge">wasmcloud -m echo/manifest.yaml</code>. This caused wasmcloud to try resolving .wasm filenames in the root of the repo rather than in the echo dir. It would be much cleaner for imports to be relative to the manifest file rather than wasmcloud’s <code class="language-plaintext highlighter-rouge">$PWD</code>.</p>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />This probably isn’t a very difficult fix. I should open an issue or make a PR at some point.</li>
</ul>

<h2 id="docker-compose">docker-compose</h2>

<p>I like that their instructions get you to run a docker registry on localhost:5000 in their docker-compose setup. When following along with the dapr kubernetes instructions, they wanted you to use a real container registry, so you end up uploading your images and downloading them, even though you are only running things on localhost (Cedd flagged this at the time, but I couldn’t remember off the top of my head what the best way to do it was). This way is much nicer.</p>

<p>Once you have this working, it starts a NATS server, so you end up in lattice mode. You can then leave the wash shell in a background tab and type commands like <code class="language-plaintext highlighter-rouge">wash ctl get hosts</code> and get output from stdout where you expect, which makes me want to throw up a bit less.</p>

<h2 id="zero-downtime-migrations">zero downtime migrations</h2>

<p>I wanted to tear down the <code class="language-plaintext highlighter-rouge">wash</code> ui entirely, because it makes me sad. Could I add a wasmcloud host on the command-line and then give it a new web server capability provider and tear down the wash node?</p>

<ul>
  <li>Starting a new node:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">wasmcloud</code></li>
      <li><code class="language-plaintext highlighter-rouge">wash ctl get hosts</code></li>
    </ul>
  </li>
  <li>… there is no way to migrate actors, so create a new one
    <ul>
      <li><code class="language-plaintext highlighter-rouge">wash ctl start actor localhost:5000/echo:0.2.2</code></li>
      <li>… oh, that comes up with the same ID, doesn’t it? Does that mean it’s already linked to the http server?</li>
      <li>Maybe? I’m not sure.</li>
    </ul>
  </li>
  <li>What about a capability provider?
    <ul>
      <li><code class="language-plaintext highlighter-rouge">wash ctl start provider wasmcloud.azurecr.io/httpserver:0.11.1</code></li>
      <li>the capability provider then spits out: <code class="language-plaintext highlighter-rouge">thread '&lt;unnamed&gt;' panicked at 'called </code>Result::unwrap()<code class="language-plaintext highlighter-rouge">on an</code>Err<code class="language-plaintext highlighter-rouge"> value: Os { code: 48, kind: AddrInUse, message: "Address already in use" }', src/lib.rs:91:14</code>, which is not <strong>hugely</strong> surprising. This wouldn’t be a problem if it was running in a docker container, I suppose. It is possible to make this work on linux using SO_REUSEPORT (https://lwn.net/Articles/542629/), but it’s a huge hack, and I would not expect actix to support this out of the box.</li>
    </ul>
  </li>
  <li>Okay, then bring down the original node, and see what happens:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">curl localhost:8080/echo</code>: <code class="language-plaintext highlighter-rouge">curl: (7) Failed to connect to localhost port 8080: Connection refused</code></li>
      <li>Not surprising, given the panic</li>
    </ul>
  </li>
  <li>Can we kick the provider into action again?
    <ul>
      <li><code class="language-plaintext highlighter-rouge">wash ctl link MDNYN4IMBBLOCOTWSRES4NLLLHAMH6UEX527K7M6HPEGRZFG3HVHTXJL VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M wasmcloud:httpserver PORT=8080</code></li>
      <li><code class="language-plaintext highlighter-rouge">curl localhost:8080/echo</code>: <code class="language-plaintext highlighter-rouge">{"method":"GET","path":"/echo","query_string":"","headers":{"host":"localhost:8080","accept":"*/*","user-agent":"curl/7.64.1"},"body":[]}</code></li>
      <li>looks like that worked.</li>
    </ul>
  </li>
</ul>

<p>I didn’t get any indication that my second capability provider was in a non-functioning state, apart from the panic in the logs. This isn’t hugely encouraging.</p>

<h2 id="what-next">What next?</h2>

<p>Probably try starting a wasmcloud host inside docker, and retry the above dance.</p>

<blockquote>
  <p>In order to do this, I made an excursion into setting up the appropriate security creds for my own NATS cluster. This ended up defeating me. I might try again following a tutorial, rather than going it alone in a docker-compose sandbox. I ended up creating an NGS Developer account.</p>
</blockquote>

<h2 id="connecting-via-ngs">Connecting via NGS</h2>

<p>If you want to connect via a leaf node, follow along with this tutorial to get a leaf node set up: https://docs.nats.io/nats-server/configuration/leafnodes#leaf-node-example-using-a-remote-global-service</p>

<p>If you want to do it all in a single process, you can do it via command-line arguments or environment variables (thanks <code class="language-plaintext highlighter-rouge">structopt</code>):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">CONTROL_HOST</span><span class="o">=</span>connect.ngs.global <span class="se">\</span>
<span class="nv">RPC_HOST</span><span class="o">=</span>connect.ngs.global <span class="se">\</span>
<span class="nv">CONTROL_CREDS</span><span class="o">=</span><span class="nv">$HOME</span>/.nkeys/creds/synadia/leaftest/leaftestuser.creds <span class="se">\</span>
<span class="nv">RPC_CREDS</span><span class="o">=</span><span class="nv">$HOME</span>/.nkeys/creds/synadia/leaftest/leaftestuser.creds <span class="se">\</span>
wasmcloud
</code></pre></div></div>

<h2 id="wash-control-plane-access"><code class="language-plaintext highlighter-rouge">wash</code> control plane access</h2>

<p>Connecting <code class="language-plaintext highlighter-rouge">wash</code> to a local leaf node seems to be a supported configuration. There doesn’t seem to be a way to specify a control-plane credentials file.</p>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Maybe I will make a patch for this. Done: https://github.com/wasmCloud/wash/pull/146</li>
</ul>

<p>It seems that control plane access from the wasm host is typically passwordless via the leaf node. This makes me nervous. Control-plane access feels like it’s equivalent to root access to your entire cluster. If anyone manages to compromise the wasm sandbox, or any of your capability providers then they just need to write <code class="language-plaintext highlighter-rouge">pub $some.topic $some.payload</code> to tcp localhost:4222.</p>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />work out what <code class="language-plaintext highlighter-rouge">$some.topic $some.payload</code> needs to look like to be sufficiently scary</li>
</ul>

<h2 id="developer-experience-experiment">Developer experience Experiment</h2>

<p>Let’s make a TodoMVC demo</p>

<p>https://github.com/redbadger/wasmcloud-examples/projects/1</p>

<p>There are a bunch of things in our backlog that were paper cuts that are probably easy to fix.</p>

<h3 id="adding-a-logging-capability">Adding a logging capability</h3>

<blockquote>
  <p>[2021-07-01T12:53:03Z ERROR] The target wasmcloud:logging was not found as an actor public key, an actor call alias, or as the contract ID in an existing link from source actor MBQ3PZKT6JSEAL4UH7WPZDR2OK7WESH352XH56ORRFC6EE4YPYE5JAEX</p>
</blockquote>

<p>This happened because I wrote a <code class="language-plaintext highlighter-rouge">warn!()</code> line in my <code class="language-plaintext highlighter-rouge">#[actor::init]</code> function. There could probably be some diagnostic hints for this.</p>

<h3 id="actor-ids">Actor ids</h3>

<p>It’s very frustrating that you have to find a way to take the actor id from <code class="language-plaintext highlighter-rouge">target/*/*/whatever_s.wasm</code> or <code class="language-plaintext highlighter-rouge">wasmcloud.azurecr.io/kvcounter:0.2.0</code> and paste it into your <code class="language-plaintext highlighter-rouge">manifest.yaml</code> (or side-loading it via <code class="language-plaintext highlighter-rouge">${VAR:default}</code> environment variable hackery)</p>

<p>Really, I feel like the manifest format wants a way to say “whatever id you find at the following path/container registry url”, and then you could store the mappings from path to signature in a lockfile, that you can optionally check in. I should make a ticket for this.</p>

<h3 id="wash-again"><code class="language-plaintext highlighter-rouge">wash</code> again</h3>

<p>We ended up using <code class="language-plaintext highlighter-rouge">wash --watch</code> in our example’s Makefile. This gave the hot-reloading experience, but also forced us to use <code class="language-plaintext highlighter-rouge">wash</code>. It makes sense that <code class="language-plaintext highlighter-rouge">wasmcloud --watch</code> isn’t a thing, since it’s not really a thing that you would expect to need in production, but <code class="language-plaintext highlighter-rouge">wash</code>’s TUI is really terrible. A reasonable middle-ground might be to have a mode similar to <code class="language-plaintext highlighter-rouge">bluetoothctl</code> on linux, where there is a prompt at the bottom, and logs are printed above the prompt, so <code class="language-plaintext highlighter-rouge">wash --no-tui</code> becomes a repl with streaming logs above. I think there is some black magic involved in making this work though, so I’m not expecting anything soon.</p>

<h2 id="scaling">Scaling</h2>

<p>In wasmcloudland, the unit of scale is wasmcloud host (like it is a pod in kubernetesland). There is no way to scale actors within a single wasmcloud host. There is a ticket about this already, at https://github.com/wasmCloud/wasmCloud/issues/54. It’s definitely worth waiting for them to get this right before adopting wasmcloud for real applications.</p>

<h2 id="conclusions">Conclusions</h2>

<p>(Yes: I’m introducing things in my conclusion section that don’t appear anywhere else in the document. Sue me.)</p>

<p>I think that most of the things above are paper-cuts, and symptoms of the fact that wasmcloud isn’t very mature yet. The wasmcloud team is small, and they seem to be focussing on the big architectural decisions, so we can expect a bit of a lack of polish as the big ticket items are figured out.</p>

<p>Architecturally, wasmcloud feels like it’s what the future will look like. In the next couple of years, I expect that either wasmcloud will get there or something will come along that looks surprisingly like wasmcloud and get to a polished platform first. Either way, learning wasmcloud has given me a glimpse into the future.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[First impressions of technologies are quite important for driving adoption, so I’ve started writing down my early impressions as I explore different technologies.]]></summary></entry></feed>