<?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>Forem: arunkant</title>
    <description>The latest articles on Forem by arunkant (@arunkant).</description>
    <link>https://forem.com/arunkant</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F136893%2F76d71e2a-5d18-4fbb-a4fb-33d3a69d532a.png</url>
      <title>Forem: arunkant</title>
      <link>https://forem.com/arunkant</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/arunkant"/>
    <language>en</language>
    <item>
      <title>Vellum — a private, on‑device screenshot assistant powered by Gemma 4</title>
      <dc:creator>arunkant</dc:creator>
      <pubDate>Thu, 21 May 2026 09:02:03 +0000</pubDate>
      <link>https://forem.com/arunkant/vellum-a-private-on-device-screenshot-assistant-powered-by-gemma-4-4opi</link>
      <guid>https://forem.com/arunkant/vellum-a-private-on-device-screenshot-assistant-powered-by-gemma-4-4opi</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-gemma-2026-05-06"&gt;Gemma 4 Challenge: Build with Gemma 4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.arunkant.com/vellum/" rel="noopener noreferrer"&gt;&lt;strong&gt;Vellum&lt;/strong&gt;&lt;/a&gt; is a tray‑resident macOS app that turns any screenshot into a conversation. Hit &lt;code&gt;⌘⇧1&lt;/code&gt; to drag a region, or &lt;code&gt;⌘⇧2&lt;/code&gt; to grab the whole screen — the capture pops open in a chat window, the image is described and OCR'd in the background, and you can immediately ask follow‑up questions about what's on screen.&lt;/p&gt;

&lt;p&gt;The interesting bit: Vellum runs Gemma 4 &lt;strong&gt;locally&lt;/strong&gt; on Apple Silicon by default. No screenshot ever leaves the machine. There's no account, no API key, no cloud round‑trip — just a global hotkey and a vision‑language model living in your menu bar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How I actually use it:&lt;/strong&gt; over a few days I capture screenshots of things I read on the internet — articles, threads, diagrams, half‑finished thoughts — and let them pile up. Then one evening I sit down and review the whole stack. Because every capture is OCR'd by Gemma 4, the pile is fully searchable, and the review session turns into something more useful than a screenshot graveyard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Capture now, think later.&lt;/strong&gt; Grab anything interesting with &lt;code&gt;⌘⇧1&lt;/code&gt; and keep reading — no context switch, no "where do I save this".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OCR everything automatically.&lt;/strong&gt; Gemma 4 reads the text out of each image on capture, so I can search across days of captures by what was &lt;em&gt;in&lt;/em&gt; them, not just when I took them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review in one sitting.&lt;/strong&gt; An evening pass over the gallery surfaces patterns I'd never have spotted scrolling live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New blog‑post ideas.&lt;/strong&gt; Asking Gemma about a cluster of related captures regularly hands me an angle for something I want to write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organize with tags.&lt;/strong&gt; Group captures into themes so the next review starts already sorted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few quality‑of‑life touches that make the review session flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One‑click "Copy for Slack / JIRA".&lt;/strong&gt; From the capture's action bar, Vellum puts the image &lt;em&gt;and&lt;/em&gt; a ready‑to‑paste blurb on the clipboard — Gemma 4's description plus the tags, formatted for each tool (Slack mrkdwn or Jira markup). Paste straight into a thread or ticket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Copy with shadow"&lt;/strong&gt; for a polished share. One click composites the raw screenshot onto a transparent canvas with padding, rounded corners and a soft drop shadow, then drops it on the clipboard — the kind of framed shot you'd otherwise reach for a separate tool to make.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plain "Copy image"&lt;/strong&gt; for everything else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each action also has a &lt;code&gt;⌘1…9&lt;/code&gt; shortcut, so the whole review‑and‑share loop stays on the keyboard.&lt;/p&gt;

&lt;p&gt;Source + install script (one curl line for Apple Silicon): &lt;a href="https://github.com/arunkant/vellum" rel="noopener noreferrer"&gt;https://github.com/arunkant/vellum&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/utFBujLU7ws"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Screenshots:&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fg3wmzha69avok3d1gyja.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.amazonaws.com%2Fuploads%2Farticles%2Fg3wmzha69avok3d1gyja.png" alt="Vellum Main UI" width="800" height="602"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Ftqx6dxildrw4nlyflk22.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.amazonaws.com%2Fuploads%2Farticles%2Ftqx6dxildrw4nlyflk22.png" alt="Vellum ai sidepanel" width="800" height="602"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2F48m46k0cphrob7ajq58l.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.amazonaws.com%2Fuploads%2Farticles%2F48m46k0cphrob7ajq58l.png" alt="Vellum search screenshots" width="800" height="602"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2F98uqa5t047d6vtokxpfv.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.amazonaws.com%2Fuploads%2Farticles%2F98uqa5t047d6vtokxpfv.png" alt="Vellum Tray widget" width="283" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install on macOS (Apple Silicon):&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://www.arunkant.com/vellum/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How I Used Gemma 4
&lt;/h2&gt;

&lt;p&gt;Vellum bundles &lt;code&gt;llama.cpp&lt;/code&gt;'s &lt;code&gt;llama-server&lt;/code&gt; as an &lt;code&gt;extraResource&lt;/code&gt; and, on first use of the local provider, downloads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gemma-4-E4B-it-Q4_K_M.gguf&lt;/code&gt; — Google's vision‑capable &lt;strong&gt;Gemma 4 E4B‑it&lt;/strong&gt;, quantized to Q4_K_M (~5 GB).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mmproj-BF16.gguf&lt;/code&gt; — the multimodal projector that lets Gemma 4 actually look at pixels.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both come from Unsloth's GGUF mirror of the official &lt;code&gt;google/gemma-4-E4B-it&lt;/code&gt;. The server is launched lazily the first time the user takes a screenshot, then kept warm for the rest of the session.&lt;/p&gt;

&lt;p&gt;The request flow is a regular OpenAI‑compatible vision call to localhost — Gemma 4 happily accepts an image part next to a text part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/ai/local.ts&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;serverEndpoint&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;/v1/chat/completions`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemma-4-e4b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image_url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;imageToDataUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imagePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxTokens&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The launcher arguments are deliberately conservative so it fits comfortably alongside a browser and an IDE on a 16 GB MacBook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/ai/llama-server.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;modelPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--mmproj&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;mmprojPath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--port&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8192&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--no-webui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two prompts drive everything:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Auto‑analysis&lt;/strong&gt; — a short instruction asking Gemma 4 to describe the app/page and pull out the most important visible text, runs the moment a capture lands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat&lt;/strong&gt; — every user message is forwarded with the original image still attached, so Gemma can ground each answer in the pixels rather than its own prior summary.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The same &lt;code&gt;VisionProvider&lt;/code&gt; interface is also implemented by an OpenRouter‑backed provider, so users who want a beefier hosted model can swap in one without touching the rest of the app. Gemma 4 is the &lt;strong&gt;default&lt;/strong&gt;, and it's the only path that works fully offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multimodal Insights
&lt;/h2&gt;

&lt;p&gt;A few things I learned shipping Gemma 4 to end‑users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The 4B/E4B vision model is genuinely shippable.&lt;/strong&gt; It comfortably reads UI text, error dialogs, and code editors, and an 8k context (&lt;code&gt;-c 8192&lt;/code&gt;) is plenty for a single screen capture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The multimodal projector is the easy thing to forget.&lt;/strong&gt; Without &lt;code&gt;--mmproj&lt;/code&gt;, the model will silently treat the image like noise. Vellum downloads it alongside the weights and refuses to start the server if either file is missing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Q4_K_M is the right tradeoff for laptops.&lt;/strong&gt; It keeps the whole thing under ~5 GB on disk, leaves headroom for a browser, and the quality cost on screenshot tasks is invisible to me in practice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I run Gemma at a low &lt;code&gt;temperature: 0.2&lt;/code&gt;.&lt;/strong&gt; Screenshot Q&amp;amp;A wants literal, deterministic answers rather than creative ones, so a low temperature is the safer default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Gemma 4 for this app
&lt;/h2&gt;

&lt;p&gt;I built Vellum around a habit: capturing things I read over days, then reviewing the pile in one sitting. That only works if every capture is OCR'd and searchable the moment I take it — and I'm not about to stream days of half‑formed reading habits and private dashboards to a third‑party endpoint just to make that happen. An open‑weights vision model I can bundle and ship inside an Electron app is the only way that problem actually gets solved: it runs the moment I hit &lt;code&gt;⌘⇧1&lt;/code&gt;, with no account, no API key, and no network hop. Gemma 4 E4B‑it is the first model in this size class where the OCR and the follow‑up answers are consistently good enough that I stopped reaching for the cloud fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; &lt;code&gt;google/gemma-4-E4B-it&lt;/code&gt; (Q4_K_M GGUF, BF16 mmproj) via Unsloth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; &lt;code&gt;llama.cpp&lt;/code&gt; &lt;code&gt;llama-server&lt;/code&gt;, bundled per‑arch as an Electron &lt;code&gt;extraResource&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App shell:&lt;/strong&gt; Electron + Vite + TypeScript, packaged with Electron Forge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; SQLite for screenshots + AI results + chat history; images on disk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto‑update:&lt;/strong&gt; GitHub Releases + a tiny installer script served from GitHub Pages&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/arunkant/vellum" rel="noopener noreferrer"&gt;https://github.com/arunkant/vellum&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latest release (v1.3.0):&lt;/strong&gt; &lt;a href="https://github.com/arunkant/vellum/releases/tag/v1.3.0" rel="noopener noreferrer"&gt;https://github.com/arunkant/vellum/releases/tag/v1.3.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Landing page + installer:&lt;/strong&gt; &lt;a href="https://www.arunkant.com/vellum/" rel="noopener noreferrer"&gt;https://www.arunkant.com/vellum/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License:&lt;/strong&gt; MIT (bundled &lt;code&gt;llama.cpp&lt;/code&gt; retains its own license — see &lt;code&gt;THIRD_PARTY_NOTICES.md&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to the Gemma team for shipping a vision model small enough to live in a menu bar, and to the &lt;code&gt;llama.cpp&lt;/code&gt; and Unsloth communities for making GGUF inference this painless.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gemmachallenge</category>
      <category>gemma</category>
    </item>
    <item>
      <title>Why I'm Building SaaS in 2026</title>
      <dc:creator>arunkant</dc:creator>
      <pubDate>Wed, 29 Apr 2026 12:42:21 +0000</pubDate>
      <link>https://forem.com/arunkant/why-im-building-saas-in-2026-55hn</link>
      <guid>https://forem.com/arunkant/why-im-building-saas-in-2026-55hn</guid>
      <description>&lt;p&gt;Every few months, someone declares SaaS dead. The argument has gotten louder in 2026: why pay for vertical tools when an LLM and a GitHub Action can do the same job in an afternoon?&lt;/p&gt;

&lt;p&gt;I've heard this pitch enough times - and built enough of those afternoon GitHub Actions myself - to know where it breaks. The demo always works. The Tuesday morning two months later, when the upstream API changes its response shape and your "AI agent" silently ships nonsense to customers, is where the wheels come off.&lt;/p&gt;

&lt;p&gt;I think SaaS in 2026 is healthier than the obituaries suggest, but the conversation around it is shifting in ways worth paying attention to.&lt;/p&gt;

&lt;h2&gt;
  
  
  SaaS Isn't Dying, But the Conversation Is Shifting
&lt;/h2&gt;

&lt;p&gt;You've seen the takes. &lt;em&gt;"SaaS is dead."&lt;/em&gt; &lt;em&gt;"AI agents will replace every vertical tool."&lt;/em&gt; &lt;em&gt;"Why buy software when you can just prompt an LLM to do it in-house?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I don't buy it.&lt;/p&gt;

&lt;p&gt;Those takes confuse the &lt;strong&gt;interface&lt;/strong&gt; with the &lt;strong&gt;infrastructure&lt;/strong&gt;. Yes, AI changes how we interact with software. But the need for shared, maintained, specialized tools doesn't vanish just because you can spin up an Autogen crew in ten minutes.&lt;/p&gt;

&lt;p&gt;If anything, the more AI floods the market, the more valuable it becomes to have a &lt;em&gt;concrete workflow&lt;/em&gt; that actually works while you sleep.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Agent Trap: Swiss Army Knives Made of Jelly
&lt;/h2&gt;

&lt;p&gt;In-house AI agents are seductive because they're infinitely flexible. The same agent can summarize tickets, draft emails, assign blame for bugs, and probably order pizza if you give it a DoorDash API key.&lt;/p&gt;

&lt;p&gt;But that flexibility comes at a hidden cost: &lt;strong&gt;reliability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An agent is essentially a reasoning layer wrapped around hope. It guesses at priorities. It hallucinates whether a PR description is customer-facing or internal. It breaks when an upstream tool sneezes. And every time a new model drops, you're back to prompt-engineering a Friday afternoon away.&lt;/p&gt;

&lt;p&gt;The real cost isn't the OpenAI bill. It's you, at 11 PM, debugging why your changelog included &lt;em&gt;"fix typo in admin panel"&lt;/em&gt; in the email that just went to 10,000 users.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Framework: Agent First, Workflow Second
&lt;/h2&gt;

&lt;p&gt;This isn't an argument against agents. It's an argument for knowing &lt;em&gt;when&lt;/em&gt; to use them.&lt;/p&gt;

&lt;p&gt;The pattern I keep coming back to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Use agents to figure out the workflow.&lt;/strong&gt;&lt;br&gt;
What do internal teams actually want - raw ticket lists grouped by squad, or plain-English summaries? Should security patches get a red border? Should refactors be auto-filtered? Agents are great for exploration. You can ask, iterate, and throw away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Convert the agent into a concrete workflow.&lt;/strong&gt;&lt;br&gt;
Once you know the shape of the work, stop treating it like a conversation. Hard-code the rules. Build the validation layer. Lock the integration. Turn the fuzzy agent into reliable software that does the same correct thing every Tuesday at 9 AM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Mix intelligence and consistency where each belongs.&lt;/strong&gt;&lt;br&gt;
Use AI for the parts that benefit from intelligence - reading unstructured tickets, understanding context, writing human prose. Use software for the parts that require consistency - formatting, routing, permissions, delivery.&lt;/p&gt;

&lt;p&gt;The mistake most teams make is stopping at step one and shipping the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build vs. Buy: The Hidden Maintenance Tax
&lt;/h2&gt;

&lt;p&gt;There's another argument I keep hearing: &lt;em&gt;"We'll just build our own AI tools internally. It's cheaper, and our data stays in-house."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sure. But agents are &lt;strong&gt;not&lt;/strong&gt; set-and-forget.&lt;/p&gt;

&lt;p&gt;You still need people to maintain them. New API version from JIRA? Fix the parser. New model release? Retest all your prompts. Linear changed their webhook format again? Back to the logs. You've built a fragile ETL pipeline dressed up as a chatbot, and now you're the on-call engineer for a paragraph generator.&lt;/p&gt;

&lt;p&gt;There's also a quieter cost most teams don't price in: &lt;strong&gt;external tools learn faster.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If every company builds its own changelog bot, you've got 500 engineering teams each solving the same edge cases in isolation. One team figures out how to handle JIRA sub-tasks. Another figures out GitHub co-author attribution. Nobody shares notes. A shared tool learns from all of them. Your internal agent only learns from you. Over time, that compounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like in Practice
&lt;/h2&gt;

&lt;p&gt;Imagine the work of generating release notes done right.&lt;/p&gt;

&lt;p&gt;You connect to your JIRA board (or GitHub, or Linear). You define your audiences - maybe an internal Slack channel for the engineering team and a public page for users. From your earlier exploration, you already know the rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal changelogs need ticket IDs and squad assignments.&lt;/li&gt;
&lt;li&gt;External changelogs need human-readable context, no jargon.&lt;/li&gt;
&lt;li&gt;Security patches always get highlighted.&lt;/li&gt;
&lt;li&gt;"WIP" and "refactor" tickets get filtered out unless manually overridden.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those rules become a workflow. Every release, it reads your tickets, applies the logic, generates the prose, and delivers it to the right place. It doesn't get creative. It doesn't forget the rules because it's Tuesday. It just works.&lt;/p&gt;

&lt;p&gt;You get the intelligence of AI - it understands your messy tickets - wrapped in the reliability of actual software. That's the shape of the SaaS I want to use, and it's the shape of the SaaS I'm building with &lt;a href="https://releasedog.com" rel="noopener noreferrer"&gt;Releasedog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  SaaS in 2026 Is the Plumbing, Not the Hype
&lt;/h2&gt;

&lt;p&gt;I'm not skeptical of AI. I'm skeptical of &lt;em&gt;where&lt;/em&gt; people are pointing it.&lt;/p&gt;

&lt;p&gt;Agents are incredible demos. They're terrible infrastructure.&lt;/p&gt;

&lt;p&gt;The future belongs to tools that take AI's flexibility and trap it inside software's reliability. Use agents to explore. Use workflows to run your business. And for the love of your future self, stop maintaining that Friday-night GitHub Action that generates your changelog.&lt;/p&gt;

&lt;p&gt;Your 11 PM self will thank you.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>ai</category>
      <category>agents</category>
      <category>workflows</category>
    </item>
  </channel>
</rss>
