<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>All Posts · unix.foo</title>
    <link>/posts/</link>
    <description>A series of essays on technology from an open-source and unix perspective.</description>
    <generator>Hugo 0.161.1 (Unixy theme)</generator>
    <language>en</language>
    
    
    <copyright>&#xA9; 2026 Cyrus</copyright>
    <lastBuildDate>Sun, 16 Jul 2023 00:00:00 +0000</lastBuildDate>
    <atom:link href="/posts/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>It Will Never Be the Year of the Linux Desktop</title>
      <link>/posts/it-will-never-be-the-year-of-the-linux-desktop/</link>
      <pubDate>Wed, 27 May 2026 00:21:33 -0800</pubDate>
      
      <guid>/posts/it-will-never-be-the-year-of-the-linux-desktop/</guid>
      <description>The agent era will not be on Linux</description>
      <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/">&lt;![CDATA[<p>Every year someone says that this is the year of the Linux desktop.</p>
<p>It is never the year of the Linux desktop.</p>
<p>There are many reasons for this. Drivers. Games. Adobe. Microsoft Office. Battery life. The thing where you close the lid of a laptop and open it again later to find that it passed into the good night. These explanations are all correct in the small and unsatisfying in the large. They explain why a person did not switch to Linux last Thursday. They do not explain why the desktop, as an institution, will continue to belong to Apple and Microsoft.</p>
<p>And now there is a new and more depressing explanation.</p>
<p>The future computer user is not a person.</p>
<p>Or at least <em>not only</em> a person. The robots are coming for the desktop. The interesting part is that the ramps were already there.</p>
<p>They were called accessibility APIs.</p>
<p>If you use a Mac and open the Accessibility Inspector tool that’s built into the system (you really should try it), you can see a second version of the computer, hiding inside the first one. The first version is the one you look at: windows, shadows, rounded rectangles, a little bouncing icon in the Dock from Slack announcing that you are falling behind.</p>
<p>The second version is a tree. A literal hierarchy of objects. Window. Group. Button. Text field. Scroll area. Static text. Each object has properties. Some have values. Some have actions. Some will tell you where they are. Some will tell you what they contain. Some will let you press them without moving the mouse at all.</p>
<figure class="figure--book-cover" style="width: 400px; max-width: 28vw; margin-left: auto; margin-right: auto;">
  
  <img src="/images/accessibility_tool.png" alt="Screenshot of Accessibility Inspector tool" width="420" height="691" loading="lazy">
  
  
</figure>

<p>This is not how computers were initially designed to be used, if by “used” you mean “used by sighted people moving a pointer around.” It is how computers had to be exposed to people who could not rely on pixels. VoiceOver needed it. Switch control needed it. Dictation systems needed it. The operating system had to learn to describe itself.</p>
<p>And now the agents need it too.</p>
<p>You can see this most clearly in OpenAI&rsquo;s <a href="https://developers.openai.com/codex/app/computer-use" target="_blank" rel="noopener noreferrer">Codex Computer Use</a>
 feature, which on macOS doesn&rsquo;t just take a screenshot. It also pulls &ldquo;available text&rdquo; out of the frontmost window including text the app makes available outside the visible scroll area, which is to say, content that is technically not on the screen at all. It also allows the agent to interact with your entire Mac <strong>without</strong> interrupting your usage as it has its own independent mouse that can work in the background.</p>
<p>OpenAI bought the company that built this in October 2025: a twelve-person shop called Software Applications Incorporated, whose product, Sky, had never been publicly released. Sam Altman had personally invested in the seed round. The founders had previously sold Workflow to Apple, where it became Shortcuts. What OpenAI got for an undisclosed but evidently real amount of money was the team&rsquo;s bet about the right way for an AI model to drive a Mac. The bet appears to have been correct. The binary that runs this inside Codex today is still named <code>SkyComputerUseClient</code>.</p>
<p>This is the part where you might expect me to say that the reason macOS is suddenly so good for agents is the accessibility API. But that’s not really the full story. Windows has accessibility APIs. Linux has accessibility APIs. APIs are easy to have. You write them down in a header file, give a conference talk about them, and then spend the next twenty years explaining why nobody used them correctly.</p>
<p>The reason macOS is so far ahead is because of defaults.</p>
<p>Apple did not, when most of this was being soldered into place in the late 1990s, anticipate that a stochastic parrot with an $800+ billion valuation would one day need to change a setting in Finder. Apple just decided that if you build a normal Mac app out of normal Mac controls with things like <code>NSButton</code>, <code>NSTextField</code>, <code>WKWebView</code>, the boring stock pieces then your app should be accessible by default. The developer didn&rsquo;t have to do anything. They wrote a regular app and got a high-fidelity accessibility tree for free, because Apple put the cost of compliance into the SDK instead of the application. The blind user got the tree. The accidental beneficiary, all these years later, is Codex.</p>
<p>This is one of those situations where a moral concern turns out, in retrospect, to have also been infrastructure.</p>
<p>For most of software history, accessibility was treated by most engineering teams as either a compliance chore, an act of kindness, or a thing you would get to at the end if there was time, which there never was, because the only features that were ever truly protected were the ones that affected someone’s bonus.</p>
<p>This was always wrong! But it is now wrong in a way that rich people can understand. A bad accessibility tree no longer excludes only disabled users. It also excludes agents. Accessibility is, by accident, becoming agent compatibility.</p>
<p>Agents are now new customers. History is not sentimental about motives. The accessibility tree was built for assistive technology, and now the robots in the machine wants to use it to book a flight.</p>
<p>And in this area, the Mac is truly far ahead.</p>
<p>Windows, in its defense, has a very serious accessibility tree. Microsoft UI Automation (UIA) is, in some ways, the most Microsoft thing imaginable.</p>
<p>It is a complete object model of the desktop with three filtered views: raw, control, and content. Because of course Microsoft looked at the question of &ldquo;what is on screen&rdquo; and decided one ontology would not suffice. It has a real pattern system: <code>InvokePattern</code> for buttons, <code>TextPattern</code> for documents, <code>ValuePattern</code> for inputs, and an enumeration of verbs that controls admit to supporting.</p>
<p>Microsoft&rsquo;s <a href="https://learn.microsoft.com/en-us/windows/win32/winauto/entry-uiauto-win32" target="_blank" rel="noopener noreferrer">own documentation</a>
 cheerfully observes that this same API can be used by assistive technologies and by automated test scripts, which has turned out to be the most prescient sentence Microsoft has written about Windows in many years.</p>
<p>UI Automation is, by any reasonable engineering standard, excellent.</p>
<p>The problem with Windows is not the API. The problem is archaeology. Every Windows machine is a museum of electricity. There&rsquo;s not one type of app. There is Win32. There is WPF. There is WinForms. There is UWP. There is WinUI. There is Electron. There is some custom line-of-business application written by a contractor in 2009 who has since moved to a farm and cannot be reached. There is a settings panel that is secretly a web page. There is a desktop app that is secretly Chromium wearing a fake mustache. The list goes on.</p>
<p>UIA can be very good. But the app has to meet it halfway. And on Windows the app frequently does not meet it halfway. It&rsquo;s nearly unusable. A UIA tree scanned across a real Windows desktop is full of regions that respond, with admirable consistency, the way an empty house responds to a knock.</p>
<p>The recurring theme here is that an agent does not just need an API. It needs a civilization of apps that conform to the API well enough that the agent can trust what they say. A button that admits to being a button. A text field that admits to containing text. A table that does not expose itself as fourteen hundred unnamed rectangles and a prayer.</p>
<p>Which brings us to the mess of Linux.</p>
<p>To be fair, and one should be fair about this, because Linux folks can smell imprecision through concrete, Linux does have an accessibility stack! It is called <a href="https://en.wikipedia.org/wiki/AT-SPI" target="_blank" rel="noopener noreferrer">AT-SPI</a>
, the Assistive Technology Service Provider Interface, and it is real. It runs over D-Bus. It exposes Accessible, Action, Component, Document, Text, Value, and so on. GTK apps support it. Qt apps support it. Firefox supports it. LibreOffice supports it. Orca, the GNOME screen reader, has been in production on it since 2006.</p>
<p>But agents do not just need an accessibility tree. They need to enumerate windows. They need to capture the screen. They need to synthesize input. They need a coherent permission model. They need to do all of this without the user feeling like they are watching a haunted mouse perform community theater.</p>
<p>On a Mac, this is one Accessibility toggle and one Screen Recording toggle, both clearly named, both stored in the same general place. On Linux under Wayland, the screen capture is a portal, the input synthesis is a different portal or <code>libei</code>, the window enumeration is a per-compositor protocol, and the cross-compositor accessibility evolution, called Newton, is a prototype being developed by a man named Matt Campbell on a grant from the Sovereign Tech Fund. A GNOME Foundation report from April 2025 describes the protocol as &ldquo;not yet rigorously defined&rdquo; and notes it &ldquo;has not yet seen any cross-desktop discussions.&rdquo; KDE has not committed to it.</p>
<p>Every step of the loop is available, after installing the correct backend and selecting the correct session type and, depending on the day, sacrificing a small goat to the compositor. Apple can force attention. Microsoft can institutionalize it. Linux has to convene it.</p>
<p>The problem is that Linux can make almost anything exist, but it cannot make almost everyone agree to care about it at the same time. This is the part that has kept the year-of-the-Linux-desktop joke alive long after Linux became, on most days, a perfectly usable desktop. StatCounter has it at 2.99% of global desktop usage in April 2026, up from 2.76% in 2022. Continents move faster. But you can put Ubuntu on a ThinkPad and do most of what a normal person needs to do, and people do. The desktop got pretty good.</p>
<p>The mission, by its original definition, is mostly accomplished.</p>
<p>Nobody is throwing a party because the target is about to move.</p>
<p>The target is moving because the standard for &ldquo;usable desktop&rdquo; is no longer whether you would enjoy using it. The standard is whether <em>a thing that is not you</em> can use it on your behalf.</p>
<p>The high-fidelity accessibility tree, the reliable input synthesis, the standardized window enumeration, the portable screen capture, the coherent permission model. Apple has been building exactly this for thirty years, paid for by Cupertino, almost entirely for the benefit of users who number in the low millions. The work is now, accidentally and irreversibly, also the substrate for agents, which are about to number in the billions.</p>
<p>Microsoft has been engineering most of it but letting half the platform skip the homework. The Linux community has been building parts of it, in scattered repositories, by ones and twos, often on grants, often by one guy in Nebraska.</p>
<p>This is not the kind of gap a community closes by writing better software. It is the kind of gap that takes a decade of full-time employees auditing every label in every default app, a market mechanism that punishes you when you don&rsquo;t, and a centralized review process to enforce it from above.</p>
<p>None of that exists for Linux. None of it is coming.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Nobody Cracks Open a Programming Book Anymore</title>
      <link>/posts/nobody-cracks-open-a-programming-book/</link>
      <pubDate>Mon, 25 May 2026 00:21:33 -0800</pubDate>
      
      <guid>/posts/nobody-cracks-open-a-programming-book/</guid>
      <description>A sad look at the state of modern programming books.</description>
      <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/">&lt;![CDATA[<p>There was, for a long time, a wall.</p>
<p>If you walked into a book store, past the magazines and the cookbooks, you&rsquo;d arrive at the computer section, and along one wall there was a stretch of books with cartoon animals on their covers. A rhino for JavaScript. A camel for Perl. A python (obviously) for Python. And whatever this was:</p>
<figure class="figure--book-cover" style="width: 200px; max-width: 28vw; margin-left: auto; margin-right: auto;">
  
  <img src="/images/vim.jpg" alt="Cover of vi Editor Pocket Reference" width="420" height="691" loading="lazy">
  
  
</figure>

<p>They were thick, they cost about $50, and they had titles like &ldquo;Learning React&rdquo; and &ldquo;HTTP: The Definitive Guide&rdquo;. If you wanted to learn how to do a thing on a computer, you bought one of these, took it home, and opened it up next to your computer and typed what it said until the thing worked.</p>
<p>That wall is smaller now. If it&rsquo;s even still there. In some stores the wall is gone and relegated to a small rack that has six books on it, three of which are about ChatGPT.</p>
<p>Through the first nine months of 2023, sales in the &ldquo;computer book&rdquo; category at Circana BookScan (the industry&rsquo;s standard tracker, which costs roughly the price of a small used car to subscribe to) were down 16.9% year over year. Publishers Weekly, which had been dutifully reporting these figures in its quarterly narrative summaries, kept doing so right up through that 16.9% figure, and then in 2024 and 2025 simply stopped mentioning the category by name.</p>
<p>To be clear, books in general are doing fine. Total U.S. print sales reached 762.4 million units in 2025, up 0.3% over 2024, which was itself up 0.5% over 2023. The category that is in trouble is the part of it that teaches you how to make software. The American Association of Publishers&rsquo; &ldquo;professional books&rdquo; segment, which is the rough corporate proxy for &ldquo;books your employer might buy you,&rdquo; fell 22.3% in August 2025.</p>
<p>The book industry is fine but the technical end is bleeding out.</p>
<p>Quickly and quietly. There was no Napster moment for the programming book. Nobody filed a lawsuit. The publishers did not, as far as I can tell, even hold a press conference. We simply found one day that they stopped reporting the category itself. The category doesn&rsquo;t die, it just stops being talked about.</p>
<p>You already know why, more or less. ChatGPT has over 900 million monthly active users. GitHub Copilot has 4.7 million paying subscribers as of January 2026, up roughly 75% in a year. You can&rsquo;t imagine writing software without Claude Code anymore.</p>
<p>Stack Overflow is receiving about 3,800 questions a month, which is what it was getting in 2008, before it had finished being launched. The chatbots have eaten the demand for the kinds of answers that programming books used to provide.</p>
<p>The programming book was, when you look at it squarely, always a slightly absurd object. Printed text on bound paper, describing software that lived on screens, which the reader had to retype, by hand, into a screen of their own. I loved doing this and they remain some of my very fondest childhood memories. But the medium was wrong for the content. People put up with it because there was no better way to get a careful sustained explanation of a technical thing into one person&rsquo;s head from another person&rsquo;s.</p>
<p>What the book was good at, despite being the wrong format, was forcing both the writer and the reader to be slow. You cannot fake your way through 400 pages. It took a certain discipline to get through.</p>
<p>The chatbot does not have this discipline. The chatbot has read every book and forgotten the point of every one of them. It will explain idempotency in the precise number of words you require, and you will close the tab, and you will not remember what it told you, because you did not type it.</p>
<p>That last sentence is the <em>whole thing</em>. Knowledge, for working programmers, was always the residue of typing. Of <em>doing</em>. The typing was the practice! What is going away is the typing.</p>
<p>Which, on balance, may be fine. I don&rsquo;t know. People used to lose weekends to installing Linux from a stack of floppies and struggling with WinModems, and nobody pretends that was character-building (though I now consider them fond memories too). Tools get easier. Skills shift. The kid who is right now learning to code by chatting with an agent is not a worse programmer than I was at 12, hunched over Learning Perl, retyping examples that would not run because I missed a semicolon.</p>
<p>That kid is a different programmer. They are, in some ways I don&rsquo;t fully understand, working at a higher level of abstraction than I ever did at that age, and the things they will build with that abstraction will surprise me.</p>
<p>But somewhere in a used bookstore in San Francisco or Seattle or wherever used bookstores still exist, there is a 1997 edition of Learning Perl. It smells faintly of basement. Someone wrote their name in the front of it in pencil. There is a furiously underlined sentence in chapter 7 about regular expressions that was made in anger. On page 112 there are coffee stains where the caffeine blots are somehow still a valid Perl program.</p>
<p>The book costs three dollars. <strong>Nobody is going to buy it.</strong></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Local AI Needs to be the Norm</title>
      <link>/posts/local-ai-needs-to-be-norm/</link>
      <pubDate>Tue, 30 Dec 2025 00:21:33 -0800</pubDate>
      
      <guid>/posts/local-ai-needs-to-be-norm/</guid>
      <description>Local AI models should be the default.</description>
      <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/">&lt;![CDATA[<p>One of the current trends in modern software is for developers to slap an API call to OpenAI or Anthropic for features within their app. Reasonable people can quibble with whether those features are actually bringing value to users, but what I want to discuss is the fundamental concept of taking on a dependency to a cloud hosted AI model for applications.</p>
<p>This laziness is creating a generation of software that is fragile, invades your privacy, and fundamentally broken. We are building applications that stop working the moment the server crashes or a credit card expires.</p>
<p>We need to return to a habit of building software where our local devices do the work. The silicon in our pocket is mind bogglingly faster than what was available a decade ago. It has a dedicated Neural Engine sitting there, mostly idle, while we wait for a JSON response from a server farm in Virginia. That’s ridiculous.</p>
<p>Even if your intentions are pure, the moment you stream user content to a third party AI provider, you’ve changed the nature of your product. You now have data retention questions and all the baggage that comes with that (consent, audit, breach, government request, training, etc.)</p>
<p>On top of that you also substantially complicated your stack because your feature now depends on network conditions, external vendor uptime, rate limits, account billing, and your own backend health.</p>
<p>Congratulations! You took a UX feature and turned it into a <strong>distributed system that costs you money.</strong></p>
<p>If the feature can be done locally, opting into this mess is self inflicted damage.</p>
<p>“AI everywhere” is not the goal. <strong>Useful software is the goal.</strong></p>
<h2 id="concrete-example-brutalist-reports-on-device-summaries">
  <a href="#concrete-example-brutalist-reports-on-device-summaries" class="heading-anchor" aria-label="Link to this section">Concrete Example: Brutalist Report’s On-Device Summaries</a>
</h2>
<p>Years ago I launched a fun side project named <a href="https://brutalist.report" target="_blank" rel="noopener noreferrer">The Brutalist Report</a>
 which is a news aggregator service inspired by the 1990s style web.</p>
<p>Recently, I decided to build a <a href="https://apps.apple.com/app/brutalist-report/id6756546583" target="_blank" rel="noopener noreferrer">native iOS client</a>
 for it with the design goal of ensuring it would remain a high-density news reading experience. Headlines in a stark list, a reader mode that strips the cancer that has overtaken the web, and (optionally) an “intelligence” view that generates a summary of the article.</p>
<div class="imgrow" style="--imgrow-cols: 3;"><a href="https://apps.apple.com/app/brutalist-report/id6756546583" rel="noopener">
        <img src="/images/brutalist_ios_main.png" alt="Brutalist iOS Main View">
      </a><a href="https://apps.apple.com/app/brutalist-report/id6756546583" rel="noopener">
        <img src="/images/brutalist_ios_readerview.png" alt="Brutalist iOS AI View">
      </a><a href="https://apps.apple.com/app/brutalist-report/id6756546583" rel="noopener">
        <img src="/images/brutalist_ios_aiview.png" alt="Brutalist iOS AI View">
      </a></div>

<p>Here’s the key point though: the summary is generated <strong>on-device</strong> using Apple’s local model APIs. No server detours. No prompt or user logs. No vendor account. No “we store your content for 30 days” footnotes needed.</p>
<p>It has become so normal for folks that any AI use is happening server-side. We have a lot of work to do to turn this around as an industry.</p>
<p>It’s not lost on me that sometimes the use-cases you have will demand the intelligence that only a cloud hosted model can provide, but that’s not the case with every use-case you’re trying to solve. We need to be thoughtful here.</p>
<h2 id="available-tooling">
  <a href="#available-tooling" class="heading-anchor" aria-label="Link to this section">Available Tooling</a>
</h2>
<p>I can only speak on the tooling available within the Apple ecosystem since that&rsquo;s what I focused initial development efforts on. In the last year, Apple has invested heavily here to allow developers to make use of a built-in local AI model easily.</p>
<p>The core flow looks roughly like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">FoundationModels</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">model</span> <span class="p">=</span> <span class="n">SystemLanguageModel</span><span class="p">.</span><span class="k">default</span>
</span></span><span class="line"><span class="cl"><span class="k">guard</span> <span class="n">model</span><span class="p">.</span><span class="n">availability</span> <span class="p">==</span> <span class="p">.</span><span class="n">available</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">session</span> <span class="p">=</span> <span class="n">LanguageModelSession</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">  Provide a brutalist, information-dense summary in Markdown format.
</span></span></span><span class="line"><span class="cl"><span class="s">  - Use **bold** for key concepts.
</span></span></span><span class="line"><span class="cl"><span class="s">  - Use bullet points for facts.
</span></span></span><span class="line"><span class="cl"><span class="s">  - No fluff. Just facts.
</span></span></span><span class="line"><span class="cl"><span class="s">  &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">response</span> <span class="p">=</span> <span class="k">try</span> <span class="n">await</span> <span class="n">session</span><span class="p">.</span><span class="n">respond</span><span class="p">(</span><span class="n">options</span><span class="p">:</span> <span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="n">maximumResponseTokens</span><span class="p">:</span> <span class="mi">1_000</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">articleText</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">markdown</span> <span class="p">=</span> <span class="n">response</span><span class="p">.</span><span class="n">content</span>
</span></span></code></pre></div><p>And for longer content, we can chunk the plain text (around 10k characters per chunk), produce concise “facts only” notes per chunk, then runs a second pass to combine them into a final summary.</p>
<p>This is the kind of work local models are <em>perfect</em> for. The input data is already on the device (because the user is reading it). The output is lightweight. It&rsquo;s fast and private. It&rsquo;s okay if it&rsquo;s not a superhuman PhD level intelligence because it&rsquo;s summarizing <em>the page you just loaded</em>, not inventing world knowledge.</p>
<p><strong>Local AI shines when the model’s job is transforming user-owned data, not acting as a search engine for the universe.</strong></p>
<p>There are plenty of AI features that people <em>want</em> but don’t <em>trust</em>. Summarizing emails, extract action items from notes, categorize this document, etc.</p>
<p>The usual cloud approach turns every one of those into a trust exercise. “Please send your data to our servers. We promise to be cool about it.”</p>
<p>Local AI changes that. Your device already has the data. We’ll do the work right here.</p>
<p>You don’t build trust with your users by writing a 2,000 word privacy policy. You build trust by not needing one to begin with.</p>
<p>The tooling available on the platform goes even further.</p>
<p>One of the best moves Apple has made recently is pushing “AI output” away from unstructured blobs of text and toward <em>typed data</em>.</p>
<p>Instead of “ask the model for JSON and pray”, the newer and better pattern is to define a Swift <code>struct</code> that represents the thing you want. Give the model guidance for each field in natural language. Ask the model to generate an instance of that type.</p>
<p>That&rsquo;s it.</p>
<p>Conceptually, it looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">FoundationModels</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="n">Generable</span>
</span></span><span class="line"><span class="cl"><span class="kd">struct</span> <span class="nc">ArticleIntel</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="p">@</span><span class="n">Guide</span><span class="p">(</span><span class="n">description</span><span class="p">:</span> <span class="s">&#34;One sentence. No hype.&#34;</span><span class="p">)</span> <span class="kd">var</span> <span class="nv">tldr</span><span class="p">:</span> <span class="nb">String</span>
</span></span><span class="line"><span class="cl">  <span class="p">@</span><span class="n">Guide</span><span class="p">(</span><span class="n">description</span><span class="p">:</span> <span class="s">&#34;3–7 bullets. Facts only.&#34;</span><span class="p">)</span> <span class="kd">var</span> <span class="nv">bullets</span><span class="p">:</span> <span class="p">[</span><span class="nb">String</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">@</span><span class="n">Guide</span><span class="p">(</span><span class="n">description</span><span class="p">:</span> <span class="s">&#34;Comma-separated keywords.&#34;</span><span class="p">)</span> <span class="kd">var</span> <span class="nv">keywords</span><span class="p">:</span> <span class="p">[</span><span class="nb">String</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">session</span> <span class="p">=</span> <span class="n">LanguageModelSession</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">response</span> <span class="p">=</span> <span class="k">try</span> <span class="n">await</span> <span class="n">session</span><span class="p">.</span><span class="n">respond</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">to</span><span class="p">:</span> <span class="s">&#34;Extract structured notes from the article.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">generating</span><span class="p">:</span> <span class="n">ArticleIntel</span><span class="p">.</span><span class="kc">self</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">articleText</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">intel</span> <span class="p">=</span> <span class="n">response</span><span class="p">.</span><span class="n">content</span>
</span></span></code></pre></div><p>Now your UI doesn’t have to scrape bullet points out of Markdown or hope the model remembered your JSON schema. You get a real type with real fields, and you can render it consistently. It produces <em>structured output</em> your app can actually use. And it&rsquo;s all running locally!</p>
<p>This isn’t just nicer ergonomics. It’s an engineering improvement.</p>
<p>And if you’re building a local first app, this is the difference between “AI as novelty” and “AI as a trustworthy subsystem”.</p>
<h2 id="but-local-models-arent-as-smart">
  <a href="#but-local-models-arent-as-smart" class="heading-anchor" aria-label="Link to this section">“But Local Models Aren’t As Smart”</a>
</h2>
<p>Correct.</p>
<p>But also so what?</p>
<p>Most app features don’t need a model that can write Shakespeare, explain quantum mechanics, and pass the bar exam. They need a model that can do one of these reliably: summarize, classify, extract, rewrite, or normalize.</p>
<p>And for those tasks, local models can be truly excellent.</p>
<p>If you try to use a local model as a replacement for the entire internet, you will be disappointed. If you use it as a “data transformer” sitting <em>inside</em> your app, you’ll wonder why you ever sent this stuff to a server.</p>
<p>Use cloud models only when they’re genuinely necessary. Keep the user’s data where it belongs. And when you <em>do</em> use AI, don’t just glue it as a chat box. Use it as a real subsystem with typed outputs and predictable behavior.</p>
<p>Stop shipping distributed systems when you meant to ship a feature.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>The Insecurity of Debian</title>
      <link>/posts/insecurity-of-debian/</link>
      <pubDate>Wed, 04 Sep 2024 00:00:00 +0000</pubDate>
      
      <guid>/posts/insecurity-of-debian/</guid>
      <description>The difference in security defaults between Debian and Red Hat.</description>
      <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/">&lt;![CDATA[<p>In June of 2023 Red Hat made a controversial decision to change how they distribute the source code behind Red Hat Enterprise Linux (RHEL). There have been a lot of keyboards tapped angrily across social media that left many uncertain about the ramifications of the decision. There were many questions about the future viability of downstream rebuilds of RHEL affecting distributions like Rocky Linux, AlmaLinux, Oracle Linux, and others. Each have since made announcements to try and calm their communities.</p>
<p>Still. Many in the open source community have interpreted Red Hat&rsquo;s decision for what it really was: A dick move.</p>
<p>There has been a steady uptick of people stating that they will migrate (or already have) to Debian &ndash; seeking refuge from what they see as greedy corporate influence. I understand the sentiment fully. However, there&rsquo;s a problem here that I want to talk about: security.</p>
<p>The ugly truth is that security is hard. It&rsquo;s tedious. Unpleasant. And requires a lot of work to get right.</p>
<p>Debian does not do enough here to protect users.</p>
<p>Long ago, Red Hat embraced the usage of <a href="https://en.wikipedia.org/wiki/Security-Enhanced_Linux" target="_blank" rel="noopener noreferrer">SELinux</a>
. And they took it beyond just enabling the feature in their kernel. They put in the arduous work of crafting default SELinux policies for their distribution.</p>
<p>These <a href="https://www.redhat.com/sysadmin/selinux-kata-containers" target="_blank" rel="noopener noreferrer">policies ship enabled by default</a>
 in their distribution. The policies help protect a variety of daemons that run by default on RHEL, as well as many of the most popular daemons folks tend to use on the server.</p>
<p>Apache, nginx, MariaDB, PostgreSQL, OpenSSH, etc. are all covered by SELinux policies that ship on RHEL distributions.</p>
<p>The protection even extends to containers. Containers are increasingly the preferred method for developers to deploy their software &ndash; myself included. A common misconception is that if you run something in a container, it&rsquo;s inherently secure. This is absolutely not true. Containers by themselves do not solve a security problem. They solve a software distribution problem. They give a false impression of security to those that run them.</p>
<p>On Red Hat based distributions, you can use a drop-in Docker alternative named podman which allows you to run containers without needing a daemon (unlike Docker) and it provides other benefits like being able to run fully root-less. But Red Hat takes it a step further here and applies strong default SELinux policies that separate the container from the host OS and even from other containers!</p>
<p>There have been numerous <a href="https://www.redhat.com/en/blog/latest-container-exploit-runc-can-be-blocked-selinux?intcmp=701f20000012ngPAAQ" target="_blank" rel="noopener noreferrer">examples</a>
 of being able to escape from a container and touch the host OS or other containers. This is where tools like SELinux step in. The application of SELinux policies on a container allows for a hardened sarcophagus to place your application in which mitigates the risk of unknown future exploits. And it’s nearly effortless to use on RHEL.</p>
<p>Red Hat was aware that unless they put in the work on these default policies, their users would simply not embrace the technology and millions of servers would remain vulnerable. Because let&rsquo;s be real here. SELinux is hard. The policy language and tooling is cumbersome, obtuse, and is about as appealing as filling out tax forms. It frankly sucks to use &ndash; if you are manually creating your own policies that is.</p>
<p>But due to the work Red Hat has put in, the usage of SELinux on RHEL is mostly transparent and provides real security benefits to their users.</p>
<h2 id="debians-approach">
  <a href="#debians-approach" class="heading-anchor" aria-label="Link to this section">Debian&rsquo;s Approach</a>
</h2>
<p>Debian, a stalwart of the open-source community, is revered for its stability and extensive software library. I am a fan and <a href="https://www.debian.org/donations" target="_blank" rel="noopener noreferrer">donate</a>
 to the project every year (you should too!) even though I don&rsquo;t run it in production environments.</p>
<p>However, its default security framework leaves much to be desired. Debian&rsquo;s decision to enable <a href="https://en.wikipedia.org/wiki/AppArmor" target="_blank" rel="noopener noreferrer">AppArmor</a>
 by default starting with version 10 signifies a positive step towards improved security, yet it falls short due to the half-baked implementation across the system.</p>
<p>Debian&rsquo;s reliance on AppArmor and its default configurations reveals a systemic issue with its approach to security. While AppArmor is capable of providing robust security when properly configured, Debian&rsquo;s out-of-the-box settings fail to leverage its full potential:</p>
<p><strong>Limited Default Profiles:</strong> Debian ships with a minimal set of AppArmor profiles, leaving many critical services unprotected.</p>
<p><strong>Reactive vs. Proactive Stance:</strong> Debian&rsquo;s security model often relies on users to implement stricter policies, rather than providing a secure-by-default configuration.</p>
<p><strong>Inconsistent Application:</strong> Unlike SELinux in Red Hat systems, which applies to the entire system consistently, AppArmor in Debian is applied piecemeal, leading to potential security gaps.</p>
<p><strong>Lack of Resources:</strong> Debian as a community-driven project lacks the resources to develop and maintain comprehensive security policies comparable to those provided by Red Hat.</p>
<p>It’s very common for folks to run container workloads on Debian via Docker &ndash; which <a href="https://docs.docker.com/engine/security/apparmor/" target="_blank" rel="noopener noreferrer">does automatically generate</a>
 and load a default AppArmor profile for containers named <code>docker-default</code>. Unfortunately, it’s not a very strong profile for security and is overly permissive.</p>
<p>This profile, while providing some protection, <a href="https://github.com/moby/moby/blob/master/profiles/apparmor/template.go" target="_blank" rel="noopener noreferrer">leaves significant attack surfaces</a>
 exposed. For instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl">  <span class="n">network</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">capability</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">file</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">umount</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Host (privileged) processes may send signals to container processes.</span>
</span></span><span class="line"><span class="cl">  <span class="k">signal</span> <span class="p">(</span><span class="n">receive</span><span class="p">)</span> <span class="n">peer</span><span class="o">=</span><span class="n">unconfined</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># runc may send signals to container processes (for &#34;docker stop&#34;).</span>
</span></span><span class="line"><span class="cl">  <span class="k">signal</span> <span class="p">(</span><span class="n">receive</span><span class="p">)</span> <span class="n">peer</span><span class="o">=</span><span class="n">runc</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># crun may send signals to container processes (for &#34;docker stop&#34; when used with crun OCI runtime).</span>
</span></span><span class="line"><span class="cl">  <span class="k">signal</span> <span class="p">(</span><span class="n">receive</span><span class="p">)</span> <span class="n">peer</span><span class="o">=</span><span class="n">crun</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># dockerd may send signals to container processes (for &#34;docker kill&#34;).</span>
</span></span><span class="line"><span class="cl">  <span class="k">signal</span> <span class="p">(</span><span class="n">receive</span><span class="p">)</span> <span class="n">peer</span><span class="o">=</span><span class="p">{{</span><span class="o">.</span><span class="n">DaemonProfile</span><span class="p">}},</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Container processes may send signals amongst themselves.</span>
</span></span><span class="line"><span class="cl">  <span class="k">signal</span> <span class="p">(</span><span class="n">send</span><span class="p">,</span><span class="n">receive</span><span class="p">)</span> <span class="n">peer</span><span class="o">=</span><span class="p">{{</span><span class="o">.</span><span class="n">Name</span><span class="p">}},</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="err">@</span><span class="p">{</span><span class="n">PROC</span><span class="p">}</span><span class="o">/*</span> <span class="n">w</span><span class="p">,</span>   <span class="c1"># deny write for all files directly in /proc (not in a subdir)</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># deny write to files not in /proc/&lt;number&gt;/** or /proc/sys/**</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="err">@</span><span class="p">{</span><span class="n">PROC</span><span class="p">}</span><span class="o">/</span><span class="p">{[</span><span class="o">^</span><span class="mi">1</span><span class="o">-</span><span class="mi">9</span><span class="p">],[</span><span class="o">^</span><span class="mi">1</span><span class="o">-</span><span class="mi">9</span><span class="p">][</span><span class="o">^</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="p">],[</span><span class="o">^</span><span class="mi">1</span><span class="o">-</span><span class="mi">9</span><span class="n">s</span><span class="p">][</span><span class="o">^</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="n">y</span><span class="p">][</span><span class="o">^</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="n">s</span><span class="p">],[</span><span class="o">^</span><span class="mi">1</span><span class="o">-</span><span class="mi">9</span><span class="p">][</span><span class="o">^</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="p">][</span><span class="o">^</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="p">][</span><span class="o">^</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">/</span><span class="p">]</span><span class="o">*</span><span class="p">}</span><span class="o">/**</span> <span class="n">w</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="err">@</span><span class="p">{</span><span class="n">PROC</span><span class="p">}</span><span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="p">[</span><span class="o">^</span><span class="n">k</span><span class="p">]</span><span class="o">**</span> <span class="n">w</span><span class="p">,</span>  <span class="c1"># deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="err">@</span><span class="p">{</span><span class="n">PROC</span><span class="p">}</span><span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">kernel</span><span class="o">/</span><span class="p">{</span><span class="err">?</span><span class="p">,</span><span class="err">??</span><span class="p">,[</span><span class="o">^</span><span class="n">s</span><span class="p">][</span><span class="o">^</span><span class="n">h</span><span class="p">][</span><span class="o">^</span><span class="n">m</span><span class="p">]</span><span class="o">**</span><span class="p">}</span> <span class="n">w</span><span class="p">,</span>  <span class="c1"># deny everything except shm* in /proc/sys/kernel/</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="err">@</span><span class="p">{</span><span class="n">PROC</span><span class="p">}</span><span class="o">/</span><span class="n">sysrq</span><span class="o">-</span><span class="n">trigger</span> <span class="n">rwklx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="err">@</span><span class="p">{</span><span class="n">PROC</span><span class="p">}</span><span class="o">/</span><span class="n">kcore</span> <span class="n">rwklx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="n">mount</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="p">[</span><span class="o">^</span><span class="n">f</span><span class="p">]</span><span class="o">*/**</span> <span class="n">wklx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">f</span><span class="p">[</span><span class="o">^</span><span class="n">s</span><span class="p">]</span><span class="o">*/**</span> <span class="n">wklx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">fs</span><span class="o">/</span><span class="p">[</span><span class="o">^</span><span class="n">c</span><span class="p">]</span><span class="o">*/**</span> <span class="n">wklx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">fs</span><span class="o">/</span><span class="n">c</span><span class="p">[</span><span class="o">^</span><span class="n">g</span><span class="p">]</span><span class="o">*/**</span> <span class="n">wklx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">fs</span><span class="o">/</span><span class="n">cg</span><span class="p">[</span><span class="o">^</span><span class="n">r</span><span class="p">]</span><span class="o">*/**</span> <span class="n">wklx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">firmware</span><span class="o">/**</span> <span class="n">rwklx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">devices</span><span class="o">/</span><span class="n">virtual</span><span class="o">/</span><span class="n">powercap</span><span class="o">/**</span> <span class="n">rwklx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="n">deny</span> <span class="o">/</span><span class="n">sys</span><span class="o">/</span><span class="n">kernel</span><span class="o">/</span><span class="n">security</span><span class="o">/**</span> <span class="n">rwklx</span><span class="p">,</span>
</span></span></code></pre></div><p>The <strong>network</strong> rule allows all network-related syscalls without restriction.</p>
<p>The <strong>capability</strong> rule, without specific denials, permits most capabilities by default.</p>
<p>The <strong>file</strong> rule grants broad file access permissions, relying on specific deny rules for protection.</p>
<h2 id="apparmor-vs-selinux">
  <a href="#apparmor-vs-selinux" class="heading-anchor" aria-label="Link to this section">AppArmor vs. SELinux</a>
</h2>
<p>The fundamental difference between AppArmor and SELinux lies in their approach to Mandatory Access Control (MAC). AppArmor operates on a path-based model, while SELinux employs a significantly more complex type enforcement system. This distinction becomes particularly evident in container environments.</p>
<p>SELinux applies a type to every object in the system - files, processes, ports, you name it. When you launch a container on a SELinux-enabled RHEL system, it&rsquo;s immediately assigned the <strong>container_t</strong> type &ndash; a strict access control mechanism. The <strong>container_t</strong> type effectively cordons off the container, preventing it from interacting with any object not explicitly labeled for container use.</p>
<p>But SELinux doesn&rsquo;t stop at type enforcement. It takes container isolation a step further with <a href="https://en.wikipedia.org/wiki/Multi_categories_security" target="_blank" rel="noopener noreferrer">Multi-Category Security (MCS)</a>
 labels. These labels function as an additional layer of segregation, ensuring that even containers of the same type (<strong>container_t</strong>) remain isolated from each other. Each container gets its own unique MCS label, creating what amounts to a private sandbox within the broader <strong>container_t</strong> environment.</p>
<p>AppArmor, in contrast, doesn&rsquo;t concern itself with types or categories. It focuses on limiting the capabilities of specific programs based on pre-defined profiles. These profiles specify which files a program can access and which operations it can perform. While this approach is more straightforward to implement and understand, it lacks the granularity and system-wide consistency of SELinux&rsquo;s type enforcement. Almost no mainstream Linux distribution distributes comprehensive AppArmor profiles for all common network-facing daemons by default.</p>
<p>The practical implications of these differences are significant. In a SELinux environment, a compromised container faces substantial hurdles in accessing or affecting the host system or other containers, thanks to the dual barriers of type enforcement and MCS labels.</p>
<p>This isn&rsquo;t to say one is universally superior to the other. SELinux offers more robust isolation but at the cost of increased complexity and potential performance overhead. AppArmor provides a simpler, more approachable security model that can still be quite effective when configured properly. The root of my point though is that Red Hat has put in the work here to make the use of SELinux and containers seamless and easy for its users. You aren’t left to fend for yourself.</p>
<p>In the end, the choice between Debian and Red Hat isn&rsquo;t just about corporate influence versus community-driven development. It&rsquo;s also a choice between a system that assumes the best and one that prepares for the worst. Unfortunately in today’s highly connected world, pessimism is a necessity.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>What&#39;s Wrong With Enterprise Linux</title>
      <link>/posts/enterprise-linux/</link>
      <pubDate>Mon, 17 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>/posts/enterprise-linux/</guid>
      <description>An examination of what is wrong with the Enterprise Linux model and a recommendation for a middle ground.</description>
      <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/">&lt;![CDATA[<p>The Enterprise Linux model works something like this:</p>
<p>It&rsquo;s decided to snapshot a collection of upstream open source projects at a specific version, including the Linux kernel, to form as the basis of a new cohesive version of an Enterprise Linux distribution. This collection of software will remain locked at its specific version throughout the lifespan of that Enterprise Linux distribution release &ndash; which is often 10 years or more.</p>
<p>The engineers, maintainers, and testers behind these distributions then put in the enormous amount of thankless work of bug fixes, tests, QA, documentation, etc. It&rsquo;s a massive undertaking of tedious painstaking work. At the end of the process, you end up with a relatively stable Linux distribution that works well and they release it to the world.</p>
<p>The next phase of hard work begins for that release. Keeping the large collection of software secured and as bug-free as possible as their upstream counterparts continue to churn and release new versions. The maintainers of your Enterprise Linux distribution must carefully keep track of the ocean of changes.</p>
<p>The process often looks like this:</p>
<figure>
    <img src="/images/wharrgarbl.jpg" loading="lazy" alt="WHARGARBL">
    
  </figure><p>In practice, what this works out to is they freeze packages at a specific version for a long time and only ever backport fixes for the most egregious of bugs or security issues that receive a CVE.</p>
<p>Sounds good, right? Nope. Not for security. There&rsquo;s a lot wrong with this model.</p>
<p>As <a href="https://arxiv.org/abs/2105.14565" target="_blank" rel="noopener noreferrer">research shows</a>
: &ldquo;Despite the National Vulnerability Database (NVD) publishes identified vulnerabilities, a vast majority of vulnerabilities and their corresponding security patches remain beyond public exposure&rdquo;.</p>
<p>Let&rsquo;s focus on the example of one of the largest ongoing open source projects: The Linux kernel. According to the 2020 Linux Kernel History Report, the Linux kernel project receives about 10 changes per hour, or approximately 225 changes per day. The team behind the kernel does not treat security bugs as something special. It&rsquo;s simply another bug. All bugs are equal in the eyes of the project. They do not designate a particular bugfix as security related in the commit logs. They do not publish advisories. In fact, the vast majority of CVEs found for the Linux kernel are only retroactively identified as security researchers and interested parties pore over the changes and realize that a few of those bugfixes are indeed closing security gaps. Only then do they file for a CVE for the issue.</p>
<p>Quite often, most identified CVEs were fixed months ago in the stable kernel release. That&rsquo;s not even thinking about the huge pile of unidentified CVEs lurking in the sea of bugfixes. Multiply this problem times the total number of packages that make up an Enterprise Linux distribution and you end up with a huge vulnerability window.</p>
<p>The fundamental issue is that the Enterprise Linux model optimizes for stability at the expense of security. By locking packages to specific versions and only backporting select fixes, these distributions lag significantly behind upstream versions when it comes to security bugfixes, albeit unintentionally.</p>
<p>Defenders will counter that this is a necessary tradeoff &ndash; you can&rsquo;t have both stability and being fully up-to-date on security fixes. I&rsquo;m not convinced that&rsquo;s true as there are other Linux distributions that have shown you can have both. Rolling release distributions like OpenSUSE Tumbleweed follow upstream much more closely while still maintaining stability through thorough <a href="https://openqa.opensuse.org/" target="_blank" rel="noopener noreferrer">automated testing</a>
. Additionally, distributions like Fedora Linux, while not technically a rolling release, do tend to hew close to upstream versions at a more regular cadence.</p>
<p>Even so, these type of distributions are not a panacea and operational challenges may present themselves with staying close to upstream given your application or team needs.</p>
<p>There does exist a middle ground, but you&rsquo;re probably not going to like hearing it.</p>
<h2 id="the-case-for-oracle-linux">
  <a href="#the-case-for-oracle-linux" class="heading-anchor" aria-label="Link to this section">The Case for Oracle Linux</a>
</h2>
<p>I need you to set aside your automatic (and justified) repulsion to what you just read. Yes, I know that Oracle has earned every bit of their reputation amongst engineers. I&rsquo;m totally with you on that &ndash; I am not affiliated with Oracle in any way.</p>
<p>But stop screaming for a moment and hear me out.</p>
<p>The Oracle Linux project was started in October 2006 as a downstream rebuild of RHEL. They simply took Red Hat&rsquo;s sources, recompiled it and put their branding on it, and sold their own support contracts for it in an attempt to undercut Red Hat&rsquo;s business.</p>
<p>Throughout the years, they&rsquo;ve continued this work and publish all their work on <a href="https://yum.oracle.com/" target="_blank" rel="noopener noreferrer">yum.oracle.com</a>
 which is freely available. As their very own site and explanation states in the <a href="https://linux.oracle.com/switch/centos/" target="_blank" rel="noopener noreferrer">FAQ</a>
:</p>
<p><em>Q. Wait, doesn&rsquo;t Oracle Linux cost money?</em></p>
<p><em>A. Oracle Linux support costs money. If you just want the software, it&rsquo;s 100% free. And it&rsquo;s all in our yum repo at yum.oracle.com. Major releases, errata, the whole shebang. Free source code, free binaries, free updates, freely redistributable, free for production use. Yes, we know that this is Oracle, but it&rsquo;s actually free. Seriously.</em></p>
<p>Where it got interesting was in September 2010 when Oracle <a href="https://www.theregister.com/2010/09/20/oracle_own_linux/" target="_blank" rel="noopener noreferrer">announced</a>
 one optional departure for their RHEL clone: the pompously named Unbreakable Enterprise Kernel.</p>
<p>Oracle was now offering everyone two kernels: The Red Hat Compatible Kernel (RHCK) and their own Unbreakable Enterprise Kernel (UEK).</p>
<p>Over time, UEK has evolved into a unique Enterprise Linux kernel offering that more closely tracks the upstream Linux kernel. Rather than cherry picking individual fixes to backport to their kernel the way most Enterprise Linux distributions do, the model they&rsquo;ve <a href="https://blogs.oracle.com/linux/post/tracking-linux-stable-kernels-with-uek" target="_blank" rel="noopener noreferrer">found works best</a>
 &ldquo;has been to lag behind the tip of the LTS tree by less than four weeks&rdquo;.</p>
<p>They use that small lag time to perform their typical &ldquo;enterprise&rdquo; validation and testing process before shipping an updated UEK release.</p>
<p>By sticking much closer to the upstream kernel releases at regular intervals, they are able to keep up with the enormous amount of bugfixes that go into the kernel &ndash; whether they were security related or not.</p>
<p>It&rsquo;s an interesting middle ground option for one of the more critical components of an Enterprise Linux distribution. You can rely on the operational stability of the overall distribution tooling, but still have a relatively updated (and validated) kernel.</p>
<p>Oracle has also publicly continued to <a href="https://www.oracle.com/news/announcement/blog/keep-linux-open-and-free-2023-07-10/" target="_blank" rel="noopener noreferrer">commit</a>
 to keeping &ldquo;&hellip;the binaries and source code for that distribution publicly and freely available&rdquo;. At this point their distribution has been around for 17 years, and they&rsquo;ve never tried to restrict access to their source code in that time.</p>
<p>It feels wrong to speak positively about an Oracle product, but Oracle Linux is actually a good option worthy of our consideration.</p>
<p>Just don&rsquo;t tell <a href="https://en.wikipedia.org/wiki/Larry_Ellison" target="_blank" rel="noopener noreferrer">Larry</a>
 I said that.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
