<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Owen</title>
    <description>The latest articles on DEV Community by Owen (@craftowen).</description>
    <link>https://dev.to/craftowen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4209643%2F89af1ea4-6805-43c5-9ee1-e3d971274b4d.png</url>
      <title>DEV Community: Owen</title>
      <link>https://dev.to/craftowen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/craftowen"/>
    <language>en</language>
    <item>
      <title>I rebuilt my AI coding agent workspace in Rust. In a 10-terminal benchmark it used 1/15 the memory of the Electron app I replaced</title>
      <dc:creator>Owen</dc:creator>
      <pubDate>Mon, 26 Oct 2026 23:26:31 +0000</pubDate>
      <link>https://dev.to/craftowen/i-rebuilt-my-ai-coding-agent-workspace-in-rust-it-uses-115-the-memory-of-the-electron-app-i-3467</link>
      <guid>https://dev.to/craftowen/i-rebuilt-my-ai-coding-agent-workspace-in-rust-it-uses-115-the-memory-of-the-electron-app-i-3467</guid>
      <description>&lt;p&gt;I run a lot of coding agents at the same time: Claude Code in one worktree, Codex in another, a dev server, a couple of shells. The agent workspace I was using did the job, but it was an Electron app, and with a dozen terminals open it had quietly become the heaviest thing on my Mac.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/craftowen/krill" rel="noopener noreferrer"&gt;Krill&lt;/a&gt;: the same parallel-agent workflow as a native Rust desktop app (no Electron, no web view). This post is about the architecture, and about a benchmark I ran to check whether the rewrite was actually worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Each task gets its own &lt;strong&gt;Git worktree&lt;/strong&gt; and terminal, so agents don't trip over each other.&lt;/li&gt;
&lt;li&gt;It works with any CLI agent (Claude Code, Codex, Cursor CLI, OpenCode, Antigravity) or a plain shell.&lt;/li&gt;
&lt;li&gt;Agent status hooks (&lt;code&gt;working&lt;/code&gt;, &lt;code&gt;waiting&lt;/code&gt;, &lt;code&gt;done&lt;/code&gt;, &lt;code&gt;blocked&lt;/code&gt;...) feed a &lt;strong&gt;Needs You&lt;/strong&gt; queue, so the agent waiting on your approval floats to the top.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sessions outlive the app.&lt;/strong&gt; Quit it, crash it, update it: the agents keep running and you reconnect to the same session IDs.&lt;/li&gt;
&lt;li&gt;A headless &lt;code&gt;krill&lt;/code&gt; CLI drives everything without the GUI (&lt;code&gt;krill new&lt;/code&gt;, &lt;code&gt;send&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;wait&lt;/code&gt;, &lt;code&gt;attach&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Three processes, not one
&lt;/h2&gt;

&lt;p&gt;The "sessions survive the app" part drove most of the design.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;krill-app      gpui UI. Renders only the panes you can see.
   │  IPC
krilld         session daemon: terminal state (alacritty_terminal),
   │           1,000 hot rows + zstd-compressed cold scrollback,
   │           hooks, git watching, mobile gateway
krill-holder   tiny PTY parent. Owns the file descriptors. Never panics.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;krill-holder&lt;/strong&gt; is the only process that owns PTYs. It is deliberately small (the macOS build has a 1,300-line budget) and panics are banned there. If it dies, krilld still holds the fds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;krilld&lt;/strong&gt; keeps the terminal state and is the only thing that answers terminal queries. Session work runs under &lt;code&gt;catch_unwind&lt;/code&gt;, so one bad session doesn't take down the others. If krilld crashes, the agents survive because the holder keeps the PTYs. Output that krilld had read but not yet written to a segment can be lost, and the UI shows an explicit gap marker rather than pretending nothing happened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;krill-app&lt;/strong&gt; can be killed at any time. On restart it restores the layout and reattaches to the same sessions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rebooting, or losing both holder and daemon at once, does end the processes. Krill then respawns shells in the last working directory and keeps the old scrollback above them, but it doesn't pretend the old process survived.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark
&lt;/h2&gt;

&lt;p&gt;I wanted a number I could defend, so I measured Krill 0.2.2 against Orca 1.4.237 (an Electron agent workspace with a similar feature set) on the same machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mac mini (Apple M6, 32 GB), macOS 27.0.1, release builds from &lt;code&gt;/Applications&lt;/code&gt;, no tuning&lt;/li&gt;
&lt;li&gt;fresh isolated profile per run, same 1200×760pt window&lt;/li&gt;
&lt;li&gt;N login-shell tabs in the same git repo, one visible; each tab prints 20,000 lines (~2.8 MB of colored, wide, Unicode text)&lt;/li&gt;
&lt;li&gt;every process the app spawns is counted (helpers, GPU, renderer, daemons); shells are excluded on both sides&lt;/li&gt;
&lt;li&gt;1, 10 and 20 terminals × 3 runs, alternating which app goes first; medians reported&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1r2aba7oh9okre9hurrc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1r2aba7oh9okre9hurrc.png" alt="Krill uses 1/15 of Orca's memory at 10 terminals" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At 10 terminals (Krill 0.2.2 vs Orca 1.4.237, measured 2026-10-25):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Krill&lt;/th&gt;
&lt;th&gt;Orca&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Memory (&lt;code&gt;phys_footprint&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;85.9 MiB&lt;/td&gt;
&lt;td&gt;1,274.9 MiB&lt;/td&gt;
&lt;td&gt;1/15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idle CPU (30 s average, screen locked)&lt;/td&gt;
&lt;td&gt;1.1%&lt;/td&gt;
&lt;td&gt;31.0%&lt;/td&gt;
&lt;td&gt;1/27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU time to process the output&lt;/td&gt;
&lt;td&gt;1.2 s&lt;/td&gt;
&lt;td&gt;6.4 s&lt;/td&gt;
&lt;td&gt;1/5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install size&lt;/td&gt;
&lt;td&gt;92 MiB&lt;/td&gt;
&lt;td&gt;689 MiB&lt;/td&gt;
&lt;td&gt;1/7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh9slgrzreguvnw6neec0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh9slgrzreguvnw6neec0.png" alt="Where the memory goes" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most of the gap is the UI layer: 30 MiB for the Krill app versus about 994 MiB for Electron's main, GPU and renderer processes. The terminal daemons differ too (46.5 vs 256.3 MiB), mostly because Krill keeps 1,000 hot rows per terminal and compresses the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Krill lost, and other caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wall-clock output at 20 terminals:&lt;/strong&gt; Krill took 7.5 s to finish the burst versus Orca's 4.0 s. Krill's runs varied a lot (1.9 to 7.9 s) and the machine was shared, so I can't yet say how much of that is load. It's a real result, though, and it's on my list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screen locked:&lt;/strong&gt; all runs were recorded with the display locked. Even then, Orca's GPU process used ~22% CPU. I haven't measured the screen-on case yet, so I'm not claiming the 1/27 holds there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared machine:&lt;/strong&gt; other work (Rust builds) was running. Memory barely moves with load; CPU and timing can.&lt;/li&gt;
&lt;li&gt;Agent processes themselves are excluded. Claude Code costs the same in either app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Apple Silicon macOS is the main platform, and there's a Windows x64 preview. The macOS build isn't notarized; the installer verifies the checksum and signature, then removes the quarantine attribute, so read it before running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://krill.page/install.sh &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/krill-install.sh
&lt;span class="c"&gt;# read it first&lt;/span&gt;
bash /tmp/krill-install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's Apache-2.0: &lt;a href="https://github.com/craftowen/krill" rel="noopener noreferrer"&gt;github.com/craftowen/krill&lt;/a&gt; · &lt;a href="https://krill.page/en" rel="noopener noreferrer"&gt;krill.page&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, the most useful feedback is which step blocked you first: install, opening a repo, starting an agent, creating a second worktree, or reconnecting after an app restart.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
