<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AI on Jon Seager</title><link>https://jnsgr.uk/tags/ai/</link><description>Recent content in AI on Jon Seager</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Tue, 10 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jnsgr.uk/tags/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>Brewlog: Coffee &amp; Agents</title><link>https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/</guid><description>&lt;h2 id="introduction" class="relative group"&gt;Introduction &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#introduction" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;I’m really into speciality coffee. In July 2025 I started tracking my coffee habits with &lt;a href="https://roast.guide/" target="_blank" rel="noreferrer"&gt;Roastguide&lt;/a&gt;, a delightfully designed iOS app for people with a similar obsession to mine. It does a good job of tracking brews, roasters and bags, but over time I found myself wanting the data in a system that I controlled. I wanted to be able to query it, back it up, and adjust some of the flows to work better for how I brew and consume coffee.&lt;/p&gt;
&lt;p&gt;Despite my past scepticism of the previous generation of coding tools, recent developments have made them hard to ignore, and I wanted an excuse to build something substantial from scratch to get some experience.&lt;/p&gt;
&lt;p&gt;So I built &lt;a href="https://github.com/jnsgruk/brewlog" target="_blank" rel="noreferrer"&gt;b{rew}log&lt;/a&gt;, which is a self-hosted coffee logging platform. It tracks roasters, roasts, bags, brews, equipment, and cafe visits. It&amp;rsquo;s live at &lt;a href="https://coffee.jnsgr.uk" target="_blank" rel="noreferrer"&gt;coffee.jnsgr.uk&lt;/a&gt; if you want to poke around and witness the depths of my strange filter coffee obsession!&lt;/p&gt;
&lt;p&gt;This was by far the most complex project I&amp;rsquo;d built with &lt;a href="https://claude.com/product/claude-code" target="_blank" rel="noreferrer"&gt;Claude Code&lt;/a&gt;, or agentic coding tools in general. I&amp;rsquo;m responsible for the technology choices, architecture, and visual design, but I wrote almost none of the code myself.&lt;/p&gt;
&lt;p&gt;This post will cover the core features of Brewlog and how I designed the app, and finish with some observations and tips about agentic programming.&lt;/p&gt;
&lt;h2 id="core-features" class="relative group"&gt;Core Features &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#core-features" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Brewlog tracks roasters, roasts, bags, brews, equipment, and cafe visits. The landing page you see below summarises details of my last few brews, currently open bags of coffee and how much remains in each and some basic stats. When logged in, it provides quick controls to repeat a brew, or brew a particular coffee:&lt;/p&gt;
&lt;p&gt;&lt;a href="01.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/01_hu_445dd8a54a9a4ea8.webp 330w,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/01_hu_7bd2a139c538b332.webp 660w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/01_hu_28dfa3482ef59c87.webp 1024w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/01_hu_ceb4eac36acb5f30.webp 1130w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1130"
height="1682"
class="mx-auto my-0 rounded-md"
alt="brewlog landing page"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/01_hu_a6bf0a68f91b6a7d.png" srcset="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/01_hu_f4a29c4400db4876.png 330w,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/01_hu_a6bf0a68f91b6a7d.png 660w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/01_hu_6d2fed53d5402f30.png 1024w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/01.png 1130w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Brews are coffees I’ve made myself. Each brew is logged against a specific bag and captures the recipe (dose, water, time, temperature), the equipment used, and tasting notes. Over time this will build up a history of what I&amp;rsquo;ve brewed, how I brewed it and how much I use the brewing gear I&amp;rsquo;ve accumulated over time. The image below shows the detail page for a specific brew:&lt;/p&gt;
&lt;p&gt;&lt;a href="04.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/04_hu_50bbb0b2b6963101.webp 330w,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/04_hu_35a1e01fcdddafaf.webp 660w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/04_hu_29bb4eaed781336a.webp 1024w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/04_hu_e31901b84c0d9203.webp 1130w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1130"
height="1340"
class="mx-auto my-0 rounded-md"
alt="detail of a specific brew in the application"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/04_hu_efb40addffa8ed5e.png" srcset="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/04_hu_2e550bb129e56e74.png 330w,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/04_hu_efb40addffa8ed5e.png 660w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/04_hu_305a0558504abcb0.png 1024w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/04.png 1130w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finally, brewlog tracks cafes I&amp;rsquo;ve visited, and &amp;ldquo;cups&amp;rdquo;, which are coffees I&amp;rsquo;ve enjoyed, but not brewed myself.&lt;/p&gt;
&lt;p&gt;The most fun part is the extensive stats page, which shows an interactive map of where my coffees are grown, roasted and drunk, as well as common flavour notes and brew times.&lt;/p&gt;
&lt;p&gt;&lt;a href="02.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/02_hu_808a259ff87d6ed8.webp 330w,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/02_hu_e2884e1516b66776.webp 660w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/02_hu_ac2b5c228a3d9507.webp 1024w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/02_hu_de544f83159c15c5.webp 1130w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1130"
height="1682"
class="mx-auto my-0 rounded-md"
alt="coffee stats - map view and consumption numbers"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/02_hu_12440ddec0cd6378.png" srcset="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/02_hu_342f11bff0c05800.png 330w,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/02_hu_12440ddec0cd6378.png 660w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/02_hu_2ce5ee59505317cb.png 1024w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/02.png 1130w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="llm-powered-bag-scanning" class="relative group"&gt;LLM-Powered Bag Scanning &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#llm-powered-bag-scanning" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;Roastguide has a nice feature that allows you to take a picture of a bag of coffee and it&amp;rsquo;ll fetch the details. If my understanding is correct, their implementation relies on a database of roasters and coffees that the creators maintain. My app wouldn&amp;rsquo;t have access to that database, nor did I want to spend too much time building ingestion pipelines to store lots of data about all the possible coffees/roasters.&lt;/p&gt;
&lt;p&gt;In brewlog, I implemented this feature using LLM extraction: I take a photo and send it to a multi-modal model hosted by &lt;a href="https://openrouter.ai/" target="_blank" rel="noreferrer"&gt;OpenRouter&lt;/a&gt;. The &lt;a href="https://github.com/jnsgruk/brewlog/blob/edffb1679db3f17a8ed8e47735e8e1aff137e117/src/infrastructure/ai.rs#L11-L28" target="_blank" rel="noreferrer"&gt;prompt&lt;/a&gt; instructs the model to extract text from the image, perform a web search, and return a JSON object containing details of the coffee or roaster. OpenRouter enables me to switch models easily without worrying about API differences.&lt;/p&gt;
&lt;p&gt;In practice this works surprisingly well. Most speciality coffee bags are covered in useful information, and vision models are good at reading it. The main failure modes I see are mixing up roast names from the web and occasionally inventing tasting notes, but the process includes a review step which makes those cheap to correct.&lt;/p&gt;
&lt;p&gt;The cost is almost negligible per scan. I&amp;rsquo;ve been using &lt;a href="https://openrouter.ai/google/gemini-2.5-flash" target="_blank" rel="noreferrer"&gt;&lt;code&gt;google/gemini-2.5-flash&lt;/code&gt;&lt;/a&gt; for the LLM extraction feature, which results in a cost of around $0.01 - $0.02 per scan.&lt;/p&gt;
&lt;p&gt;&lt;a href="03.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/03_hu_6001b8e6d92a665d.webp 330w,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/03_hu_8b28c8a2ed6b8f2.webp 660w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/03_hu_3f53612e16cc5157.webp 1024w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/03_hu_e04a32d09fe6bcd4.webp 1203w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1203"
height="969"
class="mx-auto my-0 rounded-md"
alt="adding a roaster with the option of fetching details using an llm with web search tool"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/03_hu_c42d5bcb268c69d3.png" srcset="https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/03_hu_b8fcf892c9d1ebc0.png 330w,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/03_hu_c42d5bcb268c69d3.png 660w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/03_hu_9b633d69b01861f.png 1024w
,https://jnsgr.uk/2026/03/brewlog-coffee-and-agents/03.png 1203w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="design-decisions" class="relative group"&gt;Design Decisions &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#design-decisions" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;h3 id="backend" class="relative group"&gt;Backend &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#backend" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;Brewlog is built with Rust and &lt;a href="https://github.com/tokio-rs/axum" target="_blank" rel="noreferrer"&gt;Axum&lt;/a&gt; for the backend. I&amp;rsquo;ve been &lt;a href="https://jnsgr.uk/2024/12/experiments-with-rust-nix-k6-parca/" target="_blank" rel="noreferrer"&gt;learning Rust&lt;/a&gt; for a while now, and wanted to push further with a more substantial project. Templates are handled by &lt;a href="https://github.com/djc/askama" target="_blank" rel="noreferrer"&gt;Askama&lt;/a&gt;, whose compile‑time templates worked well with Claude because template errors surface as Rust compiler errors, which are picked up and fixed by the agent automatically.&lt;/p&gt;
&lt;p&gt;The database is &lt;a href="https://www.sqlite.org/" target="_blank" rel="noreferrer"&gt;SQLite&lt;/a&gt;. A single file, easy to back up, easy to move around. For a single-user application like this, SQLite is more than sufficient and removes the need for a separate database server. &lt;a href="https://github.com/jnsgruk/brewlog/tree/main/migrations" target="_blank" rel="noreferrer"&gt;Migrations&lt;/a&gt; are embedded in the binary and &lt;a href="https://github.com/jnsgruk/brewlog/blob/edffb1679db3f17a8ed8e47735e8e1aff137e117/src/infrastructure/database.rs#L38" target="_blank" rel="noreferrer"&gt;run automatically&lt;/a&gt; on application startup.&lt;/p&gt;
&lt;h3 id="frontend" class="relative group"&gt;Frontend &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#frontend" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;I wanted the app to feel modern, but keep much of the rendering server-side. I didn&amp;rsquo;t want to be serving a large client-side Javascript single-page application. I&amp;rsquo;ve been curious about the likes of &lt;a href="https://htmx.org/" target="_blank" rel="noreferrer"&gt;HTMX&lt;/a&gt;, and the observations made in a post from last year on &lt;a href="https://www.lorenstew.art/blog/eta-htmx-lit-stack" target="_blank" rel="noreferrer"&gt;building apps with Eta, HTMX and Lit&lt;/a&gt; really resonated with me.&lt;/p&gt;
&lt;p&gt;More recently I discovered &lt;a href="https://data-star.dev/" target="_blank" rel="noreferrer"&gt;Datastar&lt;/a&gt;, which has similar goals to &lt;a href="https://htmx.org/" target="_blank" rel="noreferrer"&gt;HTMX&lt;/a&gt;, but it&amp;rsquo;s newer and results in cleaner, simpler templates. Where HTMX swaps HTML fragments, Datastar adds a reactive signal system and can patch both HTML and JSON data from the server.&lt;/p&gt;
&lt;p&gt;This was a bit of a test for Claude Code, since Datastar was new enough that it likely didn&amp;rsquo;t feature in the training data for the model, but its ability to read and digest the documentation was quite startling.&lt;/p&gt;
&lt;p&gt;While occasionally the agent regressed to vanilla &lt;code&gt;fetch&lt;/code&gt; calls, or manual Javascript DOM manipulation, I treated that as a hole in my instructions, not the model. Each time it happened I &lt;a href="https://github.com/jnsgruk/brewlog/blob/main/CLAUDE.md#gotchas" target="_blank" rel="noreferrer"&gt;updated&lt;/a&gt; CLAUDE.md to reinforce the Datastar patterns and prevent the agent making the same mistake again.&lt;/p&gt;
&lt;h3 id="single-user-passkeys-only" class="relative group"&gt;Single User, Passkeys Only &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#single-user-passkeys-only" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;Brewlog is deliberately single-user. I&amp;rsquo;m happy for people to self-host their own instance, but I don&amp;rsquo;t have much interest in providing a service more widely. The codebase is structured such that making it multi-user wouldn&amp;rsquo;t be a particularly complex task, but I&amp;rsquo;m not convinced I&amp;rsquo;ll ever do it.&lt;/p&gt;
&lt;p&gt;Perhaps unusually, authentication via username and password is not supported. Authentication is only supported using passkeys via &lt;a href="https://webauthn.io/" target="_blank" rel="noreferrer"&gt;WebAuthn&lt;/a&gt; or with an API token.&lt;/p&gt;
&lt;p&gt;Passkeys are phishing-resistant and significantly more difficult to brute-force both from an academic and a practical standpoint, which removes an entire class of abuse vector for my service.&lt;/p&gt;
&lt;h3 id="comprehensive-cli-client" class="relative group"&gt;Comprehensive CLI Client &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#comprehensive-cli-client" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;One of the many things I&amp;rsquo;ve learned from &lt;a href="https://github.com/niemeyer" target="_blank" rel="noreferrer"&gt;@niemeyer&lt;/a&gt; during my time at Canonical is to ensure that when new API endpoints are added to such applications, a corresponding CLI command or flag lands at the same time.&lt;/p&gt;
&lt;p&gt;He also illustrated to me the power of shipping a single binary that is both a server, and a client to itself. The brewlog binary runs the server that hosts the API and web UI, but also serves as a first-class API client for the app.&lt;/p&gt;
&lt;p&gt;If you’re building an API‑backed app, I highly recommend this pattern. It keeps the surface area small and gives you (and an agent&amp;hellip;) a first‑class, &lt;a href="https://github.com/jnsgruk/brewlog/blob/main/scripts/bootstrap-db.sh" target="_blank" rel="noreferrer"&gt;scriptable&lt;/a&gt; way to exercise the API and troubleshoot problems in data transformation.&lt;/p&gt;
&lt;h3 id="domain-driven-design" class="relative group"&gt;Domain-Driven Design &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#domain-driven-design" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;The codebase follows a domain-driven design approach with four layers: domain (business logic only, no external dependencies), infrastructure (database, HTTP clients, third-party APIs), application (HTTP server, routes, middleware, services), and presentation (CLI commands and web view models):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌─────────────────────────────────────────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ Presentation │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ┌─────────────────────────┐ ┌──────────────────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ CLI │ │ Web │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ roasters, roasts, bags │ │ views, templates │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ brews, cups, gear ... │ │ roasters, roasts, bags │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └─────────────────────────┘ └──────────────────────────┘ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├─────────────────────────────────────────────────────────────┤
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ Application │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ┌──────────────┐ ┌──────────────────┐ ┌─────────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ Routes │ │ Services │ │ Server / State │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ /api/* │ │ BagService │ │ Axum router │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ /app/* │ │ BrewService │ │ AppState (DI) │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ │ │ RoastService .. │ │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └──────────────┘ └──────────────────┘ └─────────────────┘ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├─────────────────────────────────────────────────────────────┤
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ Domain (pure Rust — no framework dependencies) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ┌──────────────────────┐ ┌─────────────────────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ Entities &amp;amp; Values │ │ Repository Traits │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ coffee/ roasters, │ │ trait RoasterRepository │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ roasts, bags, │ │ trait RoastRepository │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ brews, cups, gear │ │ trait BagRepository ... │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ auth/ users, │ │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ sessions, tokens │ │ Errors, IDs, Listing, │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ analytics/ │ │ Formatting │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └──────────────────────┘ └─────────────────────────────┘ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├──────────────────────────────────┬──────────────────────────┤
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ Infrastructure │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ┌────────────────────────────┐ │ ┌───────────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ Repositories (SQLite) │ │ │ External Clients │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ SqlRoasterRepository │ │ │ OpenRouter (AI) │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ SqlRoastRepository │ │ │ Foursquare │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ SqlBagRepository ... │ │ │ WebAuthn │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ (implement domain traits) │ │ │ Backup │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └────────────────────────────┘ │ └───────────────────┘ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└──────────────────────────────────┴──────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In this model, dependencies flow inward. The domain layer knows nothing about Axum, SQLite, or any other implementation detail. Repository traits are defined in the domain and implemented in the infrastructure layer. This creates flexibility in the future if, for example, I want to move to PostgreSQL rather than SQLite, or if I want to change how I store images, etc.&lt;/p&gt;
&lt;p&gt;It also simplifies testing. The domain-driven design approach encourages loose coupling and practices like dependency injection, which usually lead to simpler integration tests. For example, I could ask the agent for focused tests against pure domain logic without pulling in Axum or SQLite.&lt;/p&gt;
&lt;h2 id="agentic-coding-patterns" class="relative group"&gt;Agentic Coding Patterns &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#agentic-coding-patterns" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;A big motivation for this project was to learn some new technology and skills, and it certainly did that, but it also reinforced some patterns I&amp;rsquo;ve been using for a while now.&lt;/p&gt;
&lt;h3 id="pre-commit-hooks-as-a-contract" class="relative group"&gt;Pre-commit hooks as a contract &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#pre-commit-hooks-as-a-contract" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;I&amp;rsquo;ve never been too fond of &lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks" target="_blank" rel="noreferrer"&gt;pre-commit hooks&lt;/a&gt;. I&amp;rsquo;ve always been disciplined in using formatters and linters in projects, but never enjoyed having them set up to run automatically. This changed when I started working with agents.&lt;/p&gt;
&lt;p&gt;I treat &lt;code&gt;pre‑commit&lt;/code&gt; as a contract between me and the agent. The agent is instructed to always fix linting/testing failures before returning to me for input or declaring success. In particular, I&amp;rsquo;ve been using &lt;a href="https://prek.j178.dev/" target="_blank" rel="noreferrer"&gt;prek&lt;/a&gt;, which is a Rust rewrite of &lt;a href="https://pre-commit.com/" target="_blank" rel="noreferrer"&gt;pre-commit&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="stricter-lints-for-agents" class="relative group"&gt;Stricter lints for agents &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#stricter-lints-for-agents" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;With agents, I&amp;rsquo;m also more inclined toward stricter, more pedantic lints to enforce properties like maximum line count in a function or file. When I&amp;rsquo;m writing code myself I make these changes instinctively. It&amp;rsquo;s obvious when a function becomes cumbersome when you&amp;rsquo;re editing by hand, but I noticed early on that an agent seems much more comfortable with 100+ line functions than I am.&lt;/p&gt;
&lt;p&gt;If you care about such constraints (function length, etc.), encode them as lints rather than hoping the agent internalises your preferences.&lt;/p&gt;
&lt;p&gt;As an aside, I&amp;rsquo;d never used &lt;a href="https://flake.parts" target="_blank" rel="noreferrer"&gt;flake-parts&lt;/a&gt; before, but its ability to automatically configure pre-commit hooks and formatting tools like &lt;a href="https://treefmt.com/latest/" target="_blank" rel="noreferrer"&gt;treefmt&lt;/a&gt; in a Nix dev shell is really slick.&lt;/p&gt;
&lt;h3 id="self-updating-instructions" class="relative group"&gt;Self-updating instructions &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#self-updating-instructions" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;I hadn’t previously realised how good these tools are at writing their own instructions. I had been confused in the past at the length/complexity of &lt;code&gt;AGENTS.md&lt;/code&gt;/&lt;code&gt;CLAUDE.md&lt;/code&gt; files I&amp;rsquo;d seen, not realising that when you work with an agent to solve a problem, or remedy something it did, you can then prompt it to summarise what just happened in the &lt;code&gt;CLAUDE.md&lt;/code&gt; file to prevent it from happening again.&lt;/p&gt;
&lt;h3 id="plan-mode" class="relative group"&gt;Plan Mode &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#plan-mode" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;To begin with I made extensive use of &amp;ldquo;Plan Mode&amp;rdquo;. The premise is that the agent can explore the codebase, read docs online and work with you to plan new features, but it cannot make changes to the code or your system.&lt;/p&gt;
&lt;p&gt;This had been working well for me, but there were a couple of occasions where it seemed to get stuck in a &amp;ldquo;doom loop&amp;rdquo; trying to plan its way out of a complex failure.&lt;/p&gt;
&lt;p&gt;On one particular occasion, this lasted for over an hour. I then switched to &amp;ldquo;Agent Mode&amp;rdquo;, gave it a follow-up prompt that instructed it to inspect a running application on Kubernetes directly using &lt;code&gt;kubectl&lt;/code&gt;, and it was able to diagnose and solve the same problem in around 5 minutes. The problem here was that some &amp;ldquo;tools&amp;rdquo; are explicitly not permitted in &amp;ldquo;Plan Mode&amp;rdquo;, but depending on what you&amp;rsquo;re trying to achieve that can sometimes be a significant hindrance.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been favouring a different approach where I remain in &amp;ldquo;Agent Mode&amp;rdquo;, and prompt the agent that I&amp;rsquo;d like to work on a plan in a shared document (e.g. &lt;code&gt;./plans/new-feature.md&lt;/code&gt;), and that it may only edit that file until I say otherwise. I then work in a loop with the Agent where it writes a plan, I leave comments in-line in the file, and then it takes that into account - and repeat until I&amp;rsquo;m happy. Before asking the agent to implement the plan, I start a new session and ask it to implement the plan with fresh context. So far I&amp;rsquo;ve found this to be really effective.&lt;/p&gt;
&lt;h3 id="worktrees-and-parallel-agents" class="relative group"&gt;Worktrees and Parallel Agents &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#worktrees-and-parallel-agents" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;Pretty soon after starting work on more complicated features, I became interested in running parallel agents. Sometimes an agent can take a long time to work through a well-developed, and in that time I often felt I could be working my way through other items on my roadmap.&lt;/p&gt;
&lt;p&gt;I initially tried to just run them concurrently on the same branch, but predictably they ended up fighting and making a bit of a mess. I&amp;rsquo;ve long been a fan of git &lt;a href="https://git-scm.com/docs/git-worktree" target="_blank" rel="noreferrer"&gt;worktrees&lt;/a&gt;, and they&amp;rsquo;re a perfect fit here - enabling me to run concurrent agents on separate instances of the codebase.&lt;/p&gt;
&lt;p&gt;Even with worktrees, I still tend to work on orthogonal concerns so I don&amp;rsquo;t end up with big merge conflicts, but being able to work on a frontend feature, a backend feature and the CI pipeline all at once is a nice upgrade.&lt;/p&gt;
&lt;h3 id="visual-prompting" class="relative group"&gt;Visual prompting &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#visual-prompting" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;When working on frontend components, I built a nice workflow around screenshotting the UI, marking it up with &lt;a href="https://flameshot.org/" target="_blank" rel="noreferrer"&gt;Flameshot&lt;/a&gt;, then pasting the image back into Claude Code with the next prompt.&lt;/p&gt;
&lt;p&gt;I found this to be a really effective way to drive iteration on web components. It felt closer to pair‑designing with a human: I’d circle spacing issues or misaligned elements, and the agent would propose CSS tweaks in response to what it saw.&lt;/p&gt;
&lt;h2 id="summary" class="relative group"&gt;Summary &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#summary" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;I had a blast building Brewlog, and I&amp;rsquo;ve been using it every day for about a month now. It&amp;rsquo;s different from how I&amp;rsquo;ve approached projects in the past, in a way that&amp;rsquo;s both liberating and terrifying in equal measures.&lt;/p&gt;
&lt;p&gt;While some of the code in this project lacks the same attention to detail I might have applied, I likely would never have got around to building it without an agent.&lt;/p&gt;
&lt;p&gt;I was surprised what the process taught me. Every day I hacked on Brewlog with Claude Code, I learned something. Whether that was something about Tailwind CSS, or an architectural pattern.&lt;/p&gt;
&lt;p&gt;I think the key to useful output from these tools is that you remain the driver. You&amp;rsquo;re still responsible what you ship, which means giving the generated code the appropriate amount of review.&lt;/p&gt;
&lt;p&gt;Brewlog is live at &lt;a href="https://coffee.jnsgr.uk" target="_blank" rel="noreferrer"&gt;coffee.jnsgr.uk&lt;/a&gt; and runs comfortably on a free-tier instance on &lt;a href="https://fly.io/" target="_blank" rel="noreferrer"&gt;fly.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you’re into speciality coffee and like owning your data, give it a try and let me know how you get on.&lt;/p&gt;
&lt;p&gt;Finally, I&amp;rsquo;d like to thank the authors of Roastguide for the amazing work they&amp;rsquo;ve done on their app, the attention they pay to their community on Discord, and the fact that they were willing to get me a dump of my data I could use to populate the first deployment of Brewlog!&lt;/p&gt;</description></item><item><title>Developing with AI on Ubuntu</title><link>https://jnsgr.uk/2026/01/developing-with-ai-on-ubuntu/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2026/01/developing-with-ai-on-ubuntu/</guid><description>&lt;blockquote&gt;
&lt;p&gt;This article was originally posted &lt;a href="https://discourse.ubuntu.com/t/developing-with-ai-on-ubuntu/75299" target="_blank" rel="noreferrer"&gt;on the Ubuntu Discourse&lt;/a&gt;, and is reposted here. I welcome comments and further discussion in that thread.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;AI-assisted tooling is becoming more and more common in the workflows of engineers at all experience levels. As I see it, our challenge is one of consideration, enablement and constraint. We must enable those who opt-in to safely and responsibly harness the power of these tools, while respecting those who do not wish to have their platform defined or overwhelmed by this class of software.&lt;/p&gt;
&lt;p&gt;The use of AI is a divisive topic among the tech community. I find myself a little in both camps, somewhere between sceptic and advocate. While I&amp;rsquo;m quick to acknowledge the negative impacts that the use of LLMs &lt;em&gt;can have&lt;/em&gt; on open source projects, I&amp;rsquo;m also surrounded by examples where it has been used responsibly to great effect.&lt;/p&gt;
&lt;p&gt;Examples of this include &lt;a href="https://filippo.io" target="_blank" rel="noreferrer"&gt;Filippo&lt;/a&gt;&amp;rsquo;s article &lt;a href="https://words.filippo.io/claude-debugging/" target="_blank" rel="noreferrer"&gt;debugging low-level cryptography with Claude Code&lt;/a&gt;, &lt;a href="https://mitchellh.com" target="_blank" rel="noreferrer"&gt;Mitchell&lt;/a&gt;&amp;rsquo;s article on &lt;a href="https://mitchellh.com/writing/non-trivial-vibing" target="_blank" rel="noreferrer"&gt;Vibing a Non-Trivial Ghostty Feature&lt;/a&gt;, and &lt;a href="https://github.com/crawshaw" target="_blank" rel="noreferrer"&gt;David&lt;/a&gt;&amp;rsquo;s article &lt;a href="https://crawshaw.io/blog/programming-with-agents" target="_blank" rel="noreferrer"&gt;How I Program with Agents&lt;/a&gt;. These articles come from engineers with proven expertise in careful, precise software engineering, yet they share an important sentiment: AI-assisted tools can be a remarkable force-multiplier when used &lt;em&gt;in conjunction&lt;/em&gt; with their lived experience, but care must still be taken to avoid poor outcomes.&lt;/p&gt;
&lt;p&gt;The aim of this post is not to convince you to use AI in your work, but rather to introduce the elements of Ubuntu that make it a first-class platform for safe, efficient experimentation and development. My goals for AI and Ubuntu are currently focused on enabling those who want to develop responsibly with AI tools, without negatively impacting the experience of those who&amp;rsquo;d prefer not to opt-in.&lt;/p&gt;
&lt;h3 id="hardware--drivers" class="relative group"&gt;Hardware &amp;amp; Drivers &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#hardware--drivers" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;AI-specific silicon is moving just as fast as AI software tooling, and without constant work to integrate drivers and userspace tools into Ubuntu, it would be impossible to efficiently utilise this specialised hardware.&lt;/p&gt;
&lt;p&gt;Last year we announced that we will ship both &lt;a href="https://canonical.com/blog/canonical-announces-it-will-support-and-distribute-nvidia-cuda-in-ubuntu" target="_blank" rel="noreferrer"&gt;NVIDIA&amp;rsquo;s CUDA&lt;/a&gt; and &lt;a href="https://canonical.com/blog/canonical-amd-rocm-ai-ml-hpc-libraries" target="_blank" rel="noreferrer"&gt;AMD&amp;rsquo;s ROCm&lt;/a&gt; in the Ubuntu archive for Ubuntu 26.04 LTS, in addition to our previous work on &lt;a href="https://snapcraft.io/publisher/openvino" target="_blank" rel="noreferrer"&gt;OpenVINO&lt;/a&gt;. This will make installing the latest drivers and toolkits easier and more secure, with no third-party software repositories. Distributing this software as part of Ubuntu enables us to be proactive in the delivery of security updates and the demonstration of provenance.&lt;/p&gt;
&lt;p&gt;Our work is not limited to AMD and NVIDIA; we recently &lt;a href="https://canonical.com/blog/ubuntu-ga-for-qualcomm-dragonwing" target="_blank" rel="noreferrer"&gt;announced&lt;/a&gt; support for Qualcomm&amp;rsquo;s &lt;a href="https://www.qualcomm.com/dragonwing" target="_blank" rel="noreferrer"&gt;Dragonwing&lt;/a&gt; platforms and others. You can read more about our silicon partner projects &lt;a href="https://canonical.com/partners/silicon" target="_blank" rel="noreferrer"&gt;on our website&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="inference-snaps" class="relative group"&gt;Inference Snaps &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#inference-snaps" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;At the &lt;a href="https://ubuntu.com/summit" target="_blank" rel="noreferrer"&gt;Ubuntu Summit 25.10&lt;/a&gt;, we &lt;a href="https://canonical.com/blog/canonical-releases-inference-snaps" target="_blank" rel="noreferrer"&gt;released&lt;/a&gt; &amp;ldquo;Inference Snaps&amp;rdquo; into the wild, which provide a hassle-free mechanism for obtaining the “famous model” you want to work with, but automatically receive a version of that model which is optimised for the silicon in your machine. This removes the need to spend hours on &lt;a href="https://huggingface.co/" target="_blank" rel="noreferrer"&gt;HuggingFace&lt;/a&gt; identifying the correct model to download that matches with your hardware, and obviates the need for in-depth understanding of model quantisation and tuning when getting started.&lt;/p&gt;
&lt;p&gt;Each of our inference snaps provide a consistent experience: you need only learn the basics once, but can apply those skills to different models as they emerge, whether you&amp;rsquo;re on a laptop or a server.&lt;/p&gt;
&lt;p&gt;At the time of writing, we&amp;rsquo;ve published &lt;code&gt;beta&lt;/code&gt; quality snaps for &lt;a href="https://snapcraft.io/qwen-vl" target="_blank" rel="noreferrer"&gt;qwen-vl&lt;/a&gt;, &lt;a href="https://snapcraft.io/deepseek-r1" target="_blank" rel="noreferrer"&gt;deepseek-r1&lt;/a&gt; and &lt;a href="https://snapcraft.io/gemma3" target="_blank" rel="noreferrer"&gt;gemma3&lt;/a&gt;. You can find a current list of snaps &lt;a href="https://documentation.ubuntu.com/inference-snaps/reference/snaps/" target="_blank" rel="noreferrer"&gt;in the documentation&lt;/a&gt;, along with the silicon-optimised variants.&lt;/p&gt;
&lt;h3 id="sandboxing-agents" class="relative group"&gt;Sandboxing Agents &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sandboxing-agents" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;While many start their journey in a web browser chatting to &lt;a href="https://chat.com" target="_blank" rel="noreferrer"&gt;ChatGPT&lt;/a&gt;, &lt;a href="https://claude.ai" target="_blank" rel="noreferrer"&gt;Claude&lt;/a&gt;, &lt;a href="https://gemini.google.com/app" target="_blank" rel="noreferrer"&gt;Gemini&lt;/a&gt;, &lt;a href="https://perplexity.ai" target="_blank" rel="noreferrer"&gt;Perplexity&lt;/a&gt; or one of the myriad of alternatives, many developers will find &amp;ldquo;agentic&amp;rdquo; tools such as &lt;a href="https://github.com/features/copilot" target="_blank" rel="noreferrer"&gt;Copilot&lt;/a&gt;, &lt;a href="https://openai.com/codex/" target="_blank" rel="noreferrer"&gt;Codex&lt;/a&gt;, &lt;a href="https://claude.com/product/claude-code" target="_blank" rel="noreferrer"&gt;Claude Code&lt;/a&gt; or &lt;a href="https://ampcode.com/" target="_blank" rel="noreferrer"&gt;Amp&lt;/a&gt; quite attractive. In my experience, agents are a clear level-up in an LLM&amp;rsquo;s capability for developers, but they can still make poor decisions and are generally safer to run in sandboxed environment at the time of writing.&lt;/p&gt;
&lt;p&gt;Where a traditional chat-based AI tool responds reactively to user prompts within a single conversation, an agent operates (semi-)autonomously to pursue goals. It perceives its environment, plans, makes decisions and can call out to external tools and services to achieve those goals. If you grant permission, an agent can read and understand your code, implement features, troubleshoot bugs, optimise performance and many other tasks. The catch is that they often need &lt;em&gt;access to your system&lt;/em&gt; - whether that be to modify files or run commands.&lt;/p&gt;
&lt;p&gt;Issues such as accidental file deletion, or the inclusion of a spurious (and potentially compromised) dependency are an inevitable failure mode of the current generation of agents due to how they&amp;rsquo;re trained (see the &lt;a href="https://www.reddit.com/r/ClaudeAI/comments/1pgxckk/claude_cli_deleted_my_entire_home_directory_wiped/" target="_blank" rel="noreferrer"&gt;Reddit post&lt;/a&gt; about Claude Code deleting a user&amp;rsquo;s home directory).&lt;/p&gt;
&lt;h4 id="my-agent-sandboxes-itself" class="relative group"&gt;My agent sandboxes itself! &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#my-agent-sandboxes-itself" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;Some of you will be reading this wondering why additional sandboxing is required, since many of the popular agents &lt;a href="https://code.claude.com/docs/en/sandboxing" target="_blank" rel="noreferrer"&gt;advertise their own sandboxing&lt;/a&gt;. The fact that some agents include some measures to protect the user&amp;rsquo;s machine is of course a good thing. The touted benefits include filesystem isolation by restricting the agent to a specific directory, or prompting for approval before modifying files. Some agents also include network sandboxing to restrict network access to a list of approved domains, or by using a custom proxy to impose rules on outbound traffic.&lt;/p&gt;
&lt;p&gt;On Linux, these agent-imposed sandboxes are often implemented with &lt;a href="https://github.com/containers/bubblewrap" target="_blank" rel="noreferrer"&gt;bubblewrap&lt;/a&gt;, which is &amp;ldquo;a tool for constructing sandbox environments&amp;rdquo;, but note that the upstream project&amp;rsquo;s README includes &lt;a href="https://github.com/containers/bubblewrap#sandbox-security" target="_blank" rel="noreferrer"&gt;a section&lt;/a&gt; which states that it is &lt;em&gt;not&lt;/em&gt; a &amp;ldquo;ready-made sandbox with a specific security policy&amp;rdquo;. &lt;code&gt;bubblewrap&lt;/code&gt; is a relatively low-level tool that must be given its configuration, which in this case is provided &lt;em&gt;by the agent&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The limitation upon these tools is the shared kernel - a severe kernel exploit could enable an agent to escape from its sandbox. Of course, such vulnerabilities are rare, but note that even if the sandboxing technologies do their job, agents often run in the context of the user&amp;rsquo;s session, meaning they inherit environment variables which could contain sensitive information. They&amp;rsquo;re also agent specific: Claude Code&amp;rsquo;s sandboxing won&amp;rsquo;t help you if you&amp;rsquo;re using &lt;a href="https://cursor.com/" target="_blank" rel="noreferrer"&gt;Cursor&lt;/a&gt; or &lt;a href="https://antigravity.google/" target="_blank" rel="noreferrer"&gt;Antigravity&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Depending on your threat model and the project you&amp;rsquo;re working on, you may deem the built-in sandboxing of coding agents to be sufficient, but there are other options available to Ubuntu users that provide either different, or additional protection&amp;hellip;&lt;/p&gt;
&lt;h4 id="sandbox-with-lxd-containers" class="relative group"&gt;Sandbox with LXD containers &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sandbox-with-lxd-containers" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;Canonical&amp;rsquo;s &lt;a href="https://canonical.com/lxd" target="_blank" rel="noreferrer"&gt;LXD&lt;/a&gt; works out-of-the-box on Ubuntu, and is a great way to sandbox an agent into a disposable environment where the blast radius is limited should the agent make a mistake. My personal workflow is to create an Ubuntu container (or VM) with my project directory mounted. This way, I can edit my code directly on my filesystem with my preferred (already configured) editor, but have the agent run inside the container.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Initialise the container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc init ubuntu:noble dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Mount my project directory into the container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc config device add -q dev datadir disk &lt;span class="nv"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/my-project&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/ubuntu/project
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Start the container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc start dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Get a shell inside the container as the &amp;#39;ubuntu&amp;#39; user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc &lt;span class="nb"&gt;exec&lt;/span&gt; dev -- sudo -u ubuntu -i bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run a command in the container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc &lt;span class="nb"&gt;exec&lt;/span&gt; dev -- sudo -u ubuntu -i bash -c &lt;span class="s2"&gt;&amp;#34;cd project; claude&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can learn more about LXD in the official &lt;a href="https://documentation.ubuntu.com/lxd/stable-5.21/" target="_blank" rel="noreferrer"&gt;documentation&lt;/a&gt; and &lt;a href="https://documentation.ubuntu.com/lxd/stable-5.21/tutorial/first_steps/#first-steps" target="_blank" rel="noreferrer"&gt;tutorial&lt;/a&gt;, as well as specific instructions on &lt;a href="https://ubuntu.com/tutorials/gpu-data-processing-inside-lxd#1-overview" target="_blank" rel="noreferrer"&gt;enabling GPU data processing in containers/VMs&lt;/a&gt;. I&amp;rsquo;ve written &lt;a href="https://jnsgr.uk/2024/06/desktop-vms-lxd-multipass/" target="_blank" rel="noreferrer"&gt;previously&lt;/a&gt; about my use of LXD in development.&lt;/p&gt;
&lt;p&gt;With LXD, you can choose between running your sandbox as a container or a VM, depending on your project&amp;rsquo;s needs. If I&amp;rsquo;m working on a project that requires Kubernetes or similar, I use a VM, but for lighter projects I use system containers, preferring their lower overhead.&lt;/p&gt;
&lt;h4 id="sandbox-with-lxd-vms" class="relative group"&gt;Sandbox with LXD VMs &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sandbox-with-lxd-vms" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;LXD is best known for its ability to run &amp;ldquo;system containers&amp;rdquo;, which are somewhat analogous to Docker/OCI containers, but rather than being focused on a single application (and dependencies), a system container essentially runs an entire Ubuntu user-space (including &lt;code&gt;systemd&lt;/code&gt;, etc.). Like OCI containers, however, system containers share the kernel with the host.&lt;/p&gt;
&lt;p&gt;In some situations, you may seek more isolation from your host machine by running tools inside a virtual machine with their own kernel. LXD makes this simple - you can follow the same commands as above, but add &lt;code&gt;--vm&lt;/code&gt; to the &lt;code&gt;init&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Initialise the virtual machine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc init --vm ubuntu:noble dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can also configure the virtual machine&amp;rsquo;s CPU, memory and disk requirements. A simple example is below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc init --vm ubuntu:noble dev &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -c limits.cpu&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -c limits.memory&lt;span class="o"&gt;=&lt;/span&gt;8GiB &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d root,size&lt;span class="o"&gt;=&lt;/span&gt;100GiB
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can find more details on instance configuration in the &lt;a href="https://documentation.ubuntu.com/lxd/stable-5.21/howto/instances_configure/" target="_blank" rel="noreferrer"&gt;LXD documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="sandbox-with-multipass" class="relative group"&gt;Sandbox with Multipass &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sandbox-with-multipass" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;&lt;a href="https://multipass.run/" target="_blank" rel="noreferrer"&gt;Multipass&lt;/a&gt; provides on-demand access to Ubuntu VMs from any workstation - whether that workstation is running Linux, macOS or Windows. It is designed to replicate, in a lightweight way, the experience of provisioning a simple Ubuntu VM on a cloud.&lt;/p&gt;
&lt;p&gt;Multipass&amp;rsquo; scope is more limited than LXD, but for many users it provides a simple on-ramp for development with Ubuntu. Where it lacks advanced features like GPU passthrough, it boasts a simplified CLI and a first-class &lt;a href="https://documentation.ubuntu.com/multipass/latest/reference/gui-client/" target="_blank" rel="noreferrer"&gt;GUI client&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To get started similarly to the LXD example above, try the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install Multipass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo snap install multipass
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Launch an instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;multipass launch noble -n dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Mount your project directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;multipass mount ~/my-project dev:/home/ubuntu/project
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Get a shell in the instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;multipass shell dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run a command in the instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;multipass &lt;span class="nb"&gt;exec&lt;/span&gt; dev -- claude
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can find more details on how to configure and manage instances &lt;a href="https://documentation.ubuntu.com/multipass/latest/" target="_blank" rel="noreferrer"&gt;in the docs&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="sandbox-with-wsl" class="relative group"&gt;Sandbox with WSL &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sandbox-with-wsl" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;If you&amp;rsquo;re on Windows, &lt;a href="https://documentation.ubuntu.com/wsl/stable/tutorials/develop-with-ubuntu-wsl/" target="_blank" rel="noreferrer"&gt;development with WSL&lt;/a&gt; includes first-class &lt;a href="https://documentation.ubuntu.com/wsl/stable/howto/gpu-cuda/" target="_blank" rel="noreferrer"&gt;support for GPU acceleration&lt;/a&gt;, and is even supported for use with the &lt;a href="https://ubuntu.com/blog/accelerate-ai-development-with-ubuntu-and-nvidia-ai-workbench" target="_blank" rel="noreferrer"&gt;NVIDIA AI Workbench&lt;/a&gt;, &lt;a href="https://docs.nvidia.com/nim/wsl2/latest/getting-started.html" target="_blank" rel="noreferrer"&gt;NVIDIA NIM&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/windows/ai/directml/gpu-cuda-in-wsl" target="_blank" rel="noreferrer"&gt;CUDA&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ubuntu is the default Linux distribution for WSL, and you can find more information about how to set up and use Ubuntu on WSL in &lt;a href="https://documentation.ubuntu.com/wsl/stable/" target="_blank" rel="noreferrer"&gt;our documentation&lt;/a&gt;. WSL benefits from all the same technologies as a &amp;ldquo;regular&amp;rdquo; Ubuntu install, including the ability to use Snaps, Docker and LXD.&lt;/p&gt;
&lt;p&gt;For the enterprise developer, we recently announced &lt;a href="https://canonical.com/blog/canonical-announces-ubuntu-pro-for-wsl" target="_blank" rel="noreferrer"&gt;Ubuntu Pro for WSL&lt;/a&gt;, as well as the ability to manage WSL instances &lt;a href="https://documentation.ubuntu.com/landscape/how-to-guides/wsl-integration/manage-wsl-instances/" target="_blank" rel="noreferrer"&gt;using Landscape&lt;/a&gt;, making it easier to get access to first-class developer tooling with Ubuntu on your corporate machine.&lt;/p&gt;
&lt;h3 id="summary" class="relative group"&gt;Summary &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#summary" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;While opinion remains divided on the value and impact of current AI tooling, its presence in modern development workflows and its demands on underlying compute infrastructure are difficult to ignore.&lt;/p&gt;
&lt;p&gt;Developers who wish to experiment need reliable access to modern hardware, predictable tooling, and strong isolation boundaries. Ubuntu’s role is not to dictate how these tools are used, but to provide a stable and dependable platform on which they can be explored and deployed safely, without compromising security, provenance, or the day-to-day experience of those who choose to opt out.&lt;/p&gt;
&lt;p&gt;In addition to powering development workflows, Ubuntu makes for a dependable production operating system for your workloads. We&amp;rsquo;re building &lt;a href="https://documentation.ubuntu.com/canonical-kubernetes/latest/" target="_blank" rel="noreferrer"&gt;Canonical Kubernetes&lt;/a&gt; with first-class GPU support, &lt;a href="https://canonical.com/mlops/kubeflow" target="_blank" rel="noreferrer"&gt;Kubeflow&lt;/a&gt; and &lt;a href="https://canonical.com/mlops/mlflow" target="_blank" rel="noreferrer"&gt;MLFlow&lt;/a&gt; for model training and serving and a suite of applications like &lt;a href="https://canonical.com/data/postgresql" target="_blank" rel="noreferrer"&gt;PostgreSQL&lt;/a&gt;, &lt;a href="https://canonical.com/data/mysql" target="_blank" rel="noreferrer"&gt;MySQL&lt;/a&gt;, &lt;a href="https://canonical.com/data/opensearch" target="_blank" rel="noreferrer"&gt;Opensearch&lt;/a&gt;, as well as other data-centric tools such as &lt;a href="https://canonical.com/data/kafka" target="_blank" rel="noreferrer"&gt;Kafka&lt;/a&gt; and &lt;a href="https://canonical.com/data/spark" target="_blank" rel="noreferrer"&gt;Spark&lt;/a&gt; that can be deployed with full &lt;a href="https://ubuntu.com/pro" target="_blank" rel="noreferrer"&gt;Ubuntu Pro&lt;/a&gt; support. Let me know if you&amp;rsquo;d find value in a follow-up post on those topics!&lt;/p&gt;</description></item><item><title>Ubuntu Summit 25.10: Personal Highlights</title><link>https://jnsgr.uk/2025/11/ubuntu-summit-25/</link><pubDate>Sun, 02 Nov 2025 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2025/11/ubuntu-summit-25/</guid><description>&lt;blockquote&gt;
&lt;p&gt;This article was originally posted &lt;a href="https://discourse.ubuntu.com/t/ubuntu-summit-25-10-personal-highlights/71509" target="_blank" rel="noreferrer"&gt;on the Ubuntu Discourse&lt;/a&gt;, and is reposted here. I welcome comments and further discussion in that thread.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I recently had the privilege of attending the &lt;a href="https://ubuntu.com/summit" target="_blank" rel="noreferrer"&gt;Ubuntu Summit 25.10&lt;/a&gt;. Ubuntu Summits have a relatively long history. Some years ago Canonical ran the ‘Ubuntu Developer Summits (UDS)’, but recently the events were brought back and reimagined as the ‘Ubuntu Summit’.&lt;/p&gt;
&lt;p&gt;For the most recent Summit, we tried out a new format. We invited a select few folks to come and give talks at our London office, with a small in-person crowd. In addition, the event was livestreamed, and we encouraged people to host &amp;ldquo;watching parties&amp;rdquo; across the world as part of &lt;a href="https://ubuntu.com/community/docs/locos?next=%2Fg1m%2F" target="_blank" rel="noreferrer"&gt;Ubuntu Local Communities (LoCos)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While Ubuntu may feature in the name, the event does not require talks to be centred on Ubuntu, and in fact is aiming to draw contributions from our partners and from right across the open source community, whether or not the content is relevant to Ubuntu or Canonical - it&amp;rsquo;s designed to be a showcase for the very best of open source, and this year I felt that the talks were of a particularly high calibre.&lt;/p&gt;
&lt;p&gt;In this post I&amp;rsquo;ll highlight some of my favourite talks, in no particular order! If any of these catch your interest, you can see &lt;a href="https://discourse.ubuntu.com/t/ubuntu-summit-25-10-timetable/65271" target="_blank" rel="noreferrer"&gt;when they were aired&lt;/a&gt; and catch-up on the &lt;a href="https://www.youtube.com/live/bEEamxJ60aI" target="_blank" rel="noreferrer"&gt;Day 1&lt;/a&gt; and &lt;a href="https://www.youtube.com/live/WvNgMEumSoA" target="_blank" rel="noreferrer"&gt;Day 2&lt;/a&gt; streams.&lt;/p&gt;
&lt;h2 id="doom-in-space" class="relative group"&gt;DOOM in Space &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#doom-in-space" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href="04.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/04_hu_65dfe195c8d0716f.webp 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/04_hu_9322be6f635856bb.webp 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/04_hu_9172933e609edc31.webp 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/04_hu_8b2cd4b205915e58.webp 1280w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1280"
height="720"
class="mx-auto my-0 rounded-md"
alt="opening slide for doom in space talk"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2025/11/ubuntu-summit-25/04_hu_2091739036a71cb.png" srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/04_hu_b9533f51793b46ca.png 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/04_hu_2091739036a71cb.png 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/04_hu_9ae56e6f039ce272.png 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/04.png 1280w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What a way to kick off the Summit! &lt;a href="https://discourse.ubuntu.com/t/doom-in-space/67019" target="_blank" rel="noreferrer"&gt;DOOM in Space&lt;/a&gt; was a talk given by &lt;a href="https://olafurw.com/aboutme/" target="_blank" rel="noreferrer"&gt;Ólafur Waage&lt;/a&gt;, who introduced himself as a &amp;ldquo;professional keyboard typist&amp;rdquo;!&lt;/p&gt;
&lt;p&gt;The talk was immediately after Mark Shuttleworth&amp;rsquo;s opening remarks, and covered his journey in getting DOOM to run on the European Space Agency&amp;rsquo;s &lt;a href="https://en.wikipedia.org/wiki/OPS-SAT" target="_blank" rel="noreferrer"&gt;OPS-SAT&lt;/a&gt; satellite. DOOM has famously been ported to &lt;a href="https://en.wikipedia.org/wiki/List_of_Doom_ports" target="_blank" rel="noreferrer"&gt;many devices&lt;/a&gt;, though some were only questionably &amp;ldquo;running&amp;rdquo; the game.&lt;/p&gt;
&lt;p&gt;Ólafur covered how he became involved in the project, and the unique approach they needed to take to guarantee success, since they would only get a very limited amount of time in order to conduct their &amp;ldquo;experiment&amp;rdquo; on the satellite.&lt;/p&gt;
&lt;p&gt;Of particular note was the work done to integrate imagery from the OPS-SAT&amp;rsquo;s onboard camera into the game, which involved some clever reassigning of colors in the game&amp;rsquo;s original palette to more faithfully represent the imagery taken from the camera in-game.&lt;/p&gt;
&lt;h2 id="infrastructure-wide-profiling-of-nvidia-cuda" class="relative group"&gt;Infrastructure-Wide Profiling of Nvidia CUDA &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#infrastructure-wide-profiling-of-nvidia-cuda" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href="03.jpeg"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/03_hu_c19c7f7a82ce28ec.webp 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/03_hu_3289cc8a3c6ace68.webp 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/03_hu_63ea02edaca8b2f8.webp 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/03_hu_47b446e14afc4dc7.webp 1320w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1600"
height="900"
class="mx-auto my-0 rounded-md"
alt="opening slide for profiling talk"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2025/11/ubuntu-summit-25/03_hu_a857ebebb22203c0.jpeg" srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/03_hu_ee47e997a3027b8c.jpeg 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/03_hu_a857ebebb22203c0.jpeg 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/03_hu_a29a7d039dd13cc1.jpeg 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/03_hu_cb8dc774efe7bf9f.jpeg 1320w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://discourse.ubuntu.com/t/infrastructure-wide-profiling-of-nvidia-cuda/67248" target="_blank" rel="noreferrer"&gt;This talk&lt;/a&gt; was given by &lt;a href="https://github.com/brancz" target="_blank" rel="noreferrer"&gt;Frederic Branczyk&lt;/a&gt;, CEO and Founder of &lt;a href="https://polarsignals.com" target="_blank" rel="noreferrer"&gt;Polar Signals&lt;/a&gt;. Canonical has partnered with Polar Signals a couple of times in recent years. They were part of our journey to &lt;a href="https://ubuntu.com/blog/ubuntu-performance-engineering-with-frame-pointers-by-default" target="_blank" rel="noreferrer"&gt;enabling frame pointers by default&lt;/a&gt; on Ubuntu, and many of our teams have been using their zero-instrumentation &lt;a href="https://github.com/parca-dev/parca-agent" target="_blank" rel="noreferrer"&gt;eBPF profiler&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While CPU profiling has been commonplace for developers for many years, giving the ability to analyse CPU and memory-bound workloads, profiling GPU workloads has been less prominent, and is particularly difficult in production.&lt;/p&gt;
&lt;p&gt;Polar Signals advocate for &amp;ldquo;continuous profiling&amp;rdquo;, which means running a profiler at all times, on all nodes, in production. The benefit of this is that when an issue occurs, you don&amp;rsquo;t have to set up a profiler and try to reproduce the issue - you already have the data. It also negates the uncertainty of the impact a profiler might have on the code during reproduction. This would have been difficult with traditional profiling tools, but with technologies like &lt;a href="https://ebpf.io/" target="_blank" rel="noreferrer"&gt;eBPF&lt;/a&gt;, the overhead of the profiler is incredibly low compared to the potential performance gains from acting on the data it produces.&lt;/p&gt;
&lt;p&gt;In this talk, Frederic outlined the work they have done bringing infrastructure-wide profiling of CUDA workloads into Polar Signals Cloud. Their approach combines the &lt;a href="https://docs.nvidia.com/cupti/" target="_blank" rel="noreferrer"&gt;CUPTI profiling API&lt;/a&gt; with &lt;a href="https://docs.ebpf.io/linux/concepts/usdt/" target="_blank" rel="noreferrer"&gt;USDT&lt;/a&gt; probes and eBPF into a pipeline, relying upon the ability to inject a small library into CUDA workloads using the &lt;code&gt;CUDA_INJECTION64_PATH&lt;/code&gt; without modification.&lt;/p&gt;
&lt;p&gt;You can see more details &lt;a href="https://www.polarsignals.com/blog/posts/2025/10/22/gpu-profiling" target="_blank" rel="noreferrer"&gt;on their website&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="inference-snaps" class="relative group"&gt;Inference Snaps &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#inference-snaps" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href="02.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/02_hu_1d018afaaf936f4c.webp 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/02_hu_633333fcdd4ed333.webp 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/02_hu_9d1bbcb6c1e5044a.webp 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/02_hu_ca0ce363eac0f419.webp 1280w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1280"
height="720"
class="mx-auto my-0 rounded-md"
alt="opening slide for inference snaps talk"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2025/11/ubuntu-summit-25/02_hu_d3e5811b82e6933.png" srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/02_hu_ebbc2e9e26d4db3.png 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/02_hu_d3e5811b82e6933.png 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/02_hu_711ebc18bd1dda97.png 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/02.png 1280w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This talk served as the first &lt;a href="https://canonical.com/blog/canonical-releases-inference-snaps" target="_blank" rel="noreferrer"&gt;public announcement&lt;/a&gt; of Inference Snaps from Canonical, which represents a few months of working combining many of the new technologies behind Snaps.&lt;/p&gt;
&lt;p&gt;As Large Language Models continue to gain pace along with the rest of the AI community, silicon manufacturers are increasingly including dedicated hardware in commodity CPUs and GPUs, as well as shipping dedicated accelerators for some workloads.&lt;/p&gt;
&lt;p&gt;AI models often need to be tuned in some way in order to work optimally - for example &lt;a href="https://huggingface.co/docs/optimum/en/concept_guides/quantization" target="_blank" rel="noreferrer"&gt;quantisation&lt;/a&gt; which aims to reduce the computational memory costs of running inference on a given model.&lt;/p&gt;
&lt;p&gt;Inference snaps provide a hassle-free mechanism for users to obtain the &amp;ldquo;famous model&amp;rdquo; they want to work with, but automatically receive a version of that model which is optimised for the silicon in their machine, removing the need to spend hours on HuggingFace trying to identify the correct model to download that matches with their hardware.&lt;/p&gt;
&lt;p&gt;Using our extensive partner network, we&amp;rsquo;ll continue to work with multiple silicon vendors to ensure that models are available for the latest hardware as it drops, and provide a consistent experience to Ubuntu users that wish to work with AI.&lt;/p&gt;
&lt;p&gt;Find out more in the &lt;a href="https://canonical.com/blog/canonical-releases-inference-snaps" target="_blank" rel="noreferrer"&gt;announcement&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="nøughty-linux-ubuntus-stability-meets-nixpkgs-freshness" class="relative group"&gt;Nøughty Linux: Ubuntu’s Stability Meets Nixpkgs’ Freshness &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#n%c3%b8ughty-linux-ubuntus-stability-meets-nixpkgs-freshness" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href="05.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/05_hu_a99f9dc0efe25bc0.webp 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/05_hu_dc0c16f39603b598.webp 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/05_hu_40086b0b15441a95.webp 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/05_hu_d2c99ab7d8057151.webp 1280w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1280"
height="720"
class="mx-auto my-0 rounded-md"
alt="opening slide for noughty linux talk"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2025/11/ubuntu-summit-25/05_hu_61b22107d4c49f5e.png" srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/05_hu_448ff89cbcd9feba.png 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/05_hu_61b22107d4c49f5e.png 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/05_hu_fc9324d6dc71ff1c.png 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/05.png 1280w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This &lt;a href="https://discourse.ubuntu.com/t/noughty-linux-ubuntus-stability-meets-nixpkgs-freshness/69962" target="_blank" rel="noreferrer"&gt;talk&lt;/a&gt; was a bit of a guilty pleasure for me! Delivered by &lt;a href="https://wimpysworld.com/" target="_blank" rel="noreferrer"&gt;Martin Wimpress (wimpy)&lt;/a&gt;, the audience were shown how they could take a stock Ubuntu Server deployment, and use a collection of scripts to layer a cutting-edge GUI stack on top using &lt;a href="https://github.com/NixOS/nixpkgs" target="_blank" rel="noreferrer"&gt;Nixpkgs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Wimpy outlined his motivation as wanting to rely upon the stable kernel and hardware support offered by Ubuntu, but wanting to be more experimental with his desktop environment and utilities - preferring a tiling window management experience.&lt;/p&gt;
&lt;p&gt;Having spent some years on NixOS, Wimpy was recently required to run a security &amp;ldquo;agent&amp;rdquo; for work, which was very difficult to enable on NixOS, but worked out of the box on Ubuntu. Recognising the need to make the switch, he was reluctant to move away from the workflow he&amp;rsquo;d built so much muscle-memory around - and so &lt;a href="https://noughtylinux.org/" target="_blank" rel="noreferrer"&gt;Nøughty Linux&lt;/a&gt; was born!&lt;/p&gt;
&lt;p&gt;Nøughty Linux is not a Linux distribution, rather a set of configurations for an Ubuntu Server machine. It utilises &lt;a href="https://github.com/soupglasses/nix-system-graphics" target="_blank" rel="noreferrer"&gt;&lt;code&gt;nix-system-graphics&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/numtide/system-manager" target="_blank" rel="noreferrer"&gt;&lt;code&gt;system-manager&lt;/code&gt;&lt;/a&gt; and is actually &lt;em&gt;very&lt;/em&gt; similar to a configuration I ran in my own &lt;a href="https://github.com/jnsgruk/nixos-config" target="_blank" rel="noreferrer"&gt;nixos-config&lt;/a&gt; repository for my laptop for a while - though Wimpy has chased down significantly more of the papercuts than I did!&lt;/p&gt;
&lt;h2 id="are-we-stuck-with-the-same-desktop-ux-forever" class="relative group"&gt;Are we stuck with the same Desktop UX forever? &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#are-we-stuck-with-the-same-desktop-ux-forever" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;&lt;a href="06.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/06_hu_9509dcaa896a87c.webp 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/06_hu_cd096a2b6be3a7cf.webp 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/06_hu_e7aaa31012bfd7fe.webp 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/06_hu_8caa86dfbc373634.webp 1280w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1280"
height="720"
class="mx-auto my-0 rounded-md"
alt="opening slide for desktop ux talk"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2025/11/ubuntu-summit-25/06_hu_652830ca100ce6c5.png" srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/06_hu_942ca798315d8db4.png 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/06_hu_652830ca100ce6c5.png 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/06_hu_63850a6cf7ed9e47.png 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/06.png 1280w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jenson.org/" target="_blank" rel="noreferrer"&gt;Scott Jenson&lt;/a&gt; delivered an incredibly engaging &lt;a href="https://discourse.ubuntu.com/t/are-we-stuck-with-the-same-desktop-ux-forever/67253" target="_blank" rel="noreferrer"&gt;talk&lt;/a&gt; in which he posited that desktop user experience has somewhat stagnated, and worse that many of the patterns we&amp;rsquo;ve become used to on the desktop are antiquated and unergonomic.&lt;/p&gt;
&lt;p&gt;The crux of the talk was to focus on user &lt;em&gt;experience&lt;/em&gt;, rather than user &lt;em&gt;interfaces&lt;/em&gt; - challenging developers to think about how people learn, and how desktops could benefit more from design affordances by rethinking some critical elements such as window management or text editing.&lt;/p&gt;
&lt;p&gt;Using his years of experience at Apple, Symbian and Google, Scott delivered one of the most engaging conference talks I&amp;rsquo;ve seen, and I thoroughly recommend watching it on our YouTube channel!&lt;/p&gt;
&lt;h2 id="honorable-mentions" class="relative group"&gt;Honorable Mentions &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#honorable-mentions" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;In addition to the talks above, it was a delight to meet &lt;a href="https://cs.ru.nl/~M.Schoolderman/" target="_blank" rel="noreferrer"&gt;Mark Schoolderman&lt;/a&gt; from the &lt;a href="https://trifectatech.org/" target="_blank" rel="noreferrer"&gt;Trifecta Tech Foundation&lt;/a&gt; in-person, who led the work on &lt;a href="https://github.com/trifectatechfoundation/sudo-rs" target="_blank" rel="noreferrer"&gt;&lt;code&gt;sudo-rs&lt;/code&gt;&lt;/a&gt; as part of our &amp;ldquo;Oxidising Ubuntu&amp;rdquo; story, and interesting to hear about the value the project derived from Ubuntu&amp;rsquo;s &lt;a href="https://documentation.ubuntu.com/project/MIR/main-inclusion-review/" target="_blank" rel="noreferrer"&gt;Main Inclusion Review&lt;/a&gt; process as part of landing &lt;code&gt;sudo-rs&lt;/code&gt; in &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Equally, I was delighted that &lt;a href="https://github.com/kaplun" target="_blank" rel="noreferrer"&gt;Samuele Kaplun&lt;/a&gt; from &lt;a href="https://proton.me/" target="_blank" rel="noreferrer"&gt;Proton&lt;/a&gt; could join us to talk about the work we&amp;rsquo;ve been doing together on bringing first-class Snap packages for &lt;a href="https://proton.me/mail" target="_blank" rel="noreferrer"&gt;Proton Mail&lt;/a&gt;, &lt;a href="https://protonvpn.com/?ref=pme_lp_b2c_proton_submenu" target="_blank" rel="noreferrer"&gt;Proton VPN&lt;/a&gt;, &lt;a href="https://proton.me/pass" target="_blank" rel="noreferrer"&gt;Proton Pass&lt;/a&gt; and &lt;a href="https://proton.me/authenticator" target="_blank" rel="noreferrer"&gt;Proton Authenticator&lt;/a&gt; to the &lt;a href="https://snapcraft.io/publisher/proton-ag" target="_blank" rel="noreferrer"&gt;Snap store&lt;/a&gt;, and their reasons for choosing Snaps, adventures with &lt;a href="https://snapcraft.io/docs/snap-confinement" target="_blank" rel="noreferrer"&gt;confinement&lt;/a&gt;, and more.&lt;/p&gt;
&lt;p&gt;I was delighted to see &lt;a href="https://www.craigloewen.com/" target="_blank" rel="noreferrer"&gt;Craig Loewen&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/clintrutkas/" target="_blank" rel="noreferrer"&gt;Clint Rutkas&lt;/a&gt; present on their &lt;a href="https://discourse.ubuntu.com/t/engineering-wsl-in-the-open-a-deep-dive-into-open-sourcing-wsl-at-microsoft/67022" target="_blank" rel="noreferrer"&gt;journey&lt;/a&gt; open sourcing the &lt;a href="https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux" target="_blank" rel="noreferrer"&gt;Windows Subsystem For Linux (WSL)&lt;/a&gt;, which represents a growing proportion of Ubuntu users, and a key bridge to open source development for many.&lt;/p&gt;
&lt;p&gt;Finally, thank you to &lt;a href="https://github.com/utkarsh2102" target="_blank" rel="noreferrer"&gt;Utkarsh&lt;/a&gt; for this wonderful slide as part of his talk on Ubuntu Snapshot Releases:&lt;/p&gt;
&lt;p&gt;&lt;a href="01.jpg"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/01_hu_33528e98771d0bee.webp 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/01_hu_3200c3c97ea3fdc8.webp 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/01_hu_bdcdf80fde7402d2.webp 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/01_hu_dee895df12e2c378.webp 1320w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1445"
height="813"
class="mx-auto my-0 rounded-md"
alt="a slide depicting my profile picture, but with laser eyes and the title &amp;ldquo;violence&amp;rdquo;"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2025/11/ubuntu-summit-25/01_hu_25071a7fcfa9dc2a.jpg" srcset="https://jnsgr.uk/2025/11/ubuntu-summit-25/01_hu_aa0d0f0d8dcb5517.jpg 330w,https://jnsgr.uk/2025/11/ubuntu-summit-25/01_hu_25071a7fcfa9dc2a.jpg 660w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/01_hu_f6f1d1cda3bd05f2.jpg 1024w
,https://jnsgr.uk/2025/11/ubuntu-summit-25/01_hu_41b986c0c48d6e4d.jpg 1320w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="conclusion" class="relative group"&gt;Conclusion &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#conclusion" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Overall, I found the Ubuntu Summit 25.10 a really enjoyable event, with talks that were uniformly high in quality, charisma and creativity. I&amp;rsquo;m pleased that Canonical has broadened the Summit&amp;rsquo;s reach and I hope it continues to serve as a platform to showcase the very best open source innovation.&lt;/p&gt;
&lt;p&gt;Until next time!&lt;/p&gt;</description></item></channel></rss>