{"version":"https://jsonfeed.org/version/1","title":"Xe Iaso's blog","home_page_url":"https://preview.xeiaso.net/","feed_url":"https://preview.xeiaso.net/blog.json","description":"Thoughts and musings from Xe Iaso","items":[{"id":"https://preview.xeiaso.net/blog/2026/dancing-mad-sandboxing/","url":"https://preview.xeiaso.net/blog/2026/dancing-mad-sandboxing/","title":"Dancing mad with sandboxing","content_html":"<div class=\"flex flex-col space-y-0\"><div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Cadey is enby\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/cadey/enby\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#cadey\">Cadey</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>What is an operating system, really?</p></div></div></div><div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Aoi is wut\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/aoi/wut\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#aoi\">Aoi</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>I mean, isn't it obvious? It's something like FreeBSD or Fedora that has a\nkernel, userspace, graphics stack, core set of programs, and everything else\nyou need to be able to use a computer. Is this a trick question?</p></div></div></div><div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Numa is smug\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/numa/smug\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#numa\">Numa</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>Well it depends, is the Nintendo Switch OS an operating system? It doesn't\nhave a shell in the same way FreeBSD does. Is SEL4 an OS? It doesn't ship\nwith core utilities. Is Linux an OS? Is Windows an OS?</p></div></div></div><div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Aoi is facepalm\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/aoi/facepalm\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#aoi\">Aoi</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>Oh gods here we go again…</p></div></div></div></div>\n<p>The definition of an operating system gets really fuzzy when you start looking at the edges of it, but let's say that an operating system is any part of a computer system that doesn't involve pure math. When you print to the screen, render 3d graphics, connect to the internet, and write to files your code calls into the underlying system to do that work. These system calls are defined by your operating system and are exposed as functions*.</p>\n<div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Mara is hacker\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/mara/hacker\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#mara\">Mara</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>Okay they're not actually functions, but they quack enough like functions that\nyou can treat them like functions and not have to worry about the details too\nmuch.</p></div></div></div>\n<p>System calls are injected into each operating system process via a process kinda like how you inject dependencies into your applications for database sessions or object storage operations.</p>\n<h2>Bashing your head into the wall</h2>\n<p>A while ago a new JavaScript package got into the meme sphere at work: <a href=\"https://github.com/vercel-labs/just-bash/tree/main/packages/just-bash\">just-bash</a>. It's a sandboxed environment with a shell interpreter that was originally intended for use with AI agents after its author observed that AI agents know how to use a tool called <code>bash</code> a lot better than a tool called <code>search_documentation</code>. This is backed by a &quot;fake&quot; shell with &quot;fake&quot; core utilities (<code>cat</code>, <code>ls</code>, etc, hereinafter coreutils) so that when an agent decides to <code>rm -rf /</code>, nothing important actually leaves the room. One of my coworkers made <a href=\"https://www.npmjs.com/package/@tigrisdata/agent-shell\">@tigrisdata/agent-shell</a> on top of this that uses Tigris as its storage layer.</p>\n<p>This is great for people in the JavaScript ecosystem, but I am not mainly a JavaScript developer. I really wanted to play with it so I started thinking what it would take to have something like this in Go. mvdan's <a href=\"https://pkg.go.dev/mvdan.cc/sh/v3\">shell package</a> makes this a heck of a lot easier, meaning that this &quot;fake&quot; shell would be powered by a real shell instead of either porting half of bash to JavaScript or making up hopefully-compatible behaviour.</p>\n<p>After a bunch of thought, hacking, and a spot of vibe coding while I did some Dawntrail extreme mount farms, I ended up with <a href=\"https://tangled.org/xeiaso.net/kefka\">Kefka</a>, a &quot;fake&quot; shell with coreutils implementations that lets you put your programs in clown jail. This package lets you add a sandboxed-in-userspace shell to your existing projects without shelling out to the actual implementations of coreutils on your machine.</p>\n<div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Mara is hacker\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/mara/hacker\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#mara\">Mara</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>The name is inspired from <a href=\"https://finalfantasy.fandom.com/wiki/Kefka_Palazzo\">Kefka\nPalazzo</a>, the final boss\nof Final Fantasy VI. Need to chain uncontrollable demons? Use the power of a\nmad god driven to the brink of insanity with raw access to magic! What could\npossibly go wrong!</p></div></div></div>\n<h2>So I did that</h2>\n<p>So after some thought, I came up with this interface for the &quot;commands&quot; to use: <code>Execer</code>. This takes process context and passes it as an argument to a function named <code>Exec</code>. <code>Exec</code> then does whatever the process needs it to (list files, write to stdout, etc.) and returns an error if things went wrong and no error if things didn't.</p>\n<pre class=\"language-go\"><code class=\"language-go code-highlight\"><span class=\"code-line\"><span class=\"token keyword\">type</span> ExecContext <span class=\"token keyword\">struct</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">\tStdin          io<span class=\"token punctuation\">.</span>Reader\n</span><span class=\"code-line\">\tStdout<span class=\"token punctuation\">,</span> Stderr io<span class=\"token punctuation\">.</span>Writer\n</span><span class=\"code-line\">\tDir            <span class=\"token builtin\">string</span>\n</span><span class=\"code-line\">\tEnviron        expand<span class=\"token punctuation\">.</span>Environ\n</span><span class=\"code-line\">\tFS             billy<span class=\"token punctuation\">.</span>Filesystem\n</span><span class=\"code-line\">\t<span class=\"token comment\">// Runner is the active shell runner. Commands that need to dispatch a</span>\n</span><span class=\"code-line\">\t<span class=\"token comment\">// child command (for example, `time CMD`) should call Runner.Subshell()</span>\n</span><span class=\"code-line\">\t<span class=\"token comment\">// and re-enter the shell so the call goes through the same exec handler</span>\n</span><span class=\"code-line\">\t<span class=\"token comment\">// chain instead of poking at the registry directly. May be nil in</span>\n</span><span class=\"code-line\">\t<span class=\"token comment\">// embedders or tests that have not wired up a runner.</span>\n</span><span class=\"code-line\">\tRunner <span class=\"token operator\">*</span>interp<span class=\"token punctuation\">.</span>Runner\n</span><span class=\"code-line\"><span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\"><span class=\"token keyword\">type</span> Execer <span class=\"token keyword\">interface</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">\t<span class=\"token function\">Exec</span><span class=\"token punctuation\">(</span>ctx context<span class=\"token punctuation\">.</span>Context<span class=\"token punctuation\">,</span> ec <span class=\"token operator\">*</span>ExecContext<span class=\"token punctuation\">,</span> args <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token builtin\">error</span>\n</span><span class=\"code-line\"><span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>This is where I started vibe coding things, mostly via a <a href=\"https://tangled.org/xeiaso.net/kefka/blob/main/.claude/skills/just-bash-port/SKILL.md\">skill that ports a just-bash command to the Execer interface and filesystem in Go</a>. just-bash itself looks vibe coded from help output and manpages; I tried to go further and stay POSIX compatible, down to matching flag syntax (and in some cases output formats). If your muscle memory fails you, it's a bug in my book.</p>\n<div class=\"flex flex-col space-y-0\"><div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Aoi is wut\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/aoi/wut\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#aoi\">Aoi</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>If I recall, some POSIX utilities like <code>false</code> aren't usable as Go identifiers, how did you handle package names for that?</p></div></div></div><div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Cadey is aha\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/cadey/aha\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#cadey\">Cadey</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>By naming them things like <code>falsecmd</code>:</p><pre class=\"language-go\"><code class=\"language-go code-highlight\"><span class=\"code-line\"><span class=\"token keyword\">package</span> falsecmd\n</span><span class=\"code-line\">\n</span><span class=\"code-line\"><span class=\"token comment\">// ...</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\"><span class=\"token keyword\">type</span> Impl <span class=\"token keyword\">struct</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\"><span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>Impl<span class=\"token punctuation\">)</span> <span class=\"token function\">Exec</span><span class=\"token punctuation\">(</span>ctx context<span class=\"token punctuation\">.</span>Context<span class=\"token punctuation\">,</span> ec <span class=\"token operator\">*</span>command<span class=\"token punctuation\">.</span>ExecContext<span class=\"token punctuation\">,</span> args <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token builtin\">error</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">\t<span class=\"token keyword\">return</span> interp<span class=\"token punctuation\">.</span><span class=\"token function\">ExitStatus</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\"><span class=\"token punctuation\">}</span>\n</span></code></pre><p>Honestly the implementations of <code>true</code> and <code>false</code> are my favourite part of this implementation. Here's the implementation of <code>true</code>:</p><pre class=\"language-go\"><code class=\"language-go code-highlight\"><span class=\"code-line\"><span class=\"token keyword\">package</span> truecmd\n</span><span class=\"code-line\">\n</span><span class=\"code-line\"><span class=\"token comment\">// ...</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\"><span class=\"token keyword\">type</span> Impl <span class=\"token keyword\">struct</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\"><span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>Impl<span class=\"token punctuation\">)</span> <span class=\"token function\">Exec</span><span class=\"token punctuation\">(</span>ctx context<span class=\"token punctuation\">.</span>Context<span class=\"token punctuation\">,</span> ec <span class=\"token operator\">*</span>command<span class=\"token punctuation\">.</span>ExecContext<span class=\"token punctuation\">,</span> args <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token builtin\">error</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">\t<span class=\"token keyword\">return</span> <span class=\"token boolean\">nil</span>\n</span><span class=\"code-line\"><span class=\"token punctuation\">}</span>\n</span></code></pre><p>This is a fully POSIX compliant implementation of <code>true</code>! Here's the relevant part of the spec if you don't believe me:</p><blockquote>\n<p><code>true</code> - return true value</p>\n<p><strong>SYNOPSIS</strong></p>\n<p><code>true</code></p>\n<p><strong>OPTIONS</strong></p>\n<p>None.</p>\n<p><strong>OPERANDS</strong></p>\n<p>None.</p>\n</blockquote><p>Really, check out <a href=\"https://pubs.opengroup.org/onlinepubs/9799919799/utilities/true.html\">the POSIX spec for <code>true</code></a>. It's trivial to implement, here's a oneliner to implement it in Linux:</p><pre><code class=\"code-highlight\"><span class=\"code-line\">touch ./true &amp;&amp; chmod +x ./true\n</span></code></pre></div></div></div></div>\n<h3>I made an operating system*</h3>\n<p>This is basically an operating system: it provides interfaces for programs (well, in this case functions) to get input from a user, send output to a user, interact with a filesystem, and more. Eventually I want to add networking via a network stack on ExecContext, probably with <a href=\"https://pkg.go.dev/tailscale.com/tsnet\">tsnet</a> or <a href=\"https://pkg.go.dev/golang.zx2c4.com/wireguard/tun/netstack\">wireguard-go's netstack package</a> for the user-level side. Maybe there's room for adding <a href=\"https://cel.dev/\">CEL</a> based network filters there too.</p>\n<h2>Porting applications with WebAssembly</h2>\n<p>Once I got basic coreutils working, I thought it would be fun to get Python, jq, and ripgrep working. From <a href=\"https://tangled.org/xeiaso.net/x/tree/master/llm/codeinterpreter/python\">previous experimentation</a> <a href=\"https://xeiaso.net/blog/2024/strawberry/\">back in the strawberry era of AI</a>, I had already gotten Python running in WebAssembly via <a href=\"https://github.com/wazero/wazero\">wazero</a>. This used the stdlib <a href=\"https://pkg.go.dev/io/fs#FS\">io/fs#FS</a> interface to allow me to inject virtual filesystems into the WebAssembly context. I used this to isolate my chatbot's filesystem state so that it (hopefully) wasn't able to delete anything important by accident.</p>\n<p><code>io/fs#FS</code> has methods for the important stuff, and runtime interface assertions let you bridge the gap for things like writes. But it was really designed for <a href=\"https://pkg.go.dev/embed#hdr-File_Systems\">embedded filesystems</a>, and writes get hairy fast once you're talking to object storage or anything that isn't a tree of bytes on disk.</p>\n<p>At some point I hit a wall and had to switch from io/fs#FS to <a href=\"https://github.com/go-git/go-billy\">billy</a>, another filesystem interface that I think predates the standard library one. This gives you <a href=\"https://pkg.go.dev/github.com/go-git/go-billy/v5@v5.9.0#Filesystem\">a bunch more methods</a> that map a lot closer to filesystem semantics in ways that coreutils crave. The interface was also mostly compatible with io/fs#FS so most of the hard part was really changing out the type and then chasing down compiler errors until I found enough of a pattern to have Opus automate the rest of it.</p>\n<p>From there it was a matter of adapting billy's filesystem to wazero's <a href=\"https://pkg.go.dev/github.com/tetratelabs/wazero@v1.11.0/experimental/sys\">experimental sys interface</a>. Mostly glue code, except where I had to translate Go errors into POSIX errno values. I had to read both the POSIX spec, the WASI spec, and the wazero source to figure out how to map errors between the two worlds. I think I'm at least 95% correct, which is likely within the margin of porting error.</p>\n<p>Adapting that codeinterpreter/python library to the new interface was mostly straightforward, and I ended up with a flow like this:</p>\n<pre class=\"language-go\"><code class=\"language-go code-highlight\"><span class=\"code-line\"><span class=\"token comment\">// from https://tangled.org/xeiaso.net/kefka/blob/main/command/internal/python3/python3.go</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\"><span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>Impl<span class=\"token punctuation\">)</span> <span class=\"token function\">Exec</span><span class=\"token punctuation\">(</span>ctx context<span class=\"token punctuation\">.</span>Context<span class=\"token punctuation\">,</span> ec <span class=\"token operator\">*</span>command<span class=\"token punctuation\">.</span>ExecContext<span class=\"token punctuation\">,</span> args <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token builtin\">error</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">\tfsConfig <span class=\"token operator\">:=</span> wazero<span class=\"token punctuation\">.</span><span class=\"token function\">NewFSConfig</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>\n</span><span class=\"code-line\">\t\t<span class=\"token punctuation\">(</span>sysfs<span class=\"token punctuation\">.</span>FSConfig<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>\n</span><span class=\"code-line\">\t\t<span class=\"token function\">WithSysFSMount</span><span class=\"token punctuation\">(</span>billyfs<span class=\"token punctuation\">.</span><span class=\"token function\">New</span><span class=\"token punctuation\">(</span>ec<span class=\"token punctuation\">.</span>FS<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;/&quot;</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">\tconfig <span class=\"token operator\">:=</span> wazero<span class=\"token punctuation\">.</span><span class=\"token function\">NewModuleConfig</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>\n</span><span class=\"code-line\">\t\t<span class=\"token comment\">// Pipe ExecContext stdio</span>\n</span><span class=\"code-line\">\t\t<span class=\"token function\">WithStdin</span><span class=\"token punctuation\">(</span>ec<span class=\"token punctuation\">.</span>Stdin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">WithStdout</span><span class=\"token punctuation\">(</span>ec<span class=\"token punctuation\">.</span>Stdout<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">WithStderr</span><span class=\"token punctuation\">(</span>ec<span class=\"token punctuation\">.</span>Stderr<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>\n</span><span class=\"code-line\">\t\t<span class=\"token comment\">// Pipe argv</span>\n</span><span class=\"code-line\">\t\t<span class=\"token function\">WithArgs</span><span class=\"token punctuation\">(</span><span class=\"token function\">append</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">{</span><span class=\"token string\">&quot;python3&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> args<span class=\"token operator\">...</span><span class=\"token punctuation\">)</span><span class=\"token operator\">...</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>\n</span><span class=\"code-line\">\t\t<span class=\"token function\">WithName</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;python3&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>\n</span><span class=\"code-line\">\t\t<span class=\"token comment\">// Pipe filesystem</span>\n</span><span class=\"code-line\">\t\t<span class=\"token function\">WithFSConfig</span><span class=\"token punctuation\">(</span>fsConfig<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>\n</span><span class=\"code-line\">\t\t<span class=\"token comment\">// Pipe system time</span>\n</span><span class=\"code-line\">\t\t<span class=\"token function\">WithSysNanosleep</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">WithSysNanotime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">WithSysWalltime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">\tmod<span class=\"token punctuation\">,</span> err <span class=\"token operator\">:=</span> runtime<span class=\"token punctuation\">.</span><span class=\"token function\">InstantiateModule</span><span class=\"token punctuation\">(</span>ctx<span class=\"token punctuation\">,</span> compiled<span class=\"token punctuation\">,</span> config<span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">\t<span class=\"token keyword\">if</span> err <span class=\"token operator\">!=</span> <span class=\"token boolean\">nil</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">\t\t<span class=\"token comment\">// Fit the square peg into the round hole</span>\n</span><span class=\"code-line\">\t\t<span class=\"token keyword\">if</span> exitErr<span class=\"token punctuation\">,</span> ok <span class=\"token operator\">:=</span> errors<span class=\"token punctuation\">.</span>AsType<span class=\"token punctuation\">[</span><span class=\"token operator\">*</span>wsys<span class=\"token punctuation\">.</span>ExitError<span class=\"token punctuation\">]</span><span class=\"token punctuation\">(</span>err<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> ok <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">\t\t\t<span class=\"token keyword\">if</span> code <span class=\"token operator\">:=</span> exitErr<span class=\"token punctuation\">.</span><span class=\"token function\">ExitCode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> code <span class=\"token operator\">!=</span> <span class=\"token number\">0</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">\t\t\t\t<span class=\"token keyword\">return</span> interp<span class=\"token punctuation\">.</span><span class=\"token function\">ExitStatus</span><span class=\"token punctuation\">(</span><span class=\"token function\">uint8</span><span class=\"token punctuation\">(</span>code<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">\t\t\t<span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">\t\t\t<span class=\"token keyword\">return</span> <span class=\"token boolean\">nil</span>\n</span><span class=\"code-line\">\t\t<span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">\t\t<span class=\"token keyword\">return</span> err\n</span><span class=\"code-line\">\t<span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">\t<span class=\"token keyword\">return</span> mod<span class=\"token punctuation\">.</span><span class=\"token function\">Close</span><span class=\"token punctuation\">(</span>ctx<span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\"><span class=\"token punctuation\">}</span>\n</span></code></pre>\n<div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Mara is aha\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/mara/aha\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#mara\">Mara</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>See? The dependencies such as <code>stdin</code>, <code>stdout</code>, and <code>stderr</code> get <em>injected</em>\ninto the WebAssembly guest. Wazero also makes you inject the implementation of\ntime for boring reasons involving deterministic computing, but I'm sure you\ncan see the ways things hook in. This basic dependency injection flow is how\nthings like the <a href=\"https://wiki.freebsd.org/Linuxulator\">linuxulator</a> in FreeBSD\nor the old version of the Windows Subsystem for Linux work (WSL1 before it was\nmade into a Linux VM with WSL2). The table of system calls and filesystem\ncontext is effectively an argument to the process.</p></div></div></div>\n<p>Same trick got me ripgrep and jq. jq was annoying — wasi-sdk doesn't love jq's (ab)use of cmake — but 30 or so minutes of tweaking compiler flags got me a binary that works enough.</p>\n<p>I could see it being pretty easy to port over arbitrary programs to Kefka using WebAssembly like this. There's just one small problem: <a href=\"https://github.com/WebAssembly/WASI/tree/wasi-0.1\">WASI preview 0.1</a> doesn't allow you to open arbitrary network sockets. This has been a huge pain in practice (it means you can't do HTTP requests, database connections, or other common internet things from inside the WASM sandbox) and future work probably would include adapting wazero to use <a href=\"https://wasix.org/\">wasix</a> instead of WASI 0.1.</p>\n<h3>Using filesystems that don't exist</h3>\n<p>OK, that handles filesystems that (arguably) exist, like the btrfs volume on my dev box. What about filesystems that don't? For the sake of argument, let's say you want this fake shell to interact with object storage as its main filesystem. At some level all you need to do is adapt the billy interface to object storage using something like <a href=\"https://pkg.go.dev/github.com/tigrisdata/storage-go\">storage-go</a>.</p>\n<div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Cadey is coffee\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/cadey/coffee\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#cadey\">Cadey</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>Disclaimer, I work at Tigris and developed this library for them. It's\nbasically the S3 client with more methods to handle additional Tigris features\nlike forks and snapshots. I'll be writing more about it soon.</p></div></div></div>\n<p>After finding <a href=\"https://github.com/a-poor/s3fs\">a basic implementation of an S3 -> Billy adapter</a>, I vendored it into the Kefka repo and swapped out the &quot;real&quot; filesystem in cmd/kefka for an s3fs implementation pointed at a sample Tigris bucket. From there it was down to an iterative process of running commands, finding feature gaps when errors showed up, implementing them, fuzzing, and making sure things work mostly the same against Tigris as they do against a local filesystem.</p>\n<p>WASI is cursed: it has no process-level &quot;current working directory,&quot; which most programs assume exists. You patch around it by passing a <code>CWD</code> envvar, or just use absolute paths. I haven't hit anything broken in casual use, but expect rough edges. Here be dragons and this code may be known by the state of California to cause cancer.</p>\n<h2>Why does it have to use the command line?</h2>\n<p>Once everything got working with s3fs and a local shell, I wondered how hard it would be to make this work as an SSH server using the <a href=\"https://pkg.go.dev/github.com/gliderlabs/ssh@v0.3.8\">github.com/gliderlabs/ssh package</a>. Hooking things up was pretty easy:</p>\n<pre class=\"language-go\"><code class=\"language-go code-highlight\"><span class=\"code-line\"><span class=\"token keyword\">func</span> <span class=\"token function\">HandleSSH</span><span class=\"token punctuation\">(</span>sess ssh<span class=\"token punctuation\">.</span>Session<span class=\"token punctuation\">)</span> <span class=\"token builtin\">error</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">  <span class=\"token comment\">// Convenience variables for SSH session values</span>\n</span><span class=\"code-line\">  <span class=\"token keyword\">var</span> stdout io<span class=\"token punctuation\">.</span>Writer <span class=\"token operator\">=</span> sess\n</span><span class=\"code-line\">  <span class=\"token keyword\">var</span> stderr io<span class=\"token punctuation\">.</span>Writer <span class=\"token operator\">=</span> sess<span class=\"token punctuation\">.</span><span class=\"token function\">Stderr</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">  <span class=\"token keyword\">var</span> stdin  io<span class=\"token punctuation\">.</span>Reader <span class=\"token operator\">=</span> sess\n</span><span class=\"code-line\">  ctx <span class=\"token operator\">:=</span> sess<span class=\"token punctuation\">.</span><span class=\"token function\">Context</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// cancelled when the user disconnects</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">  <span class=\"token comment\">// Kefka command registry with coreutils/python/jq/etc</span>\n</span><span class=\"code-line\">  commands <span class=\"token operator\">:=</span> registry<span class=\"token punctuation\">.</span><span class=\"token function\">New</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">  coreutils<span class=\"token punctuation\">.</span><span class=\"token function\">Register</span><span class=\"token punctuation\">(</span>commands<span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">  wasmprog<span class=\"token punctuation\">.</span><span class=\"token function\">Register</span><span class=\"token punctuation\">(</span>commands<span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">  <span class=\"token comment\">// Base envvars for all programs, needed by POSIX</span>\n</span><span class=\"code-line\">  env <span class=\"token operator\">:=</span> expand<span class=\"token punctuation\">.</span><span class=\"token function\">ListEnviron</span><span class=\"token punctuation\">(</span>\n</span><span class=\"code-line\">    <span class=\"token string\">&quot;HOME=/&quot;</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    <span class=\"token string\">&quot;PWD=/&quot;</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    <span class=\"token string\">&quot;IFS=\\n&quot;</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    <span class=\"token string\">&quot;HOSTNAME=localhost&quot;</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    <span class=\"token string\">&quot;USER=&quot;</span><span class=\"token operator\">+</span>sess<span class=\"token punctuation\">.</span><span class=\"token function\">User</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// not strictly required, but just-bash sets it</span>\n</span><span class=\"code-line\">    <span class=\"token string\">&quot;MACHTYPE=x86_64-pc-linux-gnu&quot;</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">  <span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">  <span class=\"token comment\">// Create shell engine</span>\n</span><span class=\"code-line\">  sh<span class=\"token punctuation\">,</span> err <span class=\"token operator\">:=</span> interp<span class=\"token punctuation\">.</span><span class=\"token function\">New</span><span class=\"token punctuation\">(</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// Set the &quot;interactive&quot; flag so the shell expands aliases</span>\n</span><span class=\"code-line\">    interp<span class=\"token punctuation\">.</span><span class=\"token function\">Interactive</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// Forward our envvars</span>\n</span><span class=\"code-line\">    interp<span class=\"token punctuation\">.</span><span class=\"token function\">Env</span><span class=\"token punctuation\">(</span>env<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// Wire up stdio</span>\n</span><span class=\"code-line\">    interp<span class=\"token punctuation\">.</span><span class=\"token function\">StdIO</span><span class=\"token punctuation\">(</span>stdin<span class=\"token punctuation\">,</span> stdout<span class=\"token punctuation\">,</span> stderr<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// Change the shell exec handler such that it's constrained to the</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// Kefka registry.</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">//</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// Strictly speaking you don't have to do this, but if you don't</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// then any time the registry doesn't have a command</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// implementation, interp falls back to its default ExecHandler that</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// executes the command as a subprocess. This is almost certainly</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// not what you want.</span>\n</span><span class=\"code-line\">    interp<span class=\"token punctuation\">.</span><span class=\"token function\">ExecHandlers</span><span class=\"token punctuation\">(</span><span class=\"token function\">constrainToRegistry</span><span class=\"token punctuation\">(</span>commands<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// Wire up per-command pwd state to the filesystem implementation</span>\n</span><span class=\"code-line\">    interp<span class=\"token punctuation\">.</span><span class=\"token function\">CallHandler</span><span class=\"token punctuation\">(</span>billysh<span class=\"token punctuation\">.</span><span class=\"token function\">CallHandler</span><span class=\"token punctuation\">(</span>commands<span class=\"token punctuation\">,</span> fsys<span class=\"token punctuation\">,</span> stdout<span class=\"token punctuation\">,</span> stderr<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    <span class=\"token comment\">// Handle shell-level filesystem I/O (redirects, glob expansion, etc)</span>\n</span><span class=\"code-line\">    interp<span class=\"token punctuation\">.</span><span class=\"token function\">StatHandler</span><span class=\"token punctuation\">(</span>billysh<span class=\"token punctuation\">.</span><span class=\"token function\">FsysStatHandler</span><span class=\"token punctuation\">(</span>commands<span class=\"token punctuation\">,</span> fsys<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    interp<span class=\"token punctuation\">.</span><span class=\"token function\">FsysOpenHandler</span><span class=\"token punctuation\">(</span>billysh<span class=\"token punctuation\">.</span><span class=\"token function\">FsysOpenHandler</span><span class=\"token punctuation\">(</span>commands<span class=\"token punctuation\">,</span> fsys<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">    interp<span class=\"token punctuation\">.</span><span class=\"token function\">ReadDirHandler2</span><span class=\"token punctuation\">(</span>billysh<span class=\"token punctuation\">.</span><span class=\"token function\">FsysReadDirHandler</span><span class=\"token punctuation\">(</span>commands<span class=\"token punctuation\">,</span> fsys<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"code-line\">  <span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">  <span class=\"token comment\">// Read shell commands</span>\n</span><span class=\"code-line\">  parser <span class=\"token operator\">:=</span> syntax<span class=\"token punctuation\">.</span><span class=\"token function\">NewParser</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">  fmt<span class=\"token punctuation\">.</span><span class=\"token function\">Fprintf</span><span class=\"token punctuation\">(</span>stdout<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;$ &quot;</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">  <span class=\"token comment\">// Split input into commands</span>\n</span><span class=\"code-line\">  <span class=\"token keyword\">for</span> stmts<span class=\"token punctuation\">,</span> err <span class=\"token operator\">:=</span> <span class=\"token keyword\">range</span> parser<span class=\"token punctuation\">.</span><span class=\"token function\">InteractiveSeq</span><span class=\"token punctuation\">(</span>stdin<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">    <span class=\"token keyword\">if</span> err <span class=\"token operator\">!=</span> <span class=\"token boolean\">nil</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">      <span class=\"token keyword\">return</span> err\n</span><span class=\"code-line\">    <span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">    <span class=\"token keyword\">if</span> parser<span class=\"token punctuation\">.</span><span class=\"token function\">Incomplete</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">      fmt<span class=\"token punctuation\">.</span><span class=\"token function\">Fprintf</span><span class=\"token punctuation\">(</span>stdout<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;> &quot;</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">      <span class=\"token keyword\">continue</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">    <span class=\"token keyword\">for</span> <span class=\"token boolean\">_</span><span class=\"token punctuation\">,</span> stmt <span class=\"token operator\">:=</span> <span class=\"token keyword\">range</span> stmts <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">      err <span class=\"token operator\">:=</span> sh<span class=\"token punctuation\">.</span><span class=\"token function\">Run</span><span class=\"token punctuation\">(</span>ctx<span class=\"token punctuation\">,</span> stmt<span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">      <span class=\"token keyword\">if</span> sh<span class=\"token punctuation\">.</span><span class=\"token function\">Exited</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"code-line\">        <span class=\"token keyword\">return</span> err\n</span><span class=\"code-line\">      <span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">    <span class=\"token comment\">// Show prompt</span>\n</span><span class=\"code-line\">    fmt<span class=\"token punctuation\">.</span><span class=\"token function\">Fprintf</span><span class=\"token punctuation\">(</span>stdout<span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;$ &quot;</span><span class=\"token punctuation\">)</span>\n</span><span class=\"code-line\">  <span class=\"token punctuation\">}</span>\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">  <span class=\"token keyword\">return</span> <span class=\"token boolean\">nil</span>\n</span><span class=\"code-line\"><span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p><a href=\"https://tangled.org/xeiaso.net/kefka/blob/main/cmd/sophia/main.go#L87\">The real handler</a> is much messier because Python's REPL needs careful buffering, Ctrl-C has to actually cancel things, and <a href=\"https://en.wikipedia.org/wiki/Pseudoterminal\">pty</a> wiring is its own can of cans of worms. None of that shows up if it's working. Tab completion and readline polish are easy enough; I'll let you wire those up as an exercise for the reader.</p>\n<p>If you want to try it today, you can ssh into <code>sophia.xeiaso.net</code>:</p>\n<pre><code class=\"code-highlight\"><span class=\"code-line\">$ ssh sophia.xeiaso.net\n</span></code></pre>\n<p>You'll get an isolated sandbox in your own <a href=\"https://www.tigrisdata.com/docs/forks/\">bucket fork/branch</a>. Every <code>ls</code> is a <code>ListObjectsV2</code> against the bucket. Every <code>qjs</code> or <code>python3</code> runs WebAssembly on the server, wired to that same bucket.</p>\n<pre><code class=\"code-highlight\"><span class=\"code-line\">$ cat ./samples/hello.js\n</span><span class=\"code-line\">console.log(&quot;Hello, world!&quot;);\n</span><span class=\"code-line\">$ qjs ./samples/hello.js\n</span><span class=\"code-line\">Hello, world!\n</span></code></pre>\n<p>The demo bucket is seeded with examples. You'll probably have to poke around to find everything. Worst case, run <code>help</code>.</p>\n<div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Cadey is coffee\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/cadey/coffee\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#cadey\">Cadey</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>I should really hook up session recording to this.</p></div></div></div>\n<p>I want more experimental WebAssembly hacks like this to exist. I'll keep poking at it.</p>\n<h2>Put your programs in clown jail</h2>\n<p>With some effort, <a href=\"https://github.com/TecharoHQ/yeet\">yeet</a> could use Kefka's shell utilities to run <a href=\"https://anubis.techaro.lol/\">Anubis</a> builds on Windows; and if management ever makes you babysit AI agents, clown jail is a decent answer.</p>\n<p>The code lives <a href=\"https://tangled.org/xeiaso.net/kefka\">on Tangled</a>. I'm wiring it into <a href=\"https://github.com/tigrisdata-community/mithras\">an agent harness</a> so I can automate small tools against a local model (I'm loving Qwen3-36B-A3B).</p>\n<p>There's a sister post on the Tigris blog that goes deeper into the AI-agent angle and the porting work using Claude Code. If you want, you can check it out here:</p>\n<a href=\"https://tigrisdata.com/blog/agent-sandbox-go\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"group not-prose no-underline visited:no-underline flex flex-col sm:flex-row max-w-xl mx-auto my-8 overflow-hidden rounded-xl border border-bg-3 dark:border-bgDark-3 bg-bg-0 dark:bg-bgDark-0 shadow-sm transition duration-150 hover:-translate-y-0.5 hover:shadow-md\"><div class=\"sm:w-52 sm:shrink-0 aspect-[1.91/1] sm:aspect-auto overflow-hidden bg-bg-2 dark:bg-bgDark-2\"><img src=\"https://files.xeiaso.net/blog/tigris/mad-god-with-robots.webp\" alt loading=\"lazy\" class=\"h-full w-full object-cover transition-transform duration-200 group-hover:scale-105\"/></div><div class=\"flex flex-col justify-center gap-1 p-4\"><span class=\"font-mono text-xs uppercase tracking-wide text-orange-light dark:text-orangeDark-light group-hover:text-bg-0 dark:group-hover:text-white\">Tigris Data</span><span class=\"font-serif text-lg font-semibold leading-snug text-fg-0 dark:text-fgDark-0 group-hover:text-bg-0 dark:group-hover:text-white\">Give your agents disposable environments in Go</span><span class=\"text-sm leading-snug text-fg-3 dark:text-fgDark-3 line-clamp-3 group-hover:text-bg-1 dark:group-hover:text-bg-1\">Kefka is a userspace shell sandbox in Go that gives every AI agent its own copy-on-write Tigris bucket fork plus Python, jq, and ripgrep via WebAssembly.</span></div></a>","date_published":"2026-05-28T00:00:00.000Z","tags":[]},{"id":"https://preview.xeiaso.net/shitposts/no-way-to-prevent-this/CVE-2026-45250/","url":"https://preview.xeiaso.net/shitposts/no-way-to-prevent-this/CVE-2026-45250/","title":"\"No way to prevent this\" say users of only language where this regularly happens","content_html":"<p>In the hours following the release of <a href=\"https://www.freebsd.org/security/advisories/FreeBSD-SA-26:18.setcred.asc\">CVE-2026-45250</a> for the project <a href=\"https://www.freebsd.org/\">FreeBSD</a>, site reliability workers\nand systems administrators scrambled to desperately rebuild and patch all their systems to fix a kernel stack overflow when validating permissions of the setcred(2) system call, allowing arbitrary code execution in the context of the kernel. This is due to the affected components being\nwritten in C, the only programming language where these vulnerabilities regularly happen. &quot;This was a terrible tragedy, but sometimes\nthese things just happen and there's nothing anyone can do to stop them,&quot; said programmer Mrs. Gregoria Doyle, echoing statements\nexpressed by hundreds of thousands of programmers who use the only language where 90% of the world's memory safety vulnerabilities have\noccurred in the last 50 years, and whose projects are 20 times more likely to have security vulnerabilities. &quot;It's a shame, but what can\nwe do? There really isn't anything we can do to prevent memory safety vulnerabilities from happening if the programmer doesn't want to\nwrite their code in a robust manner.&quot; At press time, users of the only programming language in the world where these vulnerabilities\nregularly happen once or twice per quarter for the last eight years were referring to themselves and their situation as &quot;helpless.&quot;</p>\n","date_published":"2026-05-21T00:00:00.000Z","tags":[]},{"id":"https://preview.xeiaso.net/shitposts/no-way-to-prevent-this/CVE-2026-45584/","url":"https://preview.xeiaso.net/shitposts/no-way-to-prevent-this/CVE-2026-45584/","title":"\"No way to prevent this\" say users of only language where this regularly happens","content_html":"<p>In the hours following the release of <a href=\"https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-45584\">CVE-2026-45584</a> for the project <a href=\"https://www.microsoft.com/en-ca/windows/\">Microsoft Windows</a>, site reliability workers\nand systems administrators scrambled to desperately rebuild and patch all their systems to fix a memory safety vulnerability resulting in arbitrary code execution inside the virus scanner Windows Defender. This is due to the affected components being\nwritten in C++, the only programming language where these vulnerabilities regularly happen. &quot;This was a terrible tragedy, but sometimes\nthese things just happen and there's nothing anyone can do to stop them,&quot; said programmer Dr. Annabelle Connelly, echoing statements\nexpressed by hundreds of thousands of programmers who use the only language where 90% of the world's memory safety vulnerabilities have\noccurred in the last 50 years, and whose projects are 20 times more likely to have security vulnerabilities. &quot;It's a shame, but what can\nwe do? There really isn't anything we can do to prevent memory safety vulnerabilities from happening if the programmer doesn't want to\nwrite their code in a robust manner.&quot; At press time, users of the only programming language in the world where these vulnerabilities\nregularly happen once or twice per quarter for the last eight years were referring to themselves and their situation as &quot;helpless.&quot;</p>\n","date_published":"2026-05-20T00:00:00.000Z","tags":[]},{"id":"https://preview.xeiaso.net/blog/2026/tekton/","url":"https://preview.xeiaso.net/blog/2026/tekton/","title":"Slowly going mad with power using Tekton","content_html":"<ul>\n<li>I'm not feeling good about the future of GitHub\n<ul>\n<li><a href=\"https://red-squares.cian.lol/\">https://red-squares.cian.lol/</a></li>\n<li>I have a bunch of repos I'd like to migrate off of GitHub</li>\n</ul>\n</li>\n</ul>\n<p>GitHub has been the foundation of my career since before it began. I've been using it <a href=\"https://api.github.com/users/Xe\">since 2010</a> (almost half my life, wow) and I have over 400 repos there from all stages of competence and my career. I'm not feeling good about its future given how <a href=\"https://red-squares.cian.lol/\">unreliable it's been lately</a>.</p>\n<ul>\n<li>One small problem: all my CI flows assume I'm using GitHub Actions\n<ul>\n<li>I mean this is reasonable, I've used GitHub Actions for most of my career</li>\n<li>All my GitHub Actions flows only really work on GitHub</li>\n<li>I've moved some of them off of GitHub in the past, but it requires some significant modification and hacking because my actions really rely on GitHub platform features</li>\n</ul>\n</li>\n</ul>\n<p>As part of the planning for a post-GitHub future, I've been looking at one of the hardest things to move: <a href=\"https://docs.github.com/en/actions\">GitHub Actions</a>. I've used GitHub Actions for most of my career and only have vague memories of Jenkins, Travis, and the other things I've used before. Most of my repos only have GitHub Actions (with a small fraction of them having Gitea Actions from when I experimented with that) and a lot of my assumptions around how CI works are centered around the implementation of GitHub actions.</p>\n<div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Mara is hacker\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/mara/hacker\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#mara\">Mara</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>CI/CD (Continuous Integration / Continuous Delivery) is a software practice\nthat makes servers run tests and deploy code automatically as changes are\ncommitted. In practice, this means that you have something running tests and\ndeploying software when changes are committed. The group of build/test/deploy\nsteps is usually called a pipeline.</p></div></div></div>\n<p>The other thing I'm gonna have a hard time getting over is all the basically free compute that GitHub Actions provides, which is <a href=\"https://github.com/TecharoHQ/anubis/commit/b57508afcde956b098cadffdfa18df01b68d5f61\">significant when you look at how much Anubis does per commit</a>. However, I do have <a href=\"https://xeiaso.net/talks/2025/surreal-joy-homelab/\">a mildly absurd amount of compute laying around</a>, so that may just even itself out I guess.</p>\n<p>If I have to move my CI to a new solution that's going to require me to rewrite it all from scratch or adapt it over, I really want to settle on a vendor-neutral implementation that isn't beholden to a single platform. This would make me slightly more resilient against future enshittification (it's a matter of <em>when</em>, not <em>if</em>).</p>\n<div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Mara is hacker\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/mara/hacker\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#mara\">Mara</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>In this article, KubernetesTerms will be in JavaClassNameCase. If you're not sure what one of them is, search this in <a href=\"https://ddg.gg/\">DuckDuckGo</a>:</p><pre><code class=\"code-highlight\"><span class=\"code-line\">site:kubernetes.io KubernetesTerm\n</span></code></pre></div></div></div>\n<h2>Tekton and you</h2>\n<ul>\n<li>Recently I discovered <a href=\"https://tekton.dev/\">Tekton</a>\n<ul>\n<li>Tekton is a kubernetes operator that lets you create CI/CD systems on top of Kubernetes clusters</li>\n</ul>\n</li>\n</ul>\n<p>Recently I discovered <a href=\"https://tekton.dev/\">Tekton</a>, a Kubernetes operator (read: plugin) that breaks CI/CD pipelines into Kubernetes objects. It's also as far as I can tell the only real vendor-neutral CI/CD pipeline solution. There's also a really neat bridge between <a href=\"https://tangled.org/\">Tangled.org</a> and Tekton called <a href=\"https://tangled.org/mitchellh.com/tack\">Tack</a>.</p>\n<p>For the sake of this post, let's say that I'm going to settle on the TTT stack: Tangled, Tack, and Tekton.</p>\n<h3>Tekton in a nutshell</h3>\n<p>In order to help you understand where/why Tekton fits into the stack, let's think about it like this: Tekton is the layer <em>under</em> something like GitHub Actions. Tekton takes CI pipeline jobs and schedules them onto Kubernetes. Kubernetes handles scheduling pods across machines, durable storage between steps in the pipeline, networking between pods, logging, and other orchestration that you need in practice.</p>\n<p>In Tekton, your CI/CD jobs are broken out into a few basic categories:</p>\n<ul>\n<li>Tasks: Individual things to do (clone repo, run <code>go test</code>, build docker image, etc.)</li>\n<li>Pipelines: Sequences of Tasks (think your entire CI pipeline of cloning the repo, running <code>go test</code>, and building the docker image)</li>\n</ul>\n<p>Here's an example Task:</p>\n<pre class=\"language-yaml\"><code class=\"language-yaml code-highlight\"><span class=\"code-line\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\">:</span> tekton.dev/v1\n</span><span class=\"code-line\"><span class=\"token key atrule\">kind</span><span class=\"token punctuation\">:</span> Task\n</span><span class=\"code-line\"><span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> kubectl<span class=\"token punctuation\">-</span>apply\n</span><span class=\"code-line\">  <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token key atrule\">app.kubernetes.io/version</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;0.1&quot;</span>\n</span><span class=\"code-line\"><span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">|</span><span class=\"token scalar string\">\n</span></span><span class=\"code-line\"><span class=\"token scalar string\">    Run kubectl apply</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">params</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> image\n</span><span class=\"code-line\">      <span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> Docker image for kubectl\n</span><span class=\"code-line\">      <span class=\"token key atrule\">default</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;cgr.dev/chainguard/kubectl:latest&quot;</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> args\n</span><span class=\"code-line\">      <span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> Args to pass to `kubectl apply`\n</span><span class=\"code-line\">      <span class=\"token key atrule\">type</span><span class=\"token punctuation\">:</span> array\n</span><span class=\"code-line\">      <span class=\"token key atrule\">default</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> subfolder\n</span><span class=\"code-line\">      <span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> subfolder that the repo is actually in\n</span><span class=\"code-line\">      <span class=\"token key atrule\">default</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;/repo&quot;</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">workspaces</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> repo\n</span><span class=\"code-line\">      <span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> Repo to operate in\n</span><span class=\"code-line\">  <span class=\"token key atrule\">steps</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> chown<span class=\"token punctuation\">-</span>chmod\n</span><span class=\"code-line\">      <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> $(params.image)\n</span><span class=\"code-line\">      <span class=\"token key atrule\">workingDir</span><span class=\"token punctuation\">:</span> $(workspaces.source.path)$(params.subfolder)\n</span><span class=\"code-line\">      <span class=\"token key atrule\">args</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        <span class=\"token punctuation\">-</span> apply\n</span><span class=\"code-line\">        <span class=\"token punctuation\">-</span> $(params.args<span class=\"token punctuation\">[</span>*<span class=\"token punctuation\">]</span>)\n</span><span class=\"code-line\">      <span class=\"token key atrule\">securityContext</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        <span class=\"token key atrule\">runAsUser</span><span class=\"token punctuation\">:</span> <span class=\"token number\">0</span>\n</span></code></pre>\n<p>This looks like a lot, but that's mostly because Tekton is <em>completely unopinionated</em>. It places a lot of the assumptions that GitHub Actions or Travis makes out of the way so you build up your desired pipeline <em>from scratch</em>. I copied this Task out of my <a href=\"https://tangled.org/xeiaso.net/tekton-tasks\">Tekton tasks repo</a> in case you want to dig through the other ones I use.</p>\n<p>Those Tasks are then chained together into Pipelines like this:</p>\n<pre class=\"language-yaml\"><code class=\"language-yaml code-highlight\"><span class=\"code-line\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\">:</span> tekton.dev/v1beta1\n</span><span class=\"code-line\"><span class=\"token key atrule\">kind</span><span class=\"token punctuation\">:</span> Pipeline\n</span><span class=\"code-line\"><span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> xeiaso<span class=\"token punctuation\">-</span>net<span class=\"token punctuation\">-</span>alrest<span class=\"token punctuation\">-</span>autoapply\n</span><span class=\"code-line\">  <span class=\"token key atrule\">namespace</span><span class=\"token punctuation\">:</span> ci\n</span><span class=\"code-line\"><span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">|</span><span class=\"token scalar string\">\n</span></span><span class=\"code-line\"><span class=\"token scalar string\">    Autoapply for xeiaso.net/alrest manifests.</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">params</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> repo<span class=\"token punctuation\">-</span>url\n</span><span class=\"code-line\">      <span class=\"token key atrule\">type</span><span class=\"token punctuation\">:</span> string\n</span><span class=\"code-line\">      <span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Git repo to clone&quot;</span>\n</span><span class=\"code-line\">      <span class=\"token key atrule\">default</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;https://tangled.org/xeiaso.net/alrest&quot;</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;commit&quot;</span>\n</span><span class=\"code-line\">      <span class=\"token key atrule\">type</span><span class=\"token punctuation\">:</span> string\n</span><span class=\"code-line\">      <span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Git revision to check out&quot;</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">workspaces</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> repo\n</span><span class=\"code-line\">      <span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">|</span><span class=\"token scalar string\">\n</span></span><span class=\"code-line\"><span class=\"token scalar string\">        Cloned repo files.</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">tasks</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> fix<span class=\"token punctuation\">-</span>permissions\n</span><span class=\"code-line\">      <span class=\"token key atrule\">taskRef</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> fix<span class=\"token punctuation\">-</span>permissions\n</span><span class=\"code-line\">      <span class=\"token key atrule\">workspaces</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> dir\n</span><span class=\"code-line\">          <span class=\"token key atrule\">workspace</span><span class=\"token punctuation\">:</span> repo\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> clone<span class=\"token punctuation\">-</span>repo\n</span><span class=\"code-line\">      <span class=\"token key atrule\">runAfter</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;fix-permissions&quot;</span><span class=\"token punctuation\">]</span>\n</span><span class=\"code-line\">      <span class=\"token key atrule\">taskRef</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> git<span class=\"token punctuation\">-</span>clone\n</span><span class=\"code-line\">      <span class=\"token key atrule\">workspaces</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> output\n</span><span class=\"code-line\">          <span class=\"token key atrule\">workspace</span><span class=\"token punctuation\">:</span> repo\n</span><span class=\"code-line\">      <span class=\"token key atrule\">params</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> url\n</span><span class=\"code-line\">          <span class=\"token key atrule\">value</span><span class=\"token punctuation\">:</span> $(params.repo<span class=\"token punctuation\">-</span>url)\n</span><span class=\"code-line\">        <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> revision\n</span><span class=\"code-line\">          <span class=\"token key atrule\">value</span><span class=\"token punctuation\">:</span> $(params.commit)\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> kubectl<span class=\"token punctuation\">-</span>apply\n</span><span class=\"code-line\">      <span class=\"token key atrule\">runAfter</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;clone-repo&quot;</span><span class=\"token punctuation\">]</span>\n</span><span class=\"code-line\">      <span class=\"token key atrule\">taskSpec</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        <span class=\"token key atrule\">workspaces</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">          <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> repo\n</span><span class=\"code-line\">        <span class=\"token key atrule\">steps</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">          <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> do<span class=\"token punctuation\">-</span>apply\n</span><span class=\"code-line\">            <span class=\"token key atrule\">workingDir</span><span class=\"token punctuation\">:</span> $(workspaces.repo.path)/repo\n</span><span class=\"code-line\">            <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> cgr.dev/chainguard/kubectl<span class=\"token punctuation\">:</span>latest\n</span><span class=\"code-line\">            <span class=\"token key atrule\">args</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">              <span class=\"token punctuation\">-</span> apply\n</span><span class=\"code-line\">              <span class=\"token punctuation\">-</span> <span class=\"token punctuation\">-</span>k\n</span><span class=\"code-line\">              <span class=\"token punctuation\">-</span> $(workspaces.repo.path)/repo\n</span></code></pre>\n<p>You would then invoke this Pipeline by creating a PipelineRun object:</p>\n<pre class=\"language-yaml\"><code class=\"language-yaml code-highlight\"><span class=\"code-line\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\">:</span> tekton.dev/v1\n</span><span class=\"code-line\"><span class=\"token key atrule\">kind</span><span class=\"token punctuation\">:</span> PipelineRun\n</span><span class=\"code-line\"><span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> tack<span class=\"token punctuation\">-</span>autoapply<span class=\"token punctuation\">-</span>yaml<span class=\"token punctuation\">-</span>7c5e518b6c60\n</span><span class=\"code-line\">  <span class=\"token key atrule\">namespace</span><span class=\"token punctuation\">:</span> ci\n</span><span class=\"code-line\"><span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">  <span class=\"token key atrule\">params</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> commit\n</span><span class=\"code-line\">      <span class=\"token key atrule\">value</span><span class=\"token punctuation\">:</span> 838e7e0e83df04d54c035b9c60b852ff56852b4a\n</span><span class=\"code-line\">  <span class=\"token key atrule\">pipelineRef</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> xeiaso<span class=\"token punctuation\">-</span>net<span class=\"token punctuation\">-</span>alrest<span class=\"token punctuation\">-</span>autoapply\n</span><span class=\"code-line\">  <span class=\"token key atrule\">taskRunTemplate</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token key atrule\">serviceAccountName</span><span class=\"token punctuation\">:</span> xeiaso<span class=\"token punctuation\">-</span>net<span class=\"token punctuation\">-</span>alrest<span class=\"token punctuation\">-</span>autoapply\n</span><span class=\"code-line\">  <span class=\"token key atrule\">timeouts</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token key atrule\">pipeline</span><span class=\"token punctuation\">:</span> 1h0m0s\n</span><span class=\"code-line\">  <span class=\"token key atrule\">workspaces</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> repo\n</span><span class=\"code-line\">      <span class=\"token key atrule\">volumeClaimTemplate</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">        <span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">          <span class=\"token key atrule\">accessModes</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">            <span class=\"token punctuation\">-</span> ReadWriteOnce\n</span><span class=\"code-line\">          <span class=\"token key atrule\">resources</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">            <span class=\"token key atrule\">requests</span><span class=\"token punctuation\">:</span>\n</span><span class=\"code-line\">              <span class=\"token key atrule\">storage</span><span class=\"token punctuation\">:</span> 4Gi\n</span></code></pre>\n<p>You can see how this fits together, right? PipelineRuns bind values to Pipelines, which bind values to Tasks, which sequence running Pods in your cluster. This then gets turned into green and red commit statuses in the Tangled UI via Tack.</p>\n<h2>Walking through a single pipeline</h2>\n<p>From here I think it'd be useful to walk through a single CI pipeline, the one I use in <a href=\"https://tangled.org/xeiaso.net/kefka\">kefka</a> (a work-in-progress port of <a href=\"https://github.com/vercel-labs/just-bash\">just-bash</a> to Go, I plan to write more about it soon, I just need the energy/time). I had to make some compromises in my cluster because of how my storage system works, but you'll see more when it's time.</p>\n<ul>\n<li>A simple pipeline from Kefka\n<ul>\n<li>Fix permissions on the PVC with repo clone\n<ul>\n<li>Why?</li>\n</ul>\n</li>\n<li>Clone repo</li>\n<li>Go tests</li>\n<li>Shared Go mod cache PVC\n<ul>\n<li>Why?</li>\n</ul>\n</li>\n<li>Docker image building</li>\n</ul>\n</li>\n<li>Docker image building\n<ul>\n<li>Setting up automatic builds for ChainGuard's fork of Kaniko</li>\n<li><code>go build</code> exiting with a vague VCS error</li>\n</ul>\n</li>\n<li>Spawning sub-clusters with Kubernetes</li>\n</ul>","date_published":"2026-05-17T00:00:00.000Z","tags":[]},{"id":"https://preview.xeiaso.net/notes/2026/amazonbot-respecting-robots-txt/","url":"https://preview.xeiaso.net/notes/2026/amazonbot-respecting-robots-txt/","title":"Amazonbot is finally respecting robots.txt","content_html":"<p>I just got an email from Amazon saying they're finally going to respect <a href=\"https://www.robotstxt.org/\">robots.txt</a>. Here's the verbatim email I got:</p>\n<blockquote>\n<p>We are writing to inform you that starting Monday, June 15, 2026, crawl preferences for Amazonbot will be managed solely through the industry-standard directives. This gives you direct, ongoing control over how Amazonbot accesses your site, rather than relying on manual requests. If you do not implement robots.txt directives by that date, Amazonbot will follow standard web crawling practices when accessing your site.</p>\n<p>How to maintain your current preferences: The robots.txt protocol allows you to control Amazonbot’saccess at the page-, directory-, or site-level and update your preferences at any time. Please find detailed information on Amazonbots approach to these directives here: <a href=\"https://developer.amazon.com/amazonbot\">https://developer.amazon.com/amazonbot</a>.</p>\n<p>Best,</p>\n<p>Amazon Publisher Support</p>\n<p><a href=\"mailto:amazonbot@amazon.com\">amazonbot@amazon.com</a></p>\n<p>Get Outlook for Mac</p>\n</blockquote>\n<div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Numa is smug\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/numa/smug\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#numa\">Numa</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>Amazing, they even kept the &quot;sent from my iPhone&quot; message proving they sent it\nfrom Outlook for Mac. Looking at the email headers it has a bunch of\nExchange-specific headers so it's probably actually from Outlook for Mac. This\ntimeline is absolutely wild.</p></div></div></div>\n<p>This makes me feel kinda weird because <a href=\"https://xeiaso.net/talks/2025/surreal-joy-homelab/\">Amazon's scraper is why Anubis exists</a>.</p>\n<p>I'm gonna make sure to merge these robots.txt changes into Anubis if they aren't already there.</p>","date_published":"2026-05-14T00:00:00.000Z","tags":[]},{"id":"https://preview.xeiaso.net/notes/2026/gitlab-layoffs/","url":"https://preview.xeiaso.net/notes/2026/gitlab-layoffs/","title":"I'm really frustrated that GitLab is doing layoffs","content_html":"<p><a href=\"https://about.gitlab.com/blog/gitlab-act-2/\">GitLab announced layoffs today</a>. They don't state how many people are affected, but honestly I find this really frustrating for several reasons:</p>\n<ol>\n<li>This is the one time where they could have won by doing relatively nothing. GitHub is having big outages on a daily cadence. All they have to do is market themselves as &quot;we're the stable one&quot; and maybe add tooling to run your existing GitHub Actions in GitLab to make the transition easier. They could have won so hard it's not even funny because GitLab makes it trivial to host it yourself.</li>\n<li>This is yet another case of &quot;the stock price has gone down but we don't want to look bad to investors so we'll say that AI is going to help us more&quot;. I'm increasingly skeptical of this claim, but it's what makes the company look good to the people with the money sooo...</li>\n<li>They claim that one of their main goals is &quot;Speed with Quality&quot;. Usually this is a &quot;of two, pick one&quot; type of scenario. I shudder to think what may happen when GitLab turns into a feature factory powered by <a href=\"https://xeiaso.net/blog/protos/\">something on the lines of Protos</a>.</li>\n</ol>\n<p>Maybe GitLab did need to trim the fat, maybe they will come out of this stronger, but damn I just can't help but think about a world where they could have won without AI and just by being more stable than GitHub.</p>\n<div class=\"flex flex-col space-y-0\"><div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Numa is smug\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/numa/smug\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#numa\">Numa</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>One small problem with that: what you are suggesting makes sense. We live in\nclown world with clown world logic. Why would we be allowed to have things\nthat make sense in clown world?</p><small>Also yes, I do know that clowning is actually a very difficult art to pull off correctly. Humor is one of the most difficult things on the planet because if you do it wrong you offend people. People that are offended generally aren't people that are laughing. As a character that largely amounts to being a jaded contrarian comedian this is something that comes up a lot when planning what I say.</small><p>Also maybe I'm just oversensitive to it at this point, but the layoff announcement really reads like Claude Opus output. What a fuckin' world.</p></div></div></div><div class=\"flex space-x-2 bg-bg-soft dark:bg-bgDark-soft mx-auto min-h-fit\n      lg:w-[80ch] sm:w-[65ch] w-full\n      lg:p-4 p-2\n      // Base styles for all messages\n      mt-0 mb-0 rounded-none\n      // First message styles\n      first:mt-4 first:rounded-t-lg first:pb-2\n      // Last message styles\n      last:mb-4 last:rounded-b-lg last:pt-1\n      // Middle message top/bottom adjustment\n      [&amp;:not(:first-child)]:-mt-[1px] [&amp;:not(:first-child)]:py-1\"><div class=\"h-16 w-16 shrink-0 not-prose\"><img class=\"h-16 w-16 shrink-0 max-w-none object-contain rounded-xs\" alt=\"Aoi is coffee\" loading=\"lazy\" width=\"64\" height=\"64\" src=\"https://stickers.xeiaso.net/sticker/aoi/coffee\"/></div><div class=\"flex-1 min-w-0\"><span class=\"font-semibold text-sm block mb-1\"><a href=\"https://preview.xeiaso.net/characters#aoi\">Aoi</a></span><span class=\"mx-auto\"></span><div class=\"text-fg-1 dark:text-fgDark-1 text-sm prose-p:my-2\"><p>I don't want to live on this planet anymore.</p></div></div></div></div>\n<p>Apropos of nothing, I'm really enjoying <a href=\"https://tangled.org/xeiaso.net\">my experimentation with Tangled</a>. More to come soon when I have more to say.</p>","date_published":"2026-05-11T00:00:00.000Z","tags":[]},{"id":"https://preview.xeiaso.net/blog/2026/abstain-from-install/","url":"https://preview.xeiaso.net/blog/2026/abstain-from-install/","title":"Maybe you shouldn't install new software for a bit","content_html":"<p>In the wake of <a href=\"https://copy.fail/\">copy.fail</a>, there are more vulnerabilities that have been announced:</p>\n<ul>\n<li><a href=\"https://github.com/0xdeadbeefnetwork/Copy_Fail2-Electric_Boogaloo\">Copy Fail 2: Electric Boogaloo</a></li>\n<li><a href=\"https://github.com/V4bel/dirtyfrag\">Dirty Frag</a></li>\n</ul>\n<p>Right now would be one of the best times for a supply chain attack via NPM to hit hard.</p>\n<p>Outside of Linux kernel patches from your distro, I think it's probably a good idea to put a moratorium on installing new software for a week or so.</p>","date_published":"2026-05-07T00:00:00.000Z","tags":[]},{"id":"https://preview.xeiaso.net/notes/2026/claude-code-wins-april-fools/","url":"https://preview.xeiaso.net/notes/2026/claude-code-wins-april-fools/","title":"Claude Code won April Fools Day this year","content_html":"<p>April Fools Day is somewhat of a legendary day among nerds. Historically it's been when the nerds at GMail introduced <a href=\"https://archive.google/customtime/\">GMail Custom Time</a>, where you could interrupt causality by making GMail look like you sent a message before it was actually sent. It actually worked.</p>\n<p>Sometimes this gets taken too far and the joke falls flat, causing a lot more problems than would exist if the joke never happened in the first place. Incidents like this have resulted in many companies just putting in policies against doing that to avoid customer growth impact.</p>\n<p>It's refreshing to see the Claude Code team introduce the <code>/buddy</code> system this year. When you run <code>/buddy</code>, it hatches a coding companion that hangs out in your Claude Code interface like a tamagochi. Here's my buddy Xentwine:</p>\n<pre><code class=\"code-highlight\"><span class=\"code-line\">╭──────────────────────────────────────╮\n</span><span class=\"code-line\">│                                      │\n</span><span class=\"code-line\">│  ★★★ RARE                     ROBOT  │\n</span><span class=\"code-line\">│                                      │\n</span><span class=\"code-line\">│     (   )                            │\n</span><span class=\"code-line\">│     .[||].                           │\n</span><span class=\"code-line\">│    [ @  @ ]                          │\n</span><span class=\"code-line\">│    [ ==== ]                          │\n</span><span class=\"code-line\">│    `------´                          │\n</span><span class=\"code-line\">│                                      │\n</span><span class=\"code-line\">│  Xentwine                            │\n</span><span class=\"code-line\">│                                      │\n</span><span class=\"code-line\">│  &quot;A methodical circuit-whisperer     │\n</span><span class=\"code-line\">│  obsessed with untangling logical    │\n</span><span class=\"code-line\">│  snarls; speaks in patient,          │\n</span><span class=\"code-line\">│  patronizing riddles and will        │\n</span><span class=\"code-line\">│  absolutely let you sit in your own  │\n</span><span class=\"code-line\">│  bug for three minutes before        │\n</span><span class=\"code-line\">│  offering the blindingly obvious     │\n</span><span class=\"code-line\">│  fix.&quot;                               │\n</span><span class=\"code-line\">│                                      │\n</span><span class=\"code-line\">│  DEBUGGING  █████░░░░░  47           │\n</span><span class=\"code-line\">│  PATIENCE   █████░░░░░  47           │\n</span><span class=\"code-line\">│  CHAOS      ██░░░░░░░░  21           │\n</span><span class=\"code-line\">│  WISDOM     █████████░  92           │\n</span><span class=\"code-line\">│  SNARK      █████░░░░░  49           │\n</span><span class=\"code-line\">│                                      │\n</span><span class=\"code-line\">╰──────────────────────────────────────╯\n</span></code></pre>\n<p>Here's what it looks like in the Claude Code app:</p>\n<figure class=\"max-w-3xl mx-auto not-prose w-full undefined\"><a href=\"https://files.xeiaso.net/blog/2026/claude-code-wins-april-fools/claude-buddy.jpg\"><picture><source type=\"image/avif\" srcset=\"https://files.xeiaso.net/blog/2026/claude-code-wins-april-fools/claude-buddy.avif\"/><source type=\"image/webp\" srcset=\"https://files.xeiaso.net/blog/2026/claude-code-wins-april-fools/claude-buddy.webp\"/><img loading=\"lazy\" src=\"https://files.xeiaso.net/blog/2026/claude-code-wins-april-fools/claude-buddy.jpg\"/></picture></a></figure>\n<p>I think this is the best April Fools Day feature in recent memory because it seems intentionally designed to avoid impacting users in a way that would cause problems:</p>\n<ul>\n<li>You have to take manual action to create your coding buddy, it's off by default.</li>\n<li>It mostly stays out of the way when you do create it, meaning that it doesn't impact your normal working process.</li>\n<li>Your buddy sometimes randomly interjects like a tamagochi.</li>\n<li>You can pet the dog, dragon, or robot with <code>/buddy pet</code>.</li>\n</ul>\n<p>This is the kind of harmless prank that all nerds should aspire for. 10/10.</p>","date_published":"2026-04-01T00:00:00.000Z","tags":[]},{"id":"https://preview.xeiaso.net/notes/2026/ai-gpus-cant-process-graphics/","url":"https://preview.xeiaso.net/notes/2026/ai-gpus-cant-process-graphics/","title":"Small note about AI 'GPUs'","content_html":"<p>I've been seeing talk around about wanting to capitalize on the AI bubble popping and picking up server GPUs for pennies on the dollar so they can play games in higher fidelity due to server GPUs having more video ram. I hate to be the bearer of bad news here, but most of those enterprise GPUs don't have the ability to process graphics.</p>\n<p>Yeah, that's right, in order to pack in as much compute as possible per chip, they removed video output and graphics processing from devices we are calling <strong>graphics processing units</strong>. The only thing those cards will be good for is CUDA operations for AI inference, AI training, or other things that do not involve gaming.</p>\n<hr/>\n<p>On a separate note, I'm reaching the point in recovery where I am getting very bored and am so completely ready to just head home. At least the diet restrictions end this week, so that's something to look forward to. God I want a burrito.</p>","date_published":"2026-03-30T00:00:00.000Z","tags":[]},{"id":"https://preview.xeiaso.net/notes/2026/dns-fight/","url":"https://preview.xeiaso.net/notes/2026/dns-fight/","title":"Homelab downtime update: The fight for DNS supremacy","content_html":"<p>Hey all, quick update continuing from yesterday's announcement that my homelab went down. This is stream of consciousness and unedited. Enjoy!</p>\n<p>Turns out the entire homelab didn't go down and two Kubernetes nodes survived the power outage somehow.</p>\n<p>Two Kubernetes controlplane nodes.</p>\n<p>Kubernetes really wants there to be an odd number of controlplane nodes and my workloads are too heavy for any single node to run and Longhorn really wants there to be at least three nodes online. So I had to turn them off.</p>\n<p>How did I get in? The Mac mini that I used for Anubis CI. It somehow automatically powered on when the grid reset and/or survived the power outage.</p>\n<pre class=\"language-text\"><code class=\"language-text code-highlight\"><span class=\"code-line\">xe@t-elos:~$ uptime\n</span><span class=\"code-line\"> 09:45:55 up 66 days,  9:51,  4 users,  load average: 0.37, 0.22, 0.18\n</span></code></pre>\n<p>Holy shit, that's good to know!</p>\n<p>Anyways the usual suspects for trying to debug things didn't work (kubectl get nodes got a timeout, etc.), so I did an nmap across the entire home subnet. Normally this is full of devices and hard to read. This time there's basically nothing. What stood out was this:</p>\n<pre class=\"language-text\"><code class=\"language-text code-highlight\"><span class=\"code-line\">Nmap scan report for kos-mos (192.168.2.236)\n</span><span class=\"code-line\">Host is up, received arp-response (0.00011s latency).\n</span><span class=\"code-line\">Scanned at 2026-03-18 09:23:09 EDT for 1s\n</span><span class=\"code-line\">Not shown: 996 closed tcp ports (reset)\n</span><span class=\"code-line\">PORT      STATE SERVICE   REASON\n</span><span class=\"code-line\">3260/tcp  open  iscsi     syn-ack ttl 64\n</span><span class=\"code-line\">9100/tcp  open  jetdirect syn-ack ttl 64\n</span><span class=\"code-line\">50000/tcp open  ibm-db2   syn-ack ttl 64\n</span><span class=\"code-line\">50001/tcp open  unknown   syn-ack ttl 64\n</span><span class=\"code-line\">MAC Address: FC:34:97:0D:1E:CD (Asustek Computer)\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">Nmap scan report for ontos (192.168.2.237)\n</span><span class=\"code-line\">Host is up, received arp-response (0.00011s latency).\n</span><span class=\"code-line\">Scanned at 2026-03-18 09:23:09 EDT for 1s\n</span><span class=\"code-line\">Not shown: 996 closed tcp ports (reset)\n</span><span class=\"code-line\">PORT      STATE SERVICE   REASON\n</span><span class=\"code-line\">3260/tcp  open  iscsi     syn-ack ttl 64\n</span><span class=\"code-line\">9100/tcp  open  jetdirect syn-ack ttl 64\n</span><span class=\"code-line\">50000/tcp open  ibm-db2   syn-ack ttl 64\n</span><span class=\"code-line\">50001/tcp open  unknown   syn-ack ttl 64\n</span><span class=\"code-line\">MAC Address: FC:34:97:0D:1F:AE (Asustek Computer)\n</span></code></pre>\n<p>Those two machines are Kubernetes controlplane nodes! I can't SSH into them because they're running Talos Linux, but I can use <code>talosctl</code> (via port 50000) to shut them down:</p>\n<pre class=\"language-text\"><code class=\"language-text code-highlight\"><span class=\"code-line\">$ ./bin/talosctl -n 192.168.2.236 shutdown --force\n</span><span class=\"code-line\">WARNING: 192.168.2.236: server version 1.9.1 is older than client version 1.12.5\n</span><span class=\"code-line\">watching nodes: [192.168.2.236]\n</span><span class=\"code-line\">    * 192.168.2.236: events check condition met\n</span><span class=\"code-line\">\n</span><span class=\"code-line\">$ ./bin/talosctl -n 192.168.2.237 shutdown --force\n</span><span class=\"code-line\">WARNING: 192.168.2.237: server version 1.9.1 is older than client version 1.12.5\n</span><span class=\"code-line\">watching nodes: [192.168.2.237]\n</span><span class=\"code-line\">    * 192.168.2.237: events check condition met\n</span></code></pre>\n<p>And now it's offline until I get home.</p>\n<p>This was causing the sponsor panel to be offline because the external-dns pod in the homelab was online and fighting my new cloud deployment for DNS supremacy. The sponsor panel is now back online (I should have put it in the cloud in the first place, that's on me) and peace has been restored to most of the galaxy, at least as much as I can from here.</p>\n<p>Action items:</p>\n<ul>\n<li>Figure out why ontos and kos-mos came back online</li>\n<li>Make all nodes in the homelab resume power when wall power exists again</li>\n<li>Review homelab for PSU damage</li>\n<li>Re-evaluate usage of Talos Linux, switch to Rocky?</li>\n</ul>","date_published":"2026-03-18T00:00:00.000Z","tags":[]}]}