<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Inside Out</title><description>Notes on seeking wisdom and crafting software</description><link>https://codito.in/</link><language>en-us</language><item><title>Who are we building for?</title><link>https://codito.in/who-are-we-building-for/</link><guid isPermaLink="true">https://codito.in/who-are-we-building-for/</guid><pubDate>Wed, 25 Feb 2026 05:52:21 GMT</pubDate><content:encoded>&lt;p&gt;The news of agentic productivity is all over the place. There’s an explosion of
side projects (&lt;a href=&quot;https://www.arthurcnops.blog/death-of-show-hn/&quot;&gt;1&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;,
&lt;a href=&quot;https://johan.hal.se/wrote/2026/02/03/the-sideprocalypse/&quot;&gt;2&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;), may be large
enough to say creating something new has lost its unique value. There’s the
other rumour that the perennial &lt;em&gt;build-vs-buy&lt;/em&gt; software debate is now tilting in
favor of &lt;em&gt;build&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;AI Agents have shortened the idea-to-product pipeline. Everyone and
&lt;a href=&quot;https://www.calebleak.com/posts/dog-game/&quot;&gt;their dog&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; is on Claude Code,
churning code, writing blogs and automating things on OpenClaw or its
incarnations. This is a fact.&lt;/p&gt;
&lt;p&gt;I’ve one humble addition. AI Agents have shortened the idea-to-&lt;strong&gt;consumer&lt;/strong&gt;
pipeline. They have automated the consumer of the apps!&lt;/p&gt;
&lt;p&gt;There are probably a zillion apps which solve a problem. How do you choose? What
differentiates?&lt;/p&gt;
&lt;p&gt;Two things.&lt;/p&gt;
&lt;p&gt;One, may be we will never choose the apps. Your AI agent is going to build one
for itself. In this future, apps are the throwaway artefacts just like the
python or shell scripts a few years ago.&lt;/p&gt;
&lt;p&gt;Second, you will see only those apps which your agent decides to show you. It’s
like you’re paying 5$ for a pair of smart glasses and you only see
&lt;em&gt;what is shown to you&lt;/em&gt;. Your agents are those glasses, blending into your daily
life and create a bubble for you to live within. Who’s in control? That company
you’ve hired for five bucks.&lt;/p&gt;
&lt;p&gt;To my lizard brain there seems to be only one way out. We must understand these
models better, so much so that we can rationally cut through the layers of
filters. Call them sycophancy (a fancy word for a yes-man LLM that agrees with
everything you say) or a narrative that’s controlled by a few corporations to
force their discourse upon you.&lt;/p&gt;
&lt;p&gt;If you’re the one building apps, tools, websites etc please ask for open
standards or protocols like the three-four letter acronyms HTTP, TCP, or the
recent ones like ActivityPub. Don’t stop there, build on top of these. Think how
they must evolve for Agents. Use the open-weights models available to you. Learn
how the inferencing works, and what can you do to control it.&lt;/p&gt;
&lt;p&gt;Also, while we’re at it, let’s build for agents. They are the first class
consumers now! We don’t understand what it means though. Do our accessibility,
privacy, and performance standards apply to them? Or is our notion of time and
space too slow for these bots? Some thoughts to ponder.&lt;/p&gt;
&lt;p&gt;Now the Apps are &lt;em&gt;of the agents, by the agents, for the agents!&lt;/em&gt; Let’s see
beyond the “by the agents” narrative.&lt;/p&gt;</content:encoded></item><item><title>Editing Sanskrit text</title><link>https://codito.in/editing-sanskrit/</link><guid isPermaLink="true">https://codito.in/editing-sanskrit/</guid><pubDate>Thu, 25 Dec 2025 08:49:24 GMT</pubDate><content:encoded>&lt;p&gt;Our studies require editing Sanskrit text in Devanāgarī (देवनागरी) and the
&lt;a href=&quot;https://en.wikipedia.org/wiki/IAST&quot;&gt;IAST&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; scripts. Below is a cheatsheet to help with this.&lt;/p&gt;
&lt;h2&gt;Concepts&lt;a href=&quot;#concepts&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Conceptually there are two mechanisms to input text.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Use a keyboard layout&lt;/strong&gt;. All of us are familiar with this. Default keyboard
layout (US international or similar) corresponds to the keys in the physical
keyboard. You could use a keyboard layout and map the physical keys into the
Devanāgarī characters.&lt;/li&gt;
&lt;li&gt;Or, you could use a &lt;strong&gt;Transliteration scheme&lt;/strong&gt; where you type ASCII
characters in your existing keyboard layout and a special software called
&lt;a href=&quot;https://wiki.archlinux.org/title/Input_method&quot;&gt;Input Methods&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; framework transliterates that into Devanāgarī. E.g., with
&lt;a href=&quot;https://en.wikipedia.org/wiki/Harvard-Kyoto&quot;&gt;Harvard-Kyoto&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; transliteration scheme you type &lt;code&gt;kha&lt;/code&gt; and you get &lt;code&gt;ख&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We highly recommend the excellent &lt;a href=&quot;https://fcitx-im.org/wiki/Fcitx_5&quot;&gt;Fcitx5&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; input method for Linux. It supports
both keyboard layouts and rule based transliteration schemes. This tool
effectively enables us to type &lt;a href=&quot;https://en.wikipedia.org/wiki/IAST&quot;&gt;IAST&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; and Devanāgarī in any text box. The &lt;a href=&quot;https://www.nongnu.org/m17n/&quot;&gt;m17n&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;
project provides a database for Multilingualization (m17n) or transliteration
schemes. You can point &lt;a href=&quot;https://fcitx-im.org/wiki/Fcitx_5&quot;&gt;Fcitx5&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; to these with &lt;a href=&quot;https://archlinux.org/packages/?name=fcitx5-m17n&quot;&gt;fcitx-m17n&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Configuring Fcitx5&lt;a href=&quot;#configuring-fcitx5&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As usual, the &lt;a href=&quot;https://wiki.archlinux.org/title/Fcitx5&quot;&gt;Archlinux wiki&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; provides a fantastic guide for
setup. Also see &lt;a href=&quot;https://fcitx-im.org/wiki/Setup_Fcitx_5&quot;&gt;https://fcitx-im.org/wiki/Setup_Fcitx_5&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;. Use
&lt;a href=&quot;https://github.com/fcitx/fcitx5-configtool&quot;&gt;fcitx5-configtool&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; for a GUI based setup, screenshot below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Typing in IAST&lt;/strong&gt; can be done using a transliteration scheme or a keyboard
layout. We recommend latter for fast typing response. Use the
&lt;code&gt;Keyboard - English (US) - English (intl., with AltGr dead keys)&lt;/code&gt; keyboard
layout. This smart layout allows you to input ASCII text as usual, and leverages
the &lt;a href=&quot;https://en.wikipedia.org/wiki/Dead_key&quot;&gt;dead keys&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; with&lt;code&gt;Right   Alt&lt;/code&gt;modifier to input special text like&lt;code&gt;ā&lt;/code&gt;for&lt;code&gt;आ&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Typing in Devanāgarī&lt;/strong&gt; is achieved through the &lt;a href=&quot;https://en.wikipedia.org/wiki/Harvard-Kyoto&quot;&gt;Harvard-Kyoto&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; transliteration
scheme. You can pick any scheme supported by &lt;a href=&quot;https://www.nongnu.org/m17n/&quot;&gt;m17n&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/_astro/2025-12-fcitx-config-sanskrit.DovK3_pA_Z1aNWrW.webp&quot; alt=&quot;Fcitx5 configuration for Sanskrit&quot; loading=&quot;lazy&quot; width=&quot;959&quot; height=&quot;814&quot; /&gt;&lt;figcaption&gt;Sanskrit editing configurations in &lt;a href=&quot;https://github.com/fcitx/fcitx5-configtool&quot;&gt;fcitx5-configtool&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; app.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;Key Shortcuts&lt;a href=&quot;#key-shortcuts&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;US Intl with AltGr Dead Keys&lt;a href=&quot;#us-intl-with-altgr-dead-keys&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Modified from
&lt;a href=&quot;https://dry.sailingissues.com/us-international-keyboard-layout.html&quot;&gt;https://dry.sailingissues.com/us-international-keyboard-layout.html&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; for AltGr
Dead Keys keyboard layout.&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Diacritic&lt;/th&gt;&lt;th&gt;Keyboard shortcut&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ā&lt;/td&gt;&lt;td&gt;AltGr+Shift+3 + a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ś&lt;/td&gt;&lt;td&gt;AltGr+’ + s&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ȧ&lt;/td&gt;&lt;td&gt;AltGr+. + a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ạ&lt;/td&gt;&lt;td&gt;AltGr+Shift+- + a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ã&lt;/td&gt;&lt;td&gt;AltGr+Shift+` + a&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Below diacritics are not used in IAST, provided for completion.&lt;/p&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Diacritic&lt;/th&gt;&lt;th&gt;Keyboard shortcut&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;à&lt;/td&gt;&lt;td&gt;AltGr+` + a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ä&lt;/td&gt;&lt;td&gt;AltGr+Shift+” + a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;â&lt;/td&gt;&lt;td&gt;AltGr+6 + a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ă&lt;/td&gt;&lt;td&gt;AltGr+Shift+9 + a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ǎ&lt;/td&gt;&lt;td&gt;AltGr+Shift+. + a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ő&lt;/td&gt;&lt;td&gt;AltGr+Shift+2 + o&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ą&lt;/td&gt;&lt;td&gt;AltGr+8 + a&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;å&lt;/td&gt;&lt;td&gt;AltGr+Shift+0 + a&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Vowels (śvara) and Codas&lt;a href=&quot;#vowels-śvara-and-codas&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Modified from Wikipedia pages of &lt;a href=&quot;https://en.wikipedia.org/wiki/IAST&quot;&gt;IAST&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Harvard-Kyoto&quot;&gt;Harvard-Kyoto&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;. We’re using direct
Unicode keyboard shortcut &lt;code&gt;Ctrl+Shift+u&lt;/code&gt; on Linux for certain characters.&lt;/p&gt;









































































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Devanāgarī&lt;/th&gt;&lt;th&gt;IAST&lt;/th&gt;&lt;th&gt;Harvard Kyoto&lt;/th&gt;&lt;th&gt;Key Shortcut&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;अ&lt;/td&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;a&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;आ&lt;/td&gt;&lt;td&gt;ā&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;इ&lt;/td&gt;&lt;td&gt;i&lt;/td&gt;&lt;td&gt;i&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ई&lt;/td&gt;&lt;td&gt;ī&lt;/td&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;AltGr+Shift+3 + i&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;उ&lt;/td&gt;&lt;td&gt;u&lt;/td&gt;&lt;td&gt;u&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ऊ&lt;/td&gt;&lt;td&gt;ū&lt;/td&gt;&lt;td&gt;U&lt;/td&gt;&lt;td&gt;AltGr+Shift+3 + u&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ऋ&lt;/td&gt;&lt;td&gt;ṛ&lt;/td&gt;&lt;td&gt;R&lt;/td&gt;&lt;td&gt;AltGr+Shift+- + r&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ॠ&lt;/td&gt;&lt;td&gt;ṝ&lt;/td&gt;&lt;td&gt;RR&lt;/td&gt;&lt;td&gt;Ctrl+Shift+u 1e5d&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ऌ&lt;/td&gt;&lt;td&gt;ḷ&lt;/td&gt;&lt;td&gt;lR&lt;/td&gt;&lt;td&gt;AltGr+Shift+- + l&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ॡ&lt;/td&gt;&lt;td&gt;ḹ&lt;/td&gt;&lt;td&gt;lRR&lt;/td&gt;&lt;td&gt;Ctrl+Shift+u 1e39&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ए&lt;/td&gt;&lt;td&gt;e&lt;/td&gt;&lt;td&gt;e&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ऐ&lt;/td&gt;&lt;td&gt;ai&lt;/td&gt;&lt;td&gt;ai&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ओ&lt;/td&gt;&lt;td&gt;o&lt;/td&gt;&lt;td&gt;o&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;औ&lt;/td&gt;&lt;td&gt;au&lt;/td&gt;&lt;td&gt;au&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;`ं&lt;/td&gt;&lt;td&gt;ṁ&lt;/td&gt;&lt;td&gt;M&lt;/td&gt;&lt;td&gt;AltGr+. + m&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ः&lt;/td&gt;&lt;td&gt;ḥ&lt;/td&gt;&lt;td&gt;H&lt;/td&gt;&lt;td&gt;AltGr+Shift+- + h&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;`ँ&lt;/td&gt;&lt;td&gt;m̐&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Ctrl+Shift+u 0901&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ऽ&lt;/td&gt;&lt;td&gt;’&lt;/td&gt;&lt;td&gt;.a&lt;/td&gt;&lt;td&gt;Ctrl+Shift+u 093d&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;।&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;|&lt;/td&gt;&lt;td&gt;Ctrl+Shift+u 0964&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;॥&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;!&lt;/td&gt;&lt;td&gt;Ctrl+Shift+u 0965&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ॐ&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;OM&lt;/td&gt;&lt;td&gt;Ctrl+Shift+u 0950&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Consonants (vyañjana)&lt;a href=&quot;#consonants-vyañjana&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Map of Devanāgarī consonants with equivalent &lt;a href=&quot;https://en.wikipedia.org/wiki/IAST&quot;&gt;IAST&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Harvard-Kyoto&quot;&gt;Harvard-Kyoto&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;. Note
only the differing &lt;a href=&quot;https://en.wikipedia.org/wiki/Harvard-Kyoto&quot;&gt;Harvard-Kyoto&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; keys are mentioned after the comma. This
table is taken from the Wikipedia and enhanced with above.&lt;/p&gt;













































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;velars&lt;/th&gt;&lt;th&gt;palatals&lt;/th&gt;&lt;th&gt;retroflexes&lt;/th&gt;&lt;th&gt;dentals&lt;/th&gt;&lt;th&gt;labials&lt;/th&gt;&lt;th&gt;Category&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;क k&lt;/td&gt;&lt;td&gt;च c&lt;/td&gt;&lt;td&gt;ट ṭ, T&lt;/td&gt;&lt;td&gt;त t&lt;/td&gt;&lt;td&gt;प p&lt;/td&gt;&lt;td&gt;tenuis stops&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ख kh&lt;/td&gt;&lt;td&gt;छ ch&lt;/td&gt;&lt;td&gt;ठ ṭh, Th&lt;/td&gt;&lt;td&gt;थ th&lt;/td&gt;&lt;td&gt;फ ph&lt;/td&gt;&lt;td&gt;aspirated stops&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ग g&lt;/td&gt;&lt;td&gt;ज j&lt;/td&gt;&lt;td&gt;ड ḍ, D&lt;/td&gt;&lt;td&gt;द d&lt;/td&gt;&lt;td&gt;ब b&lt;/td&gt;&lt;td&gt;voiced stops&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;घ gh&lt;/td&gt;&lt;td&gt;झ jh&lt;/td&gt;&lt;td&gt;ढ ḍh, Dh&lt;/td&gt;&lt;td&gt;ध dh&lt;/td&gt;&lt;td&gt;भ bh&lt;/td&gt;&lt;td&gt;breathy-voiced stops&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ङ ṅ, G&lt;/td&gt;&lt;td&gt;ञ ñ, J&lt;/td&gt;&lt;td&gt;ण ṇ, N&lt;/td&gt;&lt;td&gt;न n&lt;/td&gt;&lt;td&gt;म m&lt;/td&gt;&lt;td&gt;nasal stops&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ह h&lt;/td&gt;&lt;td&gt;य y&lt;/td&gt;&lt;td&gt;र r&lt;/td&gt;&lt;td&gt;ल l&lt;/td&gt;&lt;td&gt;व v&lt;/td&gt;&lt;td&gt;approximants&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;श ś, z&lt;/td&gt;&lt;td&gt;ष ṣ, S&lt;/td&gt;&lt;td&gt;स s&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;sibilants&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;ळ ḻ, L&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;approximants (Vedic)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Hope you enjoy this new Sanskrit editor setup 🕉️&lt;/p&gt;</content:encoded></item><item><title>On code agents</title><link>https://codito.in/on-code-agents/</link><guid isPermaLink="true">https://codito.in/on-code-agents/</guid><pubDate>Wed, 08 Oct 2025 22:50:11 GMT</pubDate><content:encoded>&lt;p&gt;Coding agents are now a part of my daily workflow both at home and work. I’m
beginning to see a trade-off between building a lot of features, shipping them
faster, and the quality or craftsmanship. Let’s consider two scenarios.&lt;/p&gt;
&lt;p&gt;At home, I recently purchased a developer subscription for an open-weights LLM.
They provide Claude and OpenAI compatible API endpoints and nicely integrate
with Claude Code etc. I was already running &lt;a href=&quot;https://aider.chat/&quot;&gt;aider&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; with Openrouter and Gemini
free plans, so the migration was straightforward. I primarily used this for
Rust, C# and some Typescript hobby projects.&lt;/p&gt;
&lt;p&gt;The work projects were much more challenging. We’re trying to migrate ~50
&lt;a href=&quot;https://react.fluentui.dev/&quot;&gt;FluentUI&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; v8 controls to v9. They’re part of a UX platform used in multiple
downstream monorepos. Given the wider use this code requires a higher quality
standard.&lt;/p&gt;
&lt;p&gt;Here’s what I learned.&lt;/p&gt;
&lt;h2&gt;Pick joy over quantity&lt;a href=&quot;#pick-joy-over-quantity&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I cancelled the developer subscription for home. Obvious advantages of shipping
more features quickly were weighed down by the dryness of fixing AI slop.&lt;/p&gt;
&lt;p&gt;The open-weights LLM plan actually punched well-above the price point and it was
a joy to use Claude Code, one of the good CLI apps I’ve recently used. I’d
happily consider them again.&lt;/p&gt;
&lt;p&gt;In my scenario, I do these open-source hobby projects for fun and learning. By
design they are privacy focused, I don’t track DAU, MAU, or manage OKRs.&lt;/p&gt;
&lt;p&gt;Few things went wrong with the subscription.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Focus to maximize quantity&lt;/strong&gt;: I noticed myself picking up the agent at
every possible chance to churn some feature or other. &lt;em&gt;If I pay for it, I’d
rather use it all day&lt;/em&gt;. Heck I even tried to parallelize my study time with
an agent running in background building something or other. Not healthy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Too noisy&lt;/strong&gt;: With more quantity of code produced, the quality took a toll.
Even with all possible instruction tuning including focused planning and
iterations, the agent will lose context and violate basic principles. End
result most of my time went to review and ask the Agent fix the slop
introduced. This is something I’d have avoided with active coding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero learning&lt;/strong&gt;: AI is a more knowledgeable dev than I am especially so in
the areas new to me. With passive coding, the features got built, build, and
test passed without any design as such. Yes, I do the planning/architect
phase before I ask LLM to code. Still the nuances matter, getting involved
matters.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;The lesson: just like slow travel, I will embrace slow code. Nobody cares if I
ship 30 features on a side project, I’d rather do it on my terms.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Decades ago I was introduced to &lt;a href=&quot;https://www.jetbrains.com/resharper/&quot;&gt;Resharper&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; at work. Back then it was a linter
on steroids with excellent suggestions including refactorings. I genuinely
learned tons of best practices in C# and .NET from the linter warnings and
errors than from the LLMs today.&lt;/p&gt;
&lt;p&gt;Maybe today’s AI agent is more capable, runs without me, but I’d prefer its dumb
counterpart where learning and joy matters.&lt;/p&gt;
&lt;h2&gt;Where quantity and speed matters&lt;a href=&quot;#where-quantity-and-speed-matters&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All that said slow crafts-focused coding is no longer a viable option at work.&lt;/p&gt;
&lt;p&gt;There are OKRs everywhere to build and ship in days. Heck we measure the usage
of AI and you may get a gentle note to embrace it more. I must admit that I’m in
such a role pushing for AI dev productivity in my organization. Sorry for that,
it’s either you embrace AI, fit in or you’re out.&lt;/p&gt;
&lt;p&gt;When we started on the &lt;a href=&quot;https://react.fluentui.dev/&quot;&gt;FluentUI&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; v8 to v9 migration, the prototype informed
something like &lt;em&gt;80-90% code generated by AI is good along with awesome test
coverage&lt;/em&gt;. We claimed to be done with entire migration using 3x less effort.&lt;/p&gt;
&lt;p&gt;All of this was exciting until we tried a complex prototype! Suddenly the v9
code generated by AI appeared to be v8 like, missing the new features &lt;a href=&quot;https://storybooks.fluentui.dev/react/?path=%2Fdocs%2Fconcepts-developer-customizing-components-with-slots--docs&quot;&gt;slots
architecture&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;, or &lt;a href=&quot;https://storybooks.fluentui.dev/react/?path=%2Fdocs%2Fconcepts-developer-advanced-styling-techniques--docs&quot;&gt;style design tokens&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;. Like an AI first developer, we added
few MCPs including one that provided access to FluentUI v9 docs, threw in
detailed prompts with meticulous examples of code snippets, and AI was able to
generate decent code.&lt;/p&gt;
&lt;p&gt;But with poor design choices. At this point we reset the expectation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AI can do the migration of a simple control with poor design in a day or two.
It will take 2x that time for the Human to align it with our design principles
and improve the quality.&lt;/p&gt;
&lt;p&gt;It is not delegate-and-forget.&lt;/p&gt;
&lt;p&gt;Human must understand FluentUI v9 architecture and the design decisions to be
able to guide the AI. This requires a learning curve and is a pre-requisite
for AI driven migration.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This codified into the following heuristic.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Generate design docs for the UX component&lt;/strong&gt; along with a v8-v9 migration
note. Use AI if you will but the Human Developer must attest to the design
choices and quality.&lt;/li&gt;
&lt;li&gt;Develop a custom Github Copilot chat mode for migration with access to
specific tools only.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use the design doc&lt;/strong&gt; and use AI to develop step-by-step. At each step
ensure code is shippable similar to the &lt;em&gt;red-green-refactor&lt;/em&gt; cycle. Keep
building a mental model in your head as AI generates code.&lt;/li&gt;
&lt;li&gt;Bar for PR reviews remain same as earlier: unit tests, storybooks, a11y
testing and &lt;em&gt;code that you’re proud of&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;AI PR reviews is okay. But human PR review is required. Response to human
reviewer comments must be by human PR author. We learned that AI response to
human PR comments was too verbose and missed the point leading to unnecessary
cycles.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The good part of this heuristic is our human developers are no longer flying
blind. They are in control, their architectural skills grow because we have
human design discussions on each PR, and code continues to ship fast enough.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Here’s my key takeaway.&lt;/p&gt;
&lt;p&gt;I’m going to optimize for the &lt;em&gt;craft of building software&lt;/em&gt; where I get a chance.
This includes slow and mindful coding.&lt;/p&gt;
&lt;p&gt;In an industry setting where the incentives are on faster delivery and quantity
of features, I will &lt;em&gt;not&lt;/em&gt; fly blind. I will focus on setting a process that
works for the project, optimize to empower the human in loop, and improving the
craft as much as I can.&lt;/p&gt;
&lt;p&gt;I’d love to learn from your experience. Please do drop an email if you’d like to
chat more on this!&lt;/p&gt;</content:encoded></item><item><title>Migrating from docker to podman</title><link>https://codito.in/migrate-docker-to-podman/</link><guid isPermaLink="true">https://codito.in/migrate-docker-to-podman/</guid><pubDate>Sun, 14 Sep 2025 06:13:13 GMT</pubDate><content:encoded>&lt;p&gt;It was a week long journey to migrate my lab from Ubuntu 20.04 (end of life in
May 2025) to Ubuntu 24.04 and run everything in Podman. Ubuntu upgrade was
forced because Podman requires kernel 5.x.&lt;/p&gt;
&lt;p&gt;This note is a summary of pain points and how I’d approach this today :)&lt;/p&gt;
&lt;h2&gt;Mental model&lt;a href=&quot;#mental-model&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We run 12 selfhosted services on an Odroid M1 4GB model. There are three
variations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two services require Host networking i.e., open up ports on the host.
Adguard’s port 53 for DNS, and Traefik on port 80/443 for reverse proxy.&lt;/li&gt;
&lt;li&gt;Two services used Docker volumes - Postgres and Nextcloud.&lt;/li&gt;
&lt;li&gt;Other services are mostly self sufficient either running on sqlite or a host
mounted volume for data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Networking&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;These are the network flows.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;DNS requests:
&lt;code&gt;machines in LAN -&amp;gt; 192.168.1.111:53 (selfhost box) -&amp;gt; Podman (pasta) network -&amp;gt; Adguard home (container)&lt;/code&gt;.
Uses &lt;strong&gt;pasta&lt;/strong&gt; network.&lt;/li&gt;
&lt;li&gt;HTTP requests:
&lt;code&gt;machines in LAN -&amp;gt; news.domain.com -&amp;gt; 192.168.1.111:443 (selfhost box) -&amp;gt; Traefik on 80/443 -&amp;gt; 10.89.0.3 (miniflux)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Internal requests:
&lt;code&gt;10.89.0.3 (miniflux) -&amp;gt; postgres:5432 -&amp;gt; 10.89.0.2 (postgres)&lt;/code&gt;. Uses a
&lt;strong&gt;custom network&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note that both (1) and (3) networks will listen on port 53 for DNS resolution.
We’re using (1) for LAN wide DNS resolution. (3)‘s DNS uses Podman’s &lt;code&gt;netavark&lt;/code&gt;
for container-to-container communication using container names. We &lt;em&gt;cannot&lt;/em&gt; have
both DNS servers (netavark) and Adguard on the same network.&lt;/p&gt;
&lt;h2&gt;TL;DR - Docker to Podman&lt;a href=&quot;#tldr---docker-to-podman&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let’s do a summary for the entire process.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;You’ll need Linux Kernel 5.x. If you’re running Ubuntu, I’d recommend 24.04.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Podman. &lt;code&gt;# apt install podman&lt;/code&gt; or equivalent.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure Podman&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open port 53 onwards for rootless containers. How to:
&lt;a href=&quot;https://access.redhat.com/solutions/7044059&quot;&gt;link&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;. Run
&lt;code&gt;# sysctl --system&lt;/code&gt; after the config change.&lt;/li&gt;
&lt;li&gt;Add your registries to Podman. How to:
&lt;a href=&quot;https://docs.oracle.com/en/operating-systems/oracle-linux/podman/podman-UsingContainerRegistries.html#podman-registry-conf&quot;&gt;link&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Enable podman.socket, auto update and $USER lingering to allow auto start of
containers.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; --user&lt;/span&gt;&lt;span&gt; enable&lt;/span&gt;&lt;span&gt; podman.socket&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; --user&lt;/span&gt;&lt;span&gt; enable&lt;/span&gt;&lt;span&gt; podman-auto-update&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;loginctl&lt;/span&gt;&lt;span&gt; enable-linger&lt;/span&gt;&lt;span&gt; $USER&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test if podman is working&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;podman&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; -dt&lt;/span&gt;&lt;span&gt; -p&lt;/span&gt;&lt;span&gt; 8080:80/tcp&lt;/span&gt;&lt;span&gt; docker.io/library/httpd&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;podman&lt;/span&gt;&lt;span&gt; ps&lt;/span&gt;&lt;span&gt; # verify the container is running&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; http://localhost:8080&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Optional: At this step, you can try automated migration with
&lt;a href=&quot;https://github.com/Edu4rdSHL/fly-to-podman&quot;&gt;https://github.com/Edu4rdSHL/fly-to-podman&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Migrate volumes&lt;/strong&gt;: use &lt;code&gt;rsync&lt;/code&gt; or similar to copy
&lt;code&gt;/var/lib/docker/volumes/&amp;lt;volume_name&amp;gt;/_data&lt;/code&gt; to
&lt;code&gt;~/.local/share/containers/storage/volumes/&amp;lt;volume_name&amp;gt;/_data&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Setup networking&lt;/strong&gt;: read the excellent
&lt;a href=&quot;https://github.com/eriksjolund/podman-networking-docs&quot;&gt;podman networking docs&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;
to learn all options. Here’s I did:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create an internal network for all containers except Adguard.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;pasta&lt;/code&gt; network for Adguard to expose port 53. Details in the note
below.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Permissions&lt;/strong&gt; for host mounted volumes require additional gymnastics.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# identify the USER:GROUP running the worker process in container&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;podman&lt;/span&gt;&lt;span&gt; exec&lt;/span&gt;&lt;span&gt; -it&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;containe&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; sh&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;top&lt;/span&gt;&lt;span&gt; # note the process user:group, run inside container&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# or, check the permissions in directories _without_ mount&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;podman&lt;/span&gt;&lt;span&gt; exec&lt;/span&gt;&lt;span&gt; -it&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;containe&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; sh&lt;/span&gt;&lt;span&gt; -c&lt;/span&gt;&lt;span&gt; &apos;&lt;/span&gt;&lt;span&gt;ls -la /var/www/html&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Now, we need to chown/chmod for the same user in host mounted directories&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;podman&lt;/span&gt;&lt;span&gt; unshare&lt;/span&gt;&lt;span&gt; chown&lt;/span&gt;&lt;span&gt; -R&lt;/span&gt;&lt;span&gt; 82:82&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;host&lt;/span&gt;&lt;span&gt; shared&lt;/span&gt;&lt;span&gt; di&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; # run on the host&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Podman creates subuids for your user, e.g. above command will give
&lt;code&gt;100082:100082&lt;/code&gt; ownership to the directory, assuming &lt;code&gt;1000&lt;/code&gt; is your uid.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the quadlets. &lt;a href=&quot;https://github.com/containers/podlet&quot;&gt;https://github.com/containers/podlet&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; can help create
the skeletal *.container files. I did three additional changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;AutoUpdate=true&lt;/code&gt; for the container images to be auto pulled from
registry.&lt;/li&gt;
&lt;li&gt;Configure Traefik &lt;code&gt;Label=traefik.enable=true&lt;/code&gt; and other labels.&lt;/li&gt;
&lt;li&gt;Add an environment file &lt;code&gt;EnvironmentFile=%h/path/to/.env&lt;/code&gt; to load secrets
inside the container.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example quadlet is shared below.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You’re going to spend a good amount of time in starting/stopping systemd
units.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get yourself familiar with &lt;a href=&quot;https://github.com/rgwood/systemctl-tui&quot;&gt;https://github.com/rgwood/systemctl-tui&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Name your quadlets with a prefix (I used
&lt;code&gt;selfhost-&amp;lt;service name&amp;gt;.container&lt;/code&gt;) to help filter easily.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; --user&lt;/span&gt;&lt;span&gt; daemon-reload&lt;/span&gt;&lt;span&gt; # load changes from container files&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; --user&lt;/span&gt;&lt;span&gt; start&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;containe&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;.service&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;journalctl&lt;/span&gt;&lt;span&gt; --user&lt;/span&gt;&lt;span&gt; -xeu&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;containe&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; # see logs if something goes wrong&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or, use the &lt;code&gt;systemctl-tui&lt;/code&gt; for all these commands.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Artifacts&lt;a href=&quot;#artifacts&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now, let’s look at the quadlets and related configurations that worked for me.
I’ve annotated these with pointers wherever relevant.&lt;/p&gt;
&lt;h3&gt;Example: network quadlet&lt;a href=&quot;#example-network-quadlet&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Description&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;selfhost network&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;After&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;network-online.target&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# DNS ensures we hit the Adguard service with all queries from containers.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Adguard is listening on 192.168.1.111:53&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Network]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;NetworkName&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;selfhost&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Subnet&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;10.89.0.0/24&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Gateway&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;10.89.0.1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;DNS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;192.168.1.111&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;IPv6&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Install]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;WantedBy&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;default.target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example: container with custom network and traefik&lt;a href=&quot;#example-container-with-custom-network-and-traefik&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Requires&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;selfhost-postgres.service&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;After&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;selfhost-postgres.service&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Container]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ContainerName&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;miniflux&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Image&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;docker.io/miniflux/miniflux:latest&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;AutoUpdate&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;registry&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Network&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;selfhost.network&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;HostName&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;miniflux&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;PublishPort&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;127.0.0.1:8080:8080&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;DNS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1.1.1.1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;DNS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1.0.0.1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# %h = location to your $HOME in host. Store the credentials in this file.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# All env variables will be available inside the container.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;EnvironmentFile&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;%h&lt;/span&gt;&lt;span&gt;/containers/miniflux/miniflux.env&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Environment&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;BASE_URL&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;https://domain.host.com/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Environment&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;RUN_MIGRATIONS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Environment&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;CLEANUP_ARCHIVE_UNREAD_DAYS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Environment&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;CLEANUP_ARCHIVE_READ_DAYS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Remember to update the docker.network name to your custom network&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;traefik.enable=&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;traefik.docker.network=selfhost&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;traefik.http.routers.miniflux.rule=Host(`domain.host.com`)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;traefik.http.routers.miniflux.entrypoints=websecure&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;traefik.http.routers.miniflux.tls.certresolver=letsencrypt&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Service]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Restart&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;always&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;TimeoutStartSec&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;300&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Install]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;WantedBy&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;default.target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example: Adguard quadlet using pasta network&lt;a href=&quot;#example-adguard-quadlet-using-pasta-network&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Description&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;Adguard server&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Container]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ContainerName&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;adguardhome&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Image&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;docker.io/adguard/adguardhome&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;AutoUpdate&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;registry&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Note the `pasta` network is critical. This ensures the container listens on&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# port 53 on the host. Since it is outside the selfhost.network, we have added&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# this to traefik-dynamic.yml. Other services are auto discovered from the&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# docker socket.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Port 53 can be opened by setting /etc/sysctl.conf&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# net.ipv4.ip_unprivileged_port_start=53&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Run `sysctl --system` after the setting update.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Network&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;pasta&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;HostName&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;adguardhome&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;PublishPort&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;53&lt;/span&gt;&lt;span&gt;:53/tcp&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;PublishPort&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;53&lt;/span&gt;&lt;span&gt;:53/udp&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;PublishPort&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;3001&lt;/span&gt;&lt;span&gt;:80/tcp&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Volume&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;%h&lt;/span&gt;&lt;span&gt;/containers/adguard/workdir:/opt/adguardhome/work&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Volume&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;%h&lt;/span&gt;&lt;span&gt;/containers/adguard/confdir:/opt/adguardhome/conf&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;traefik.enable=&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Service]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;EnvironmentFile&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;%h&lt;/span&gt;&lt;span&gt;/containers/.env&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Restart&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;always&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;TimeoutStartSec&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;300&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Install]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;WantedBy&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;default.target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example: Traefik quadlet&lt;a href=&quot;#example-traefik-quadlet&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Note how we’re exposing &lt;code&gt;podman.socket&lt;/code&gt; as docker socket. This allows the
traefik service to query for all Podman containers and automatically set the
&lt;code&gt;routers&lt;/code&gt; and &lt;code&gt;services&lt;/code&gt; using the &lt;code&gt;Label=&lt;/code&gt; values in the container.&lt;/p&gt;
&lt;p&gt;To enable &lt;code&gt;podman.socket&lt;/code&gt;, see
&lt;a href=&quot;https://docs.podman.io/en/latest/markdown/podman-system-service.1.html#examples&quot;&gt;docs&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; --user&lt;/span&gt;&lt;span&gt; enable&lt;/span&gt;&lt;span&gt; podman.socket&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;loginctl&lt;/span&gt;&lt;span&gt; enable-linger&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;USE&lt;/span&gt;&lt;span&gt;R&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Traefik quadlet is below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Description&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;Traefik server&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Container]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ContainerName&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;traefik&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Image&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;docker.io/traefik:v3.1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;AutoUpdate&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;registry&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;EnvironmentFile&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;%h&lt;/span&gt;&lt;span&gt;/containers/.env&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Network&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;selfhost.network&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;HostName&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;traefik&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;PublishPort&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt;:80&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;PublishPort&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;443&lt;/span&gt;&lt;span&gt;:443&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Note the dynamic config is required for services running outside the docker&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# socket/selfhost network. E.g., adguardhome and traefik itself. Both are&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# statically routed.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Volume&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;/run/user/1000/podman/podman.sock:/var/run/docker.sock:ro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Volume&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;%h&lt;/span&gt;&lt;span&gt;/configs/traefik/dynamic:/etc/traefik/dynamic:ro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Volume&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;%h&lt;/span&gt;&lt;span&gt;/configs/traefik/traefik.yml:/traefik.yml:ro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Volume&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;%h&lt;/span&gt;&lt;span&gt;/configs/traefik/logs:/logs&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Volume&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;%h&lt;/span&gt;&lt;span&gt;/configs/traefik/acme.json:/acme.json&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Keep the SELinux label of the container runtime.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Required to communicate with the socket.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;SecurityLabelDisable&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Service]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Restart&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;always&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;TimeoutStartSec&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;300&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[Install]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;WantedBy&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;default.target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Traefik configurations&lt;a href=&quot;#traefik-configurations&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Traefik and Adguard are the only services which listen directly on the host
ports 80/443 and 3001 respectively.&lt;/p&gt;
&lt;p&gt;We setup Traefik to directly route to &lt;code&gt;admin.domain.com&lt;/code&gt; for Traefik admin
dashboard and &lt;code&gt;adguard.domain.com&lt;/code&gt; for Adguard web portal.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# traefik-dynamic.yml&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;http&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    routers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        adguard&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            entryPoints&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;web&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            rule&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;Host(`adguard.domain.com`)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            middlewares&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;adguard_redirect&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            service&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;adguard-pasta&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        adguard_https&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            entryPoints&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;websecure&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            rule&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;Host(`adguard.domain.com`)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            tls&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                certResolver&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;letsencrypt&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            service&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;adguard-pasta&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        traefik&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            entryPoints&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;websecure&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            rule&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;Host(`admin.domain.com`)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            service&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;api@internal&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            tls&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                certResolver&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;letsencrypt&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                domains&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    -&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;domain.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                      sans&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;*.domain.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    middlewares&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        adguard_redirect&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            redirectScheme&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                scheme&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;https&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    services&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        adguard-pasta&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            loadBalancer&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                servers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    -&lt;/span&gt;&lt;span&gt; url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;http://192.168.1.111:3001&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For all the other services we use the &lt;code&gt;docker&lt;/code&gt; provider.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# traefik.yml snippet&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;providers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    docker&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        watch&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        exposedByDefault&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    file&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        directory&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;/etc/traefik/dynamic&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;a href=&quot;#conclusion&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This migration was spread over a week of learning and sometimes the agony of
things not working as expected. Moving over to Podman and running the containers
rootless was worth the effort.&lt;/p&gt;
&lt;p&gt;Adios, until next time.&lt;/p&gt;</content:encoded></item><item><title>XMF released</title><link>https://codito.in/xmf-fund-tracker/</link><guid isPermaLink="true">https://codito.in/xmf-fund-tracker/</guid><pubDate>Sun, 31 Aug 2025 17:11:27 GMT</pubDate><content:encoded>&lt;p&gt;We released &lt;a href=&quot;https://github.com/codito/xmf&quot;&gt;xmf&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; today. &lt;code&gt;xmf&lt;/code&gt; can track stocks
and mutual fund portfolios using the command line terminal. As usual the tool is
free, open source and completely private.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/_astro/2025-08-xmf-summary.C8fjKG64_1cCdYn.webp&quot; alt=&quot;xmf summary view&quot; loading=&quot;lazy&quot; width=&quot;812&quot; height=&quot;851&quot; /&gt;&lt;figcaption&gt;Summary of an example profile with three portfolios and USD currency.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;A short list of features&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create multiple profiles, each profile can have multiple portfolios.&lt;/li&gt;
&lt;li&gt;A portfolio can track stocks, mutual funds (India) and fixed deposits.&lt;/li&gt;
&lt;li&gt;Data sourced from Yahoo Finance and the MF API by
&lt;a href=&quot;https://mf.captnemo.in/&quot;&gt;@captnemo&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Track summary, changes in the portfolios, returns (CAGR and rolling), mutual
fund fees, and asset allocation.&lt;/li&gt;
&lt;li&gt;Supports multiple currencies and auto-conversions for reporting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The app is built with LLMs for the most part. It’s also my first rust app
published on &lt;a href=&quot;https://crates.io/crates/xmf&quot;&gt;crates.io&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Any feedback is most welcome!&lt;/p&gt;</content:encoded></item><item><title>Pomodoro in Polybar and i3</title><link>https://codito.in/pomodoro-in-polybar-i3-archlinux/</link><guid isPermaLink="true">https://codito.in/pomodoro-in-polybar-i3-archlinux/</guid><pubDate>Tue, 22 Jul 2025 16:50:58 GMT</pubDate><content:encoded>&lt;p&gt;A few weeks back I setup a pomodoro workflow on my primary desktop running &lt;a href=&quot;https://i3wm.org&quot;&gt;i3&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;,
&lt;a href=&quot;https://github.com/polybar/polybar&quot;&gt;polybar&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; and &lt;a href=&quot;https://archlinux.org&quot;&gt;Archlinux&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;. The flow and scripts are built around the &lt;a href=&quot;https://github.com/metent/uair&quot;&gt;uair&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;
pomodoro app.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/_astro/2025-07-pomodoro-polybar-i3.CUVgFo1c_1mrhCi.webp&quot; alt=&quot;Pomodoro workflow&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;311&quot; /&gt;&lt;figcaption&gt;The user controls pomodoro using hotkeys bound in i3wm config. Polybar
uses a custom module to read the state. &lt;em&gt;uair&lt;/em&gt; is the pomodoro timer CLI app.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;We defined two types of sessions in &lt;a href=&quot;https://github.com/codito/dotfiles/blob/master/.config/uair/uair.toml&quot;&gt;uair config&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;. Both are hooked to a shell
script (see step 4 &amp;amp; 5).&lt;/li&gt;
&lt;li&gt;User has configured hotkeys to &lt;em&gt;toggle&lt;/em&gt; sessions (break or work) and
&lt;em&gt;start/stop&lt;/em&gt; them in &lt;a href=&quot;https://github.com/codito/dotfiles/blob/f2333e33666473cad023fef56403811e7260ee6c/.config/i3/config#L190&quot;&gt;i3 config&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Hotkeys run the &lt;code&gt;uairctl&lt;/code&gt; command to instruct uair - change current session,
or toggle its running state.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uairctl&lt;/code&gt; passes the instruction to &lt;code&gt;uair&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On a session start, &lt;code&gt;uair&lt;/code&gt; will invoke the configured shell scripts.&lt;/li&gt;
&lt;li&gt;These scripts may store some state in a file, e.g., session count.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Polybar is &lt;a href=&quot;https://github.com/codito/dotfiles/blob/f2333e33666473cad023fef56403811e7260ee6c/.config/polybar/config.ini#L253&quot;&gt;configured&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; to read &lt;code&gt;uair&lt;/code&gt; output via the &lt;a href=&quot;https://github.com/codito/dotfiles/blob/master/.config/polybar/scripts/pomo.sh&quot;&gt;pomo.sh&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;
shell script. On start this script will run &lt;code&gt;uair&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;I think this flow is a good example of composing tools that just do one thing
well to build complex workflows. Hope our AI workflows will also be this simple
and composable someday!&lt;/p&gt;</content:encoded></item><item><title>What problems to solve</title><link>https://codito.in/what-problems-to-solve/</link><guid isPermaLink="true">https://codito.in/what-problems-to-solve/</guid><pubDate>Sun, 13 Jul 2025 06:22:23 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Picking the most interesting problems is top of mind for many of us riding the
AI hype cycle. I’ve quoted this correspondence between Richard Feynman and a
former student (Koichi Mano) several times this week. Here are some additional
notes to self.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://genius.cat-v.org/richard-feynman/writtings/letters/problems&quot;&gt;&lt;strong&gt;What Problems to Solve&lt;/strong&gt;&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; - a letter by Richard Feynman, 1966.&lt;/p&gt;
&lt;h2&gt;Chasing the grand problems&lt;a href=&quot;#chasing-the-grand-problems&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;More often than not we get into a creator’s block followed by self-doubt. I
should probably be working on AI Agents? AGI or deep research? Wait,
representation engineering and alignment research is the hottest field right
now! Am I even solving the right problems?&lt;/p&gt;
&lt;p&gt;Feyman’s advice may sound simple. Don’t bother, just show up and build.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The worthwhile problems are the ones you can really solve or help solve, the
ones you can really contribute something to.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I would advise you to take even simpler, or as you say, humbler, problems
until you find some you can really solve easily, no matter how trivial. […]
You must not take away from yourself these pleasures because you have some
erroneous idea of what is worthwhile.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The mirage of solving the grandest problems might rob us from the fundamental
reasons that got us into our fields. For me it was the joy of creation, and
solving problems with a computer. It was not data structures, algorithms, or the
leetcode. It was also not the earlier hypes of WWW boom or the browser wars. I
learned to code for the sake of it.&lt;/p&gt;
&lt;p&gt;It may be the time to go back to the roots, tinker with AI, rip it apart and
learn block by block. I’m sure the right challenges will show up.&lt;/p&gt;
&lt;h2&gt;Find your gig&lt;a href=&quot;#find-your-gig&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There’s one big catch. We work on &lt;em&gt;their problems&lt;/em&gt; instead of ours.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I accepted him as a student because he came to me with the problem he wanted
to solve. With you I made a mistake, I gave you the problem instead of letting
you find your own; and left you with a wrong idea of what is interesting or
pleasant or important to work on (namely those problems you see you may do
something about).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We’ve been handed over problems to work on through the formative years of our
training - in school, colleges, and at work. Our employers decide the highest
business priority and we dive deep to solve those. Is this second-hand problem
also a priority for you to work on?&lt;/p&gt;
&lt;p&gt;Just as the employers hire the best candidate for a job, we too hire our
employers to give us the right problems to work on.&lt;/p&gt;
&lt;p&gt;Let’s step back.&lt;/p&gt;
&lt;p&gt;Shouldn’t we first pick a problem? And then hire an employer where we can push
your chosen field further?&lt;/p&gt;
&lt;h2&gt;Things you love&lt;a href=&quot;#things-you-love&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What comes first - the problem? Or the fact that we can do something about it?&lt;/p&gt;
&lt;p&gt;It may sound unintuitive &lt;strong&gt;how can you care about a problem even before you know
it?&lt;/strong&gt; Say we work through the principle above and strive to identify our own
problems. Where do you think you will identify the problems?&lt;/p&gt;
&lt;p&gt;You will find them in the areas you’re deeply passionate about. You must care
about it more than anyone else. Call it the calling, the ikigai or whatever.
Once we’re in our domain, problem solving is natural, and so is the state of
flow. I don’t need the extrinsic motivations. I stay awake to find a solution
just because I should.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No problem is too small or too trivial if we can really do something about it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why do I care whether the problem is grand or puny? Why does it matter if it is
solved or otherwise? I can and I will work on it.&lt;/p&gt;</content:encoded></item><item><title>An agent for that</title><link>https://codito.in/an-agent-for-that/</link><guid isPermaLink="true">https://codito.in/an-agent-for-that/</guid><pubDate>Sat, 05 Jul 2025 08:09:52 GMT</pubDate><content:encoded>&lt;p&gt;In the beginning there was a machine that could talk like most of us. Maybe not
as smart yet coherent enough. We realized it needs to do more. As good computer
scientists, we know Garbage In, Garbage Out; we need to feed this talking
machine interesting context for it to be smarter.&lt;/p&gt;
&lt;p&gt;RAG came up - we were able to push relevant data the machine could spew out in a
coherent and convincing packing. There’s a challenge, not everything can be
solved with search or information retrieval.&lt;/p&gt;
&lt;p&gt;A good AI must be able to act, and maybe plan, or appear to do so.&lt;/p&gt;
&lt;p&gt;We brought in the tools. Created functions, plugins, and recently MCP (model
context protocol) and the A2A (agent to agent) protocol. I’ve never seen any
other protocol get adopted as fast. A few years back, there used to be
committees to think through and propose the correct™ specification.&lt;/p&gt;
&lt;p&gt;Not any more.&lt;/p&gt;
&lt;p&gt;Why you’d wonder?&lt;/p&gt;
&lt;h2&gt;The wrong problem&lt;a href=&quot;#the-wrong-problem&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our agents can build protocols, tools, software, artwork, and what have you.
Just so that &lt;strong&gt;you can focus on the thing that matters&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;WTF! A bunch of us oldies thought software programming is creativity. We even
raised our art and called ourselves craftsmen. We’ve debated everything from
choosing the right editor to the correct delineation (tab vs space). We know our
craft.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Let our agents do that, so you can focus on the thing that matters&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The agent decides the protocol spec, it creates the reference servers and
clients, all the way up. A change in protocol burns a few million tokens at the
worst.&lt;/p&gt;
&lt;p&gt;We don’t need a committee for this. Instead this committee can ask agents to
churn tokens while they do more important things.&lt;/p&gt;
&lt;p&gt;Stop working on the wrong problem, and do the &lt;em&gt;thing that matters&lt;/em&gt;?&lt;/p&gt;
&lt;h2&gt;Grunt begets grunt&lt;a href=&quot;#grunt-begets-grunt&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we find the important things™, let’s talk about our day right now.&lt;/p&gt;
&lt;p&gt;It took &lt;a href=&quot;https://github.com/codito/arey/pull/2&quot;&gt;153 commits&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; to port &lt;a href=&quot;https://github.com/codito/arey&quot;&gt;arey&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;, a simple ~18 file python commandline app
to rust over several weeks. I’ll talk about the lessons in another post. The key
challenge: &lt;strong&gt;who’s going to review this&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;I asked AI to write tests. What if it writes wrong code and an equally wrong
test accompanying it? Who’ll find the issue?&lt;/p&gt;
&lt;p&gt;A key paradigm change is in order. While we used to swear by craftsmanship,
quality, and the art of &lt;em&gt;writing&lt;/em&gt; code, the present is about the art of
&lt;em&gt;reading&lt;/em&gt; and &lt;em&gt;reviewing code&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;All that said, I’m churning code much faster than ever with AI.&lt;/p&gt;
&lt;p&gt;A part of my job is to review documents and proposals. Guess who’s writing all
the docs these days? An AI. And who’s reviewing all of that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Let our agents do that, so you can focus on the thing that matters&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This doesn’t sound any true right now when you’re bombarded with sloppy code to
review, or the same monotonic proposals. I too will use agents to review.&lt;/p&gt;
&lt;p&gt;But isn’t this a game of Monkey in the Middle. I am the monkey trying to catch
the ball played by agents.&lt;/p&gt;
&lt;p&gt;I used AI to automate my grunt work, and here I’m reviewing the grunt created by
AI.&lt;/p&gt;
&lt;h2&gt;The important thing™&lt;a href=&quot;#the-important-thing&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The elephant in the room is &lt;strong&gt;AI creates, AI reviews, AI commits&lt;/strong&gt; and thus I am
out of a job. Or, a corollary, AI now does everything that mattered in my job.&lt;/p&gt;
&lt;p&gt;I don’t think so.&lt;/p&gt;
&lt;p&gt;I wish I could hold the AI and ding it for the Severity 2 incident it created
last week. I can’t do that. I still put the accountability on the human.&lt;/p&gt;
&lt;p&gt;So, our job to create value remains the same; with the unfortunate side effect
of dealing with some chaos for now. It’s a phase like any other, well until, we
build an AI on-call that we can hold accountable.&lt;/p&gt;
&lt;p&gt;Note that phase by phase we’re transitioning a layer of the work from coding to
monitoring and on-call.&lt;/p&gt;
&lt;p&gt;Once this phase is done, all of us are CTOs with a bunch of agents to command.&lt;/p&gt;
&lt;p&gt;Still, the value creation stays with us, just like the CXOs today steer a 200K
employee organization to create value for stakeholders.&lt;/p&gt;
&lt;h2&gt;It’s you, not the AI&lt;a href=&quot;#its-you-not-the-ai&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There’s the hype of what AI is capable of. This set is changing every day,
literally. Models are powerful than ever before.&lt;/p&gt;
&lt;p&gt;Then there’s us, the tool wielder. We’re trying hard to tame AI to our ways, and
it is a struggle every day.&lt;/p&gt;
&lt;p&gt;The good news is we’re used to this struggle. Do you think it would’ve been easy
to rub stones to create fire? If you were a student who struggled to create
fire, would your teacher blame the stones or you?&lt;/p&gt;
&lt;p&gt;It’s you. It’s us.&lt;/p&gt;
&lt;p&gt;Learn the tool. Or create one that just works™. And don’t forget to sit back
and enjoy the evolution live show.&lt;/p&gt;</content:encoded></item><item><title>Year in review - 2024</title><link>https://codito.in/year-in-review-2024/</link><guid isPermaLink="true">https://codito.in/year-in-review-2024/</guid><pubDate>Wed, 01 Jan 2025 10:31:52 GMT</pubDate><content:encoded>&lt;p&gt;This is our yearly review for 2024. Previous editions: &lt;a href=&quot;/year-in-review-2023/&quot;&gt;2023&lt;/a&gt;, &lt;a href=&quot;/mistakes-were-made-2022/&quot;&gt;2022&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Scorecard&lt;a href=&quot;#scorecard&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here is a snapshot of the last year. Lessons are in the next section.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reading: a handful of papers, 9 books (goal: 24 books)&lt;/li&gt;
&lt;li&gt;Writing: 8 posts (goal: 24 posts)&lt;/li&gt;
&lt;li&gt;Work
&lt;ul&gt;
&lt;li&gt;Like last year, the focus this year was on AI. Shipped two AI productivity
projects for enterprises.&lt;/li&gt;
&lt;li&gt;Six prototypes. 5 for AI and one for a large engineering refactoring. Did
some LLM finetuning without much luck.&lt;/li&gt;
&lt;li&gt;Authored a few guidance documents, one got picked by office of the CTO.&lt;/li&gt;
&lt;li&gt;Short-circuited and closed few experiments early. Similarly, happily said
&lt;em&gt;no&lt;/em&gt; to several projects where I believed impact is not proportional to
the effort.&lt;/li&gt;
&lt;li&gt;Decided against shifting to an ML engineering role. An architect creates
and ships more 🚀&lt;/li&gt;
&lt;li&gt;Balanced work and life well this year. We now run a different and more
holistic race ☯️&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Home
&lt;ul&gt;
&lt;li&gt;Lab and projects
&lt;ul&gt;
&lt;li&gt;Multiple releases this year: &lt;a href=&quot;https://github.com/spekt/testlogger&quot;&gt;spekt test loggers&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href=&quot;https://github.com/codito/noted&quot;&gt;noted&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;, &lt;a href=&quot;https://github.com/codito/arey&quot;&gt;arey&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; and
&lt;a href=&quot;https://github.com/codito/habito&quot;&gt;habito&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I had big plans for &lt;a href=&quot;https://github.com/codito/arey&quot;&gt;arey&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; but couldn’t do much because of other
pursuits.&lt;/li&gt;
&lt;li&gt;Adopted AI for my vim setup with code companion and minuet.&lt;/li&gt;
&lt;li&gt;Moved out of Google search in favor of self-hosted SearXNG.&lt;/li&gt;
&lt;li&gt;Decided against pursuing Philosophy for an MA degree. Curriculum was
awesome, but we have to go beyond intellectual pleasure.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Body and mind
&lt;ul&gt;
&lt;li&gt;3 trips this year: beaches, mountains and 8 days road trip through
some of the offbeat places in a neighboring state.&lt;/li&gt;
&lt;li&gt;With experience, the goals and priorities are clearer this year. Our
identity happily grew beyond the little bubble of work.&lt;/li&gt;
&lt;li&gt;Good progress on patience and non-violence virtues.&lt;/li&gt;
&lt;li&gt;Philosophy practice went well with lectures and books.&lt;/li&gt;
&lt;li&gt;Ran some Ayurveda experiments on self. Changes are slow but positive.
Will continue this.&lt;/li&gt;
&lt;li&gt;Spent a week on the bed due to an ailment. Its aftereffects are
continuing for months now. This is a timely reminder that I’m nothing
if this body fails, and I dug this hole for myself.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Lessons learned&lt;a href=&quot;#lessons-learned&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Large refactoring projects never work without a customer.&lt;/li&gt;
&lt;li&gt;How do you know something’s never going to ship? Too high a bar, no clarity on
the scope and lot of bike shedding to find the right thing.&lt;/li&gt;
&lt;li&gt;A lot of projects ship just around the time for appraisals. Guess the reason?&lt;/li&gt;
&lt;li&gt;We create solutions which create more problems, and we set out to solve them.
Is this is a virtuous or a vicious cycle?&lt;/li&gt;
&lt;li&gt;Saying no is a necessity. And you will inevitably disappoint a few people.
Know the impact and go for it.&lt;/li&gt;
&lt;li&gt;Disillusionment is healthy. But don’t be a pessimist.&lt;/li&gt;
&lt;li&gt;Inertia of action is better than inertia of rest.&lt;/li&gt;
&lt;li&gt;Time is extremely limited.&lt;/li&gt;
&lt;li&gt;Open LLM models have won. Gap is small within the models in a given param
range. We have seen &lt;em&gt;open&lt;/em&gt; win the cloud, and now it is a repeat.&lt;/li&gt;
&lt;li&gt;Artha (remember the &lt;em&gt;purusharthas&lt;/em&gt;) provides a sense of security. Money is one
means. Good health and relationships maybe the others.&lt;/li&gt;
&lt;li&gt;Segregation of reading by a topic allows diving deeper and framing a concrete
opinion. We did this for Philosophy, Ayurveda and Biology this year.&lt;/li&gt;
&lt;li&gt;What you read fuels &lt;em&gt;what you think&lt;/em&gt; and eventually your future actions.&lt;/li&gt;
&lt;li&gt;A little practice in philosophy goes a long way. Don’t go by the quantity. The
small exposure fills our mind with inquiries that last for days at a time,
even while all the mundane activities are running.&lt;/li&gt;
&lt;li&gt;We need a teacher to help go beyond the words and appreciate the truth.
Self-study can complement but not replace.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Year ahead&lt;a href=&quot;#year-ahead&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Reading: 24 books. Papers is not an explicit goal this time.&lt;/li&gt;
&lt;li&gt;Writing: 24 posts.&lt;/li&gt;
&lt;li&gt;Work: 5 experiments, ship at least 2.&lt;/li&gt;
&lt;li&gt;Home
&lt;ul&gt;
&lt;li&gt;Lab and projects
&lt;ul&gt;
&lt;li&gt;At least 2 releases for every active project&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Body and mind
&lt;ul&gt;
&lt;li&gt;Journal: daily with weekly reviews.&lt;/li&gt;
&lt;li&gt;Exercise: 3/week.&lt;/li&gt;
&lt;li&gt;Philosophy: practice daily.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks for reading this far. Wish you a great year ahead!&lt;/p&gt;</content:encoded></item><item><title>Release please</title><link>https://codito.in/release-please/</link><guid isPermaLink="true">https://codito.in/release-please/</guid><pubDate>Sat, 12 Oct 2024 16:24:59 GMT</pubDate><content:encoded>&lt;p&gt;A usual release of the &lt;a href=&quot;https://github.com/spekt/testlogger&quot;&gt;spekt/testlogger&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; project involves going through all the
commits and meticulously crafting the &lt;a href=&quot;https://github.com/spekt/testlogger/CHANGELOG.md&quot;&gt;CHANGELOG&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;. It takes about half an hour
and is boring enough to keep postponing the releases 🤭 We set out to automate
this one.&lt;/p&gt;
&lt;p&gt;Fortunately, Google’s &lt;a href=&quot;https://github.com/googleapis/release-please-action&quot;&gt;release-please&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; automates generating changelogs, creating
release tags and GitHub releases. It’s supposed to be just adding a step to your
GitHub action workflow; except I could not make sense of the state transitions
😕 Thus was born &lt;a href=&quot;https://github.com/codito/test-release-please&quot;&gt;test-release-please&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; experiments.&lt;/p&gt;
&lt;p&gt;Lets deep dive into the state transitions to clarify the usage of this tool. And
also talk a bit about releasing dotnet projects.&lt;/p&gt;
&lt;h2&gt;Release please&lt;a href=&quot;#release-please&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;&lt;img src=&quot;/_astro/2024-10-release-please.kQkH_I1N_Z1HwV0q.svg&quot; alt=&quot;Release me state transition&quot; loading=&quot;lazy&quot; width=&quot;773&quot; height=&quot;377&quot; /&gt;&lt;figcaption&gt;Release please action works in two phases. In the phase 1 (blue), the
action runs as part of a GitHub workflow and ensures a “release PR” reflects the
set of changes for next release. And in phase 2 (green), the user merges the
“release PR” and a GitHub release is created automatically by the action.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;First things first, you need to add a single release-please action somewhere in
your workflow. It must run on every push to the main branch. This action is
stateful, so you don’t want concurrent workflows/jobs with same action.&lt;/p&gt;
&lt;p&gt;Here are the steps along with examples:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setup &lt;code&gt;release-please&lt;/code&gt; action in a GitHub workflow. See &lt;a href=&quot;https://github.com/codito/test-release-please/blob/79b912b10e9a4569c2426d155b9631f3018375bd/.github/workflows/ci.yml#L19&quot;&gt;example&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;. Now, keep
making changes to the code as usual.&lt;/li&gt;
&lt;li&gt;The GitHub workflow will &lt;a href=&quot;https://github.com/codito/test-release-please/actions/runs/11302328578/job/31438009046#step:3:14&quot;&gt;compute the changes&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; since last release and
will create a PR like &lt;a href=&quot;https://github.com/codito/test-release-please/pull/4&quot;&gt;this&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;. Note that the action parses
&lt;a href=&quot;https://www.conventionalcommits.org/&quot;&gt;conventional commit messages&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; and GitHub tags to find the next
&lt;a href=&quot;https://semver.org/&quot;&gt;semver&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; release.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/codito/test-release-please/pull/4&quot;&gt;PR&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; will be auto updated with every workflow run. E.g., a patch
release will be upgraded to a minor release if you have any commits with
&lt;code&gt;feat: xyz&lt;/code&gt; message. Note the PR has a label &lt;code&gt;autorelease: pending&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Now you decide to release by merging the release PR.&lt;/li&gt;
&lt;li&gt;PR is merged and a workflow run is triggered.&lt;/li&gt;
&lt;li&gt;On this workflow run, the PR’s state changes to &lt;code&gt;autorelease: tagged&lt;/code&gt;, the
commit is tagged, and a release gets created.&lt;/li&gt;
&lt;li&gt;See an example previous &lt;a href=&quot;https://github.com/codito/test-release-please/pull/2&quot;&gt;release PR&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; and the corresponding
&lt;a href=&quot;https://github.com/codito/test-release-please/releases/tag/v1.1.0&quot;&gt;release&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;How this magic works?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In three keywords: GitHub actions, events, and labels/tags managing the state.&lt;/p&gt;
&lt;p&gt;Blue phase: continuous commits result in changelog updates by parsing the
conventional commit messages. A PR is created and stays up to date. It also
serves like a mini dashboard of the release payload for next version.&lt;/p&gt;
&lt;p&gt;Green phase: release-please tool learns that you’ve merged a PR with
&lt;code&gt;autorelease: pending&lt;/code&gt; label. It will cut a release and update the label in the
PR post that. Next run of the tool will happen in blue phase.&lt;/p&gt;
&lt;h2&gt;Dotnet releases&lt;a href=&quot;#dotnet-releases&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/spekt/testlogger/blob/master/.github/workflows/dotnet.yml&quot;&gt;release-please&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; action knows which files to keep up-to-date in
the coding phase, e.g. &lt;code&gt;CHANGELOG.md&lt;/code&gt; and &lt;code&gt;pyproject.toml&lt;/code&gt; for python projects.&lt;/p&gt;
&lt;p&gt;Now dotnet is not supported as a first class language. Fortunately, the tool
supports a generic strategy called &lt;code&gt;simple&lt;/code&gt; which can update the changelog and a
&lt;code&gt;version.txt&lt;/code&gt; file in the repo to depict current release version.&lt;/p&gt;
&lt;p&gt;The workflow in &lt;a href=&quot;https://github.com/spekt/testlogger&quot;&gt;spekt/testlogger&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; had two requirements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We need the pre-release NuGet packages to be uploaded to a specific feed
(MyGet).&lt;/li&gt;
&lt;li&gt;Release versions are uploaded to both MyGet and NuGet feeds.&lt;/li&gt;
&lt;li&gt;Version is managed in the workflow through a MSBUILD property. We must
determine the next version dynamically inside the GitHub workflow.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can see the solution in &lt;a href=&quot;https://github.com/spekt/testlogger/blob/master/.github/workflows/dotnet.yml&quot;&gt;testlogger ci&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt; workflow.&lt;/p&gt;
&lt;p&gt;We refactored the workflow into two jobs.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;version&lt;/code&gt; job runs a bash script to find the next semver given the previous
release in &lt;code&gt;version.txt&lt;/code&gt;. See the example code below. Further, if
&lt;code&gt;release-please&lt;/code&gt; task detects we have a release cut, we force the build version
to be the tagged version.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;version&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    runs-on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        -&lt;/span&gt;&lt;span&gt; uses&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; actions/checkout@v4&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        -&lt;/span&gt;&lt;span&gt; uses&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; googleapis/release-please-action@v4&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; release&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          if&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; github.ref == &apos;refs/heads/master&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              token&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              release-type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; simple&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        -&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Set default build number&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              # https://stackoverflow.com/questions/8653126/how-to-increment-version-number-in-a-shell-script&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              BUILD_VERSION=$(cat version.txt | awk -F. -v OFS=. &apos;{$NF=$NF+1;print}&apos;)-pre.${{ github.run_number }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              echo &quot;APP_BUILD_VERSION=${BUILD_VERSION}&quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        -&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Update build number&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          if&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ${{ steps.release.outputs.release_created }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              RELEASE_VERSION=${{ steps.release.outputs.tag_name }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              echo &quot;APP_BUILD_VERSION=${RELEASE_VERSION#v}&quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        -&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Final build version&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          run&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              echo ${{ env.APP_BUILD_VERSION }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    outputs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        build_version&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ${{ env.APP_BUILD_VERSION }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  build&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    needs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;version&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      APP_BUILD_VERSION&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ${{ needs.version.outputs.build_version }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    # rest of the steps use APP_BUILD_VERSION env variable.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;build&lt;/code&gt; job uses the output of &lt;code&gt;version&lt;/code&gt; job to set the package version. Now
with every commit, the release PR will stay up to date and pre-release packages
will use &lt;code&gt;x.y.z-pre.n&lt;/code&gt; versions. Once you merge the release PR, a package
version is generated with &lt;code&gt;x.y.z&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;That’s all for this experiment. Thanks for reading!&lt;/p&gt;</content:encoded></item></channel></rss>