<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexei Ledenev</title>
    <description>The latest articles on DEV Community by Alexei Ledenev (@alexeiled).</description>
    <link>https://dev.to/alexeiled</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F632283%2F50c8249f-8550-427a-a48c-b759f41e0dd2.png</url>
      <title>DEV Community: Alexei Ledenev</title>
      <link>https://dev.to/alexeiled</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexeiled"/>
    <language>en</language>
    <item>
      <title>cc-thingz v6: portable skills and hooks for AI coding agents</title>
      <dc:creator>Alexei Ledenev</dc:creator>
      <pubDate>Sun, 28 Jun 2026 05:01:02 +0000</pubDate>
      <link>https://dev.to/alexeiled/cc-thingz-v6-portable-skills-and-hooks-for-ai-coding-agents-1ike</link>
      <guid>https://dev.to/alexeiled/cc-thingz-v6-portable-skills-and-hooks-for-ai-coding-agents-1ike</guid>
      <description>&lt;p&gt;I released v6 of &lt;a href="https://github.com/alexei-led/cc-thingz" rel="noopener noreferrer"&gt;&lt;code&gt;cc-thingz&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Short version: &lt;strong&gt;battle-tested skills and hooks for AI coding agents&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Longer version: I wanted the same developer workflow available in whichever coding agent and LLM model I use today.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code for some tasks&lt;/li&gt;
&lt;li&gt;Codex CLI for others&lt;/li&gt;
&lt;li&gt;Gemini CLI sometimes&lt;/li&gt;
&lt;li&gt;Pi when I want the harness and subagents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The annoying part: &lt;strong&gt;skills are almost portable but work better when written as agent-specific and model-specific&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each tool has its own plugin format, lifecycle events, hook model, permissions, and model behavior. Copy-pasting prompts between tools works for a few minutes, then turns into balagan.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;cc-thingz&lt;/code&gt; keeps one source of truth and compiles it into each agent's native setup.&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;cc-thingz&lt;/code&gt; is not a coding agent.&lt;/p&gt;

&lt;p&gt;It is a portable workflow layer for coding agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;skills&lt;/li&gt;
&lt;li&gt;hooks&lt;/li&gt;
&lt;li&gt;small role agents&lt;/li&gt;
&lt;li&gt;guardrails&lt;/li&gt;
&lt;li&gt;language references&lt;/li&gt;
&lt;li&gt;generated per-agent exports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repo source lives under &lt;code&gt;src/&lt;/code&gt;. The build emits target-specific output for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code plugins&lt;/li&gt;
&lt;li&gt;Codex CLI skill exports&lt;/li&gt;
&lt;li&gt;Gemini CLI extensions&lt;/li&gt;
&lt;li&gt;Pi skills, agents, and hooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same workflow ideas. Native shape per agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What developers get
&lt;/h2&gt;

&lt;p&gt;The project covers the boring, useful parts around coding with agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;code review with severity/confidence rubrics&lt;/li&gt;
&lt;li&gt;bug fixing with reproduce-first loops&lt;/li&gt;
&lt;li&gt;refactoring with behavior-preserving checks&lt;/li&gt;
&lt;li&gt;tests based on behavior seams&lt;/li&gt;
&lt;li&gt;documentation updates grounded in code facts&lt;/li&gt;
&lt;li&gt;git hygiene and worktrees&lt;/li&gt;
&lt;li&gt;infra ops&lt;/li&gt;
&lt;li&gt;browser automation&lt;/li&gt;
&lt;li&gt;docs lookup and web research&lt;/li&gt;
&lt;li&gt;lightweight spec-driven work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the stuff that keeps an AI coding session from becoming one giant chat window with vibes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why hooks matter
&lt;/h2&gt;

&lt;p&gt;Prompts can ask the agent to behave.&lt;/p&gt;

&lt;p&gt;Hooks can put friction in the right place.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cc-thingz&lt;/code&gt; includes hooks for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;suggesting relevant skills from the user's prompt&lt;/li&gt;
&lt;li&gt;blocking protected paths and secrets&lt;/li&gt;
&lt;li&gt;formatting and linting after edits&lt;/li&gt;
&lt;li&gt;running focused tests after code changes&lt;/li&gt;
&lt;li&gt;printing repo/session context on start&lt;/li&gt;
&lt;li&gt;git guardrails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the practical difference between a prompt pack and a workflow.&lt;/p&gt;

&lt;p&gt;A prompt says: "please run tests".&lt;/p&gt;

&lt;p&gt;A hook runs the focused tests when the edit is done.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is new in v6
&lt;/h2&gt;

&lt;p&gt;v6 adds first-class workflows for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rust&lt;/li&gt;
&lt;li&gt;C#/.NET&lt;/li&gt;
&lt;li&gt;Java/Kotlin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That brings the language set to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;C#/.NET&lt;/li&gt;
&lt;li&gt;Go&lt;/li&gt;
&lt;li&gt;Java/Kotlin&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Rust&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;shell&lt;/li&gt;
&lt;li&gt;web&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The language work is not just &lt;code&gt;writing-rust&lt;/code&gt; or &lt;code&gt;writing-java-kotlin&lt;/code&gt; prompts.&lt;/p&gt;

&lt;p&gt;The hooks understand the stacks too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;focused lint/format on changed files&lt;/li&gt;
&lt;li&gt;nearest test target per build system&lt;/li&gt;
&lt;li&gt;project detection on session start&lt;/li&gt;
&lt;li&gt;skill routing by file extension, toolchain, and framework terms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if the agent edits a Rust file, a &lt;code&gt;.csproj&lt;/code&gt;, or a Gradle module, the workflow can choose a scoped feedback loop instead of running the universe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why these skills are better than random prompts
&lt;/h2&gt;

&lt;p&gt;Not magic. Just boring engineering hygiene.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. They come from daily use
&lt;/h3&gt;

&lt;p&gt;The skills were written and refined while using agents on real code, not generated as a weekend prompt dump.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. They are agent-native
&lt;/h3&gt;

&lt;p&gt;Each target gets its own native format. Claude Code gets plugins. Gemini gets extensions. Pi gets skills, agents, and extensions. Codex gets skill exports.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. They include hooks
&lt;/h3&gt;

&lt;p&gt;The workflow is not only instructions. It also includes runnable checks and guardrails.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. They compose
&lt;/h3&gt;

&lt;p&gt;The agent model was simplified from 39 agents to 3 roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;engineer&lt;/code&gt; — can edit and verify code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reviewer&lt;/code&gt; — read-only, adversarial, finds bugs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;advisor&lt;/code&gt; — verdict, risks, next actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Languages live in references. One reviewer can work across 8 languages instead of maintaining one reviewer per language.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The source of truth is shared
&lt;/h3&gt;

&lt;p&gt;The same workflow logic is authored once, then compiled into the target output. Less drift. Less "why is this broken only in tool X?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you try it?
&lt;/h2&gt;

&lt;p&gt;Try it if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you use Claude Code, Codex CLI, Gemini CLI, or Pi&lt;/li&gt;
&lt;li&gt;you switch between more than one agent or model&lt;/li&gt;
&lt;li&gt;you work in polyglot repos&lt;/li&gt;
&lt;li&gt;you want focused lint/test hooks around agent edits&lt;/li&gt;
&lt;li&gt;you want review/fix/test/refactor workflows that are more specific than "be careful"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe skip it if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you only use one tool and already have a setup you love&lt;/li&gt;
&lt;li&gt;you want a fully generic, unopinionated prompt pack&lt;/li&gt;
&lt;li&gt;you do not want hooks touching your workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;cc-thingz&lt;/code&gt; is opinionated on purpose. Neutral agent workflows become mush.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin marketplace add alexei-led/cc-thingz
/plugin &lt;span class="nb"&gt;install &lt;/span&gt;dev-flow@cc-thingz
/plugin &lt;span class="nb"&gt;install &lt;/span&gt;programming@cc-thingz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gemini CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gemini extensions &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/alexei-led/cc-thingz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pi &lt;span class="nb"&gt;install &lt;/span&gt;npm:pi-subagents
pi &lt;span class="nb"&gt;install &lt;/span&gt;git:github.com/alexei-led/cc-thingz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Codex CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/alexei-led/cc-thingz.git ~/src/cc-thingz
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src/cc-thingz
make build
codex
&lt;span class="c"&gt;# inside Codex: /plugins&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repo: &lt;a href="https://github.com/alexei-led/cc-thingz" rel="noopener noreferrer"&gt;https://github.com/alexei-led/cc-thingz&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built from daily use. Practical, opinionated, and hopefully less balagan.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vibecoding</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>CCGram v4: Control AI Coding Agents from Telegram — Now with herdr Support</title>
      <dc:creator>Alexei Ledenev</dc:creator>
      <pubDate>Mon, 22 Jun 2026 18:01:53 +0000</pubDate>
      <link>https://dev.to/alexeiled/ccgram-v4-control-ai-coding-agents-from-telegram-now-with-herdr-support-3h2k</link>
      <guid>https://dev.to/alexeiled/ccgram-v4-control-ai-coding-agents-from-telegram-now-with-herdr-support-3h2k</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/alexei-led/ccgram" rel="noopener noreferrer"&gt;CCGram&lt;/a&gt; is a Telegram bridge for AI coding agents. Each Telegram Forum topic binds 1:1 to a terminal multiplexer window running Claude Code, Codex, Gemini, Pi, or a plain shell session. You monitor output, respond to interactive prompts via inline keyboards, and manage sessions — all from your phone while the agent runs on your machine.&lt;/p&gt;

&lt;p&gt;The agent process stays in your local terminal session. Walk away from your desk, keep working from Telegram. Come back, attach to your terminal, and you have full scrollback exactly where the session left off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;v4.0.0 brings full herdr backend support.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is herdr?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ogulcancelik/herdr" rel="noopener noreferrer"&gt;herdr&lt;/a&gt; is a modern terminal multiplexer with a workspace/tab model — like tmux, but organized around named workspaces and tabs rather than sessions and windows. It has a socket-based control API and native agent-status reporting built in.&lt;/p&gt;

&lt;p&gt;If you use herdr and want ccgram to work with it: set one environment variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CCGRAM_MULTIPLEXER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;herdr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything else — topics, providers, Claude hooks, session recovery, voice messages, screenshots, live view — works identically. The multiplexer is behind a Protocol seam in the codebase; callers never see which backend is active.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes when you use the herdr backend
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adaptive topic labels
&lt;/h3&gt;

&lt;p&gt;On tmux, topics inherit the window name. On herdr, topics are labeled&lt;br&gt;
&lt;code&gt;"workspace ▸ tab"&lt;/code&gt; — e.g. &lt;code&gt;"api ▸ auth-rewrite"&lt;/code&gt;. The tab name is primary so two tabs in the same workspace still get distinct titles. If either part is missing, the label degrades gracefully to just the tab name or just the workspace name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workspace picker in &lt;code&gt;/new&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When you create a new session from Telegram, the flow gains an extra step on herdr: a workspace picker that lets you pin the new tab inside an existing workspace. If no workspaces exist yet, the step is skipped and ccgram creates a new one automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Only agent tabs surface as topics
&lt;/h3&gt;

&lt;p&gt;On tmux, every window is eligible to become a Telegram topic. On herdr, only tabs actively running an agent CLI (Claude Code, Codex, Gemini, Pi) are surfaced. A bare shell tab doesn't become a topic. This uses herdr's native agent-status capability — no terminal scraping required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Native agent status
&lt;/h3&gt;

&lt;p&gt;herdr reports what's running in a pane directly via &lt;code&gt;pane process-info&lt;/code&gt;. ccgram doesn't need to scrape terminal output to figure out the active agent; it reads the structured data the multiplexer already provides.&lt;/p&gt;

&lt;h3&gt;
  
  
  Session re-resolution after restart
&lt;/h3&gt;

&lt;p&gt;herdr re-mints tab IDs when its server restarts. ccgram handles this by re-mapping stale tab IDs to new ones via the shared session ID written by the Claude hook. Telegram topic bindings survive a herdr restart.&lt;/p&gt;

&lt;h3&gt;
  
  
  Protocol version pinning
&lt;/h3&gt;

&lt;p&gt;ccgram pins the herdr socket protocol version it was built against and refuses to start on a mismatch, rather than silently misparsing changed JSON shapes.&lt;br&gt;
If you upgrade herdr and see a protocol error, the ccgram build needs updating to match.&lt;/p&gt;

&lt;h3&gt;
  
  
  Same Claude Code hook
&lt;/h3&gt;

&lt;p&gt;The hook that tracks sessions and fires events (&lt;code&gt;SessionStart&lt;/code&gt;, &lt;code&gt;Stop&lt;/code&gt;, interactive UI prompts) resolves which window fired from &lt;code&gt;$HERDR_PANE_ID&lt;/code&gt; on herdr vs &lt;code&gt;$TMUX_PANE&lt;/code&gt; on tmux. No herdr-specific hook setup is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture: the multiplexer seam
&lt;/h2&gt;

&lt;p&gt;The multiplexer access lives behind a &lt;code&gt;Multiplexer&lt;/code&gt; Protocol in&lt;br&gt;
&lt;code&gt;multiplexer/base.py&lt;/code&gt; — a pure, I/O-free module with no backend imports.&lt;br&gt;
Both tmux (&lt;code&gt;multiplexer/tmux.py&lt;/code&gt;) and herdr (&lt;code&gt;multiplexer/herdr.py&lt;/code&gt;) implement the same protocol and return the same neutral value types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;WindowRef&lt;/code&gt; — a window/tab reference&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PaneInfo&lt;/code&gt; — pane metadata&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CaptureResult&lt;/code&gt; — scrollback capture with a truncation flag&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ForegroundInfo&lt;/code&gt; — the foreground process (pid, argv, cwd)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Callers import the module-level &lt;code&gt;multiplexer&lt;/code&gt; proxy and type against the protocol — never a concrete backend. An architectural fitness test (&lt;code&gt;tests/ccgram/test_multiplexer_boundary.py&lt;/code&gt;) enforces the boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick start with herdr
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install ccgram&lt;/span&gt;
uv tool &lt;span class="nb"&gt;install &lt;/span&gt;ccgram

&lt;span class="c"&gt;# Set the backend&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"CCGRAM_MULTIPLEXER=herdr"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.ccgram/.env

&lt;span class="c"&gt;# Install Claude Code hooks (same as tmux)&lt;/span&gt;
ccgram hook &lt;span class="nt"&gt;--install&lt;/span&gt;

&lt;span class="c"&gt;# Verify everything looks good&lt;/span&gt;
ccgram doctor

&lt;span class="c"&gt;# Run&lt;/span&gt;
ccgram
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your Telegram group, create a new topic, send a message — the directory browser appears. Pick a project directory, pick your agent, pick a workspace, and you're connected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv tool &lt;span class="nb"&gt;install &lt;/span&gt;ccgram          &lt;span class="c"&gt;# recommended&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;alexei-led/tap/ccgram  &lt;span class="c"&gt;# Homebrew (macOS)&lt;/span&gt;
pipx &lt;span class="nb"&gt;install &lt;/span&gt;ccgram             &lt;span class="c"&gt;# pipx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Release notes: &lt;a href="https://github.com/alexei-led/ccgram/releases/tag/v4.0.0" rel="noopener noreferrer"&gt;https://github.com/alexei-led/ccgram/releases/tag/v4.0.0&lt;/a&gt;&lt;br&gt;
Source: &lt;a href="https://github.com/alexei-led/ccgram" rel="noopener noreferrer"&gt;https://github.com/alexei-led/ccgram&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>devtools</category>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>cc-thingz v4: portable AI coding workflows, now properly multi-agent</title>
      <dc:creator>Alexei Ledenev</dc:creator>
      <pubDate>Wed, 13 May 2026 11:01:44 +0000</pubDate>
      <link>https://dev.to/alexeiled/cc-thingz-v4-portable-ai-coding-workflows-now-properly-multi-agent-35h2</link>
      <guid>https://dev.to/alexeiled/cc-thingz-v4-portable-ai-coding-workflows-now-properly-multi-agent-35h2</guid>
      <description>&lt;p&gt;I released v4 of &lt;a href="https://github.com/alexei-led/cc-thingz" rel="noopener noreferrer"&gt;&lt;code&gt;cc-thingz&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is my open-source toolbox for AI coding agents: &lt;strong&gt;skills, agents, hooks, and safety rails&lt;/strong&gt; for real coding workflows.&lt;/p&gt;

&lt;p&gt;It works across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenAI Codex CLI&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Gemini CLI&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pi&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tachles: this is &lt;strong&gt;not&lt;/strong&gt; another pile of prompts.&lt;/p&gt;

&lt;p&gt;It is the same workflow logic, authored once, then compiled into each tool’s native shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why v4 exists
&lt;/h2&gt;

&lt;p&gt;Before v4, the project was still too Claude-shaped.&lt;/p&gt;

&lt;p&gt;Other tools were supported, but the setup had too much duplication and too much:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;why is this different here?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Classic balagan.&lt;/p&gt;

&lt;p&gt;Now everything is authored once under &lt;code&gt;src/&lt;/code&gt; and compiled into platform-specific output.&lt;/p&gt;

&lt;h3&gt;
  
  
  What that buys you
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;shared workflow logic lives once&lt;/li&gt;
&lt;li&gt;each tool gets instructions in its own shape&lt;/li&gt;
&lt;li&gt;hooks and manifests are generated, not hand-juggled&lt;/li&gt;
&lt;li&gt;tests catch drift before users do&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not glamorous. Very useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The infra got more serious too
&lt;/h2&gt;

&lt;p&gt;One thing I ended up caring about more than expected: &lt;strong&gt;the prompts are not managed like random markdown blobs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Skills live in canonical &lt;code&gt;SKILL.md&lt;/code&gt; files, then get small per-tool overlays where behavior really differs. The build strips vendor-specific bits out of the shared base, validates that the base stays vendor-neutral, and snapshot-tests generated outputs so drift gets caught early.&lt;/p&gt;

&lt;p&gt;There are also paid skill eval fixtures for regression testing behavior, because “seems fine” is not much of a test strategy when you are shipping instructions to models.&lt;/p&gt;

&lt;p&gt;That part is not the headline, but it is why the portability claim is real instead of decorative.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I care about most: multi-agent support
&lt;/h2&gt;

&lt;p&gt;The bigger value is multi-agent work that does &lt;strong&gt;not&lt;/strong&gt; depend on one vendor UI.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cc-thingz&lt;/code&gt; now ships a larger &lt;strong&gt;shared agent set&lt;/strong&gt; for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;review&lt;/li&gt;
&lt;li&gt;implementation&lt;/li&gt;
&lt;li&gt;testing&lt;/li&gt;
&lt;li&gt;docs&lt;/li&gt;
&lt;li&gt;language work&lt;/li&gt;
&lt;li&gt;infra&lt;/li&gt;
&lt;li&gt;planning&lt;/li&gt;
&lt;li&gt;exploration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Claude Code&lt;/strong&gt; and &lt;strong&gt;Pi&lt;/strong&gt; can both use that shared set.&lt;/p&gt;

&lt;p&gt;In Pi, this works through the &lt;code&gt;@tintinweb/pi-subagents&lt;/code&gt; runtime instead of some separate Pi-only agent universe.&lt;/p&gt;

&lt;p&gt;Pi also gets four pipeline agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;scout&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;planner&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reviewer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;worker&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are there to decompose work instead of stuffing everything into one heroic context window and pretending it is an engineering team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small specialized agents. Bounded jobs. Clear handoffs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Less magic. More plumbing. Plumbing wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks got properly portable too
&lt;/h2&gt;

&lt;p&gt;Hooks are part of the point.&lt;/p&gt;

&lt;p&gt;v4 keeps pushing safety into automatic checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;linting&lt;/li&gt;
&lt;li&gt;test running&lt;/li&gt;
&lt;li&gt;git guardrails&lt;/li&gt;
&lt;li&gt;session context&lt;/li&gt;
&lt;li&gt;protected-path handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And this bit matters more than it sounds:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pi now bridges its own lifecycle and tool events to the same hook scripts and exit-code semantics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means existing hook logic can run there too, and user-defined hooks can be layered on top instead of rebuilding the whole thing from scratch.&lt;/p&gt;

&lt;p&gt;Same guardrails. More than one agent. Much better.&lt;/p&gt;

&lt;p&gt;Recent v4 work also made the file-protection hook understand &lt;strong&gt;Codex patch-based edits&lt;/strong&gt;, not just simple file writes.&lt;/p&gt;

&lt;p&gt;If an agent can edit multiple files in one patch, your guardrails need to understand patches.&lt;/p&gt;

&lt;p&gt;Otherwise it is not a guardrail.&lt;/p&gt;

&lt;p&gt;It is decoration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is for
&lt;/h2&gt;

&lt;p&gt;Use it if you work with coding agents and want reusable workflows for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;code review and fixes&lt;/li&gt;
&lt;li&gt;Go, Python, TypeScript, web, infra&lt;/li&gt;
&lt;li&gt;docs, tests, research, worktrees&lt;/li&gt;
&lt;li&gt;spec-driven development&lt;/li&gt;
&lt;li&gt;multi-agent planning and review&lt;/li&gt;
&lt;li&gt;boring safety checks before expensive mistakes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/alexei-led/cc-thingz" rel="noopener noreferrer"&gt;https://github.com/alexei-led/cc-thingz&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is opinionated because neutral tools become mush.&lt;/p&gt;

&lt;p&gt;Built from daily use, not from a weekend prompt dump with a logo.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>devtools</category>
      <category>programming</category>
    </item>
    <item>
      <title>Pumba v1.1.0: Native Podman Support, and What "Docker-Compatible API" Actually Means</title>
      <dc:creator>Alexei Ledenev</dc:creator>
      <pubDate>Fri, 24 Apr 2026 14:56:40 +0000</pubDate>
      <link>https://dev.to/alexeiled/pumba-v110-native-podman-support-and-what-docker-compatible-api-actually-means-4mb6</link>
      <guid>https://dev.to/alexeiled/pumba-v110-native-podman-support-and-what-docker-compatible-api-actually-means-4mb6</guid>
      <description>&lt;p&gt;Pumba — a container chaos CLI I've maintained since 2016 — just shipped v1.1.0 with native Podman runtime support alongside Docker and containerd.&lt;/p&gt;

&lt;p&gt;I'd expected this to be the quiet release. Podman advertises a Docker-compatible API. The Docker SDK connects to its socket and most calls work unchanged. That part turned out to be true.&lt;/p&gt;

&lt;p&gt;What I didn't expect: the 10% where it &lt;em&gt;doesn't&lt;/em&gt; match are exactly the calls a chaos tool lives on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The landmines
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;ContainerExecStart&lt;/code&gt; with empty options
&lt;/h3&gt;

&lt;p&gt;Docker accepts &lt;code&gt;ExecStartOptions{}&lt;/code&gt; — no AttachStdout, no AttachStderr, no Detach. Podman rejects it outright: &lt;em&gt;"must provide at least one stream to attach to."&lt;/em&gt; Four callsites in pumba (tc exec, iptables exec, exec-on-container, command-existence check) had to switch from &lt;code&gt;ContainerExecStart&lt;/code&gt; to &lt;code&gt;ContainerExecAttach&lt;/code&gt; + drain + &lt;code&gt;ContainerExecInspect&lt;/code&gt;. About sixty test mocks needed updating for flags Docker didn't require.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cgroup path divergence
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Docker: &lt;code&gt;docker-&amp;lt;id&amp;gt;.scope&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Podman: &lt;code&gt;libpod-&amp;lt;id&amp;gt;.scope&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Podman + systemd: often nests a &lt;code&gt;libpod-&amp;lt;id&amp;gt;.scope/container/&lt;/code&gt; leaf as libpod's init sub-cgroup&lt;/li&gt;
&lt;li&gt;cgroup v2 forbids processes in internal nodes, so stress-ng sidecars must target the nested leaf when it exists&lt;/li&gt;
&lt;li&gt;Podman's default &lt;code&gt;cgroupns=private&lt;/code&gt; means &lt;code&gt;/proc/self/cgroup&lt;/code&gt; inside the target is &lt;code&gt;0::/&lt;/code&gt; — ancestry hidden&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pumba now reads &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/cgroup&lt;/code&gt; host-side. Which means pumba must run on the same kernel as the targets. On macOS: inside the &lt;code&gt;podman machine&lt;/code&gt; VM. Same pattern we already used for containerd-in-Colima.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Sidecar reap
&lt;/h3&gt;

&lt;p&gt;tc sidecars run &lt;code&gt;tail -f /dev/null&lt;/code&gt; as PID 1. PID 1 ignores SIGTERM. Podman's DELETE-with-force sends SIGTERM, waits &lt;code&gt;StopTimeout&lt;/code&gt; (default 10s), then SIGKILLs. Every netem call was paying 10s per cleanup. Fix: &lt;code&gt;StopSignal: "SIGKILL"&lt;/code&gt; on the sidecar. Immediate reap.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Cleanup vs. caller cancellation
&lt;/h3&gt;

&lt;p&gt;If pumba itself is SIGTERM'd between &lt;code&gt;tc&lt;/code&gt; exec and sidecar removal, the cleanup defer never runs — sidecar leaks and the netem qdisc persists on the target's netns. Cleanup now uses &lt;code&gt;context.WithoutCancel(ctx)&lt;/code&gt; with a 15s budget so the defer actually survives cancellation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's not in the release (honestly)
&lt;/h2&gt;

&lt;p&gt;Rootless Podman support. Detected at client init from &lt;code&gt;Info.SecurityOptions&lt;/code&gt;; netem/iptables/stress fail fast with guidance (&lt;code&gt;podman machine set --rootful&lt;/code&gt; or the rootful systemd unit). Doing rootless correctly needs slirp4netns/pasta netns handling and user-namespace cgroup math — that's a release of its own, not a marketing-shaped hack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The broader lesson
&lt;/h2&gt;

&lt;p&gt;"Compatible API" is usually harder to integrate against than "completely different API." With a different API you build a fresh mental model and check every call. With a compatible one you assume parity and discover the exceptions empirically.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Release: &lt;a href="https://github.com/alexei-led/pumba/releases/tag/1.1.0" rel="noopener noreferrer"&gt;https://github.com/alexei-led/pumba/releases/tag/1.1.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/alexei-led/pumba" rel="noopener noreferrer"&gt;https://github.com/alexei-led/pumba&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're running chaos tests on Podman and hit something I missed — open an issue.&lt;/p&gt;

</description>
      <category>podman</category>
      <category>kubernetes</category>
      <category>devops</category>
      <category>containers</category>
    </item>
    <item>
      <title>CCGram - Control AI Coding Agents from Your Phone via Telegram and tmux</title>
      <dc:creator>Alexei Ledenev</dc:creator>
      <pubDate>Mon, 16 Mar 2026 12:27:11 +0000</pubDate>
      <link>https://dev.to/alexeiled/ccgram-control-ai-coding-agents-from-your-phone-via-telegram-and-tmux-4jjd</link>
      <guid>https://dev.to/alexeiled/ccgram-control-ai-coding-agents-from-your-phone-via-telegram-and-tmux-4jjd</guid>
      <description>&lt;p&gt;AI coding agents — Claude Code, Codex CLI, Gemini CLI — run in your terminal. When you step away from your desk, the session keeps working, but you lose visibility and control. Especially when the agent hits a permission prompt or needs your input.&lt;/p&gt;

&lt;p&gt;CCGram fixes this. It bridges Telegram to tmux so you can monitor and control your agents from your phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  The key insight: operate on tmux, not on SDKs
&lt;/h2&gt;

&lt;p&gt;Your agent runs in a tmux window on your machine. CCGram reads its transcript output and forwards it to a Telegram Forum topic. You type in Telegram — keystrokes go to the agent's tmux pane. Walk away from your laptop, keep the conversation going. Come back, &lt;code&gt;tmux attach&lt;/code&gt;, full scrollback. Nothing lost.&lt;/p&gt;

&lt;p&gt;Other Telegram bots wrap agent SDKs to create isolated API sessions that can't be resumed in your terminal. CCGram is different — it's a thin control layer over tmux.&lt;/p&gt;

&lt;h2&gt;
  
  
  One topic, one window, one agent
&lt;/h2&gt;

&lt;p&gt;Each Telegram Forum topic binds to one tmux window running one agent session. You can run Claude Code, Codex CLI, and Gemini CLI in parallel across different topics.&lt;/p&gt;

&lt;p&gt;Creating a session: open a Telegram topic, send any message. A directory browser appears — pick your project directory, choose the agent (Claude, Codex, or Gemini), choose the mode (Standard or YOLO), and you're connected.&lt;/p&gt;

&lt;p&gt;Or create a tmux window manually, start an agent — CCGram auto-detects the provider and creates a matching topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interactive prompts as inline keyboards
&lt;/h2&gt;

&lt;p&gt;When your agent asks for permission, approval, or input, CCGram renders the prompt as Telegram inline keyboard buttons. Tap to approve — no typing "yes" or copying options.&lt;/p&gt;

&lt;p&gt;This works across providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code: AskUserQuestion, ExitPlanMode, permissions&lt;/li&gt;
&lt;li&gt;Codex: edit approvals (reformatted with compact summary + diff preview), selection prompts&lt;/li&gt;
&lt;li&gt;Gemini: action-required prompts from &lt;code&gt;@inquirer/select&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Claude Code: deepest integration
&lt;/h2&gt;

&lt;p&gt;Claude Code gets 7 hook event types — SessionStart, Notification, Stop, SubagentStart, SubagentStop, TeammateIdle, TaskCompleted. These provide instant session tracking and notifications rather than polling.&lt;/p&gt;

&lt;p&gt;Multi-pane support for agent teams: blocked panes in non-active panes are auto-surfaced as inline keyboard alerts. &lt;code&gt;/panes&lt;/code&gt; command shows all panes with status and per-pane screenshot buttons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Session management
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recovery&lt;/strong&gt;: when a session dies, the bot offers Fresh (new session), Continue (last conversation), or Resume (pick from past sessions). Buttons adapt to provider capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sessions dashboard&lt;/strong&gt; (&lt;code&gt;/sessions&lt;/code&gt;): overview of all active sessions with status and kill buttons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message history&lt;/strong&gt; (&lt;code&gt;/history&lt;/code&gt;): paginated browsing of past messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal screenshots&lt;/strong&gt;: capture the current pane as a PNG image — useful for visual context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-close&lt;/strong&gt;: done topics close after 30 minutes, dead sessions after 10. Configurable or disabled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider commands&lt;/strong&gt;: &lt;code&gt;/commands&lt;/code&gt; shows all slash commands available for that topic's agent. Menus auto-switch per provider context.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Operations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-instance&lt;/strong&gt;: run separate bots per Telegram group on the same machine, sharing a single bot token. Each instance has its own tmux session, config, and state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tmux auto-detection&lt;/strong&gt;: start ccgram inside an existing tmux session — it discovers all agent windows, no creation needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emdash integration&lt;/strong&gt;: auto-discovers emdash-managed tmux sessions with zero configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diagnostics&lt;/strong&gt;: &lt;code&gt;ccgram doctor&lt;/code&gt; validates setup, checks hooks, finds orphan processes. &lt;code&gt;ccgram doctor --fix&lt;/code&gt; auto-fixes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent state&lt;/strong&gt;: thread bindings, read offsets, window states survive restarts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run as service&lt;/strong&gt;: systemd unit, launchd plist, or detached tmux.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  uv tool &lt;span class="nb"&gt;install &lt;/span&gt;ccgram
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatives: &lt;code&gt;pipx install ccgram&lt;/code&gt;, &lt;code&gt;brew install alexei-led/tap/ccgram&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Configure a Telegram bot token via BotFather, enable Topics, add to a group, set your user ID in ~/.ccgram/.env, run ccgram.&lt;/p&gt;

&lt;p&gt;For Claude Code, install hooks for best experience: &lt;code&gt;ccgram hook --install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Links&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/alexei-led/ccgram" rel="noopener noreferrer"&gt;https://github.com/alexei-led/ccgram&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PyPI: &lt;a href="https://pypi.org/project/ccgram" rel="noopener noreferrer"&gt;https://pypi.org/project/ccgram&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://github.com/alexei-led/ccgram/blob/main/docs/guides.md" rel="noopener noreferrer"&gt;https://github.com/alexei-led/ccgram/blob/main/docs/guides.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MIT licensed, Python. If you run AI coding agents and want mobile access without losing your terminal workflow — give it a try. Contributions and feedback welcome.&lt;/p&gt;

&lt;p&gt;Hope you find this useful.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>coding</category>
      <category>cli</category>
    </item>
    <item>
      <title>Kubernetes 1.33: Resizing Pods Without the Drama (Finally!) 🎉</title>
      <dc:creator>Alexei Ledenev</dc:creator>
      <pubDate>Fri, 16 May 2025 13:32:12 +0000</pubDate>
      <link>https://dev.to/alexeiled/kubernetes-133-resizing-pods-without-the-drama-finally-3cm5</link>
      <guid>https://dev.to/alexeiled/kubernetes-133-resizing-pods-without-the-drama-finally-3cm5</guid>
      <description>&lt;p&gt;Ever found yourself in that classic Kubernetes predicament? You meticulously set up your pod resources, patted yourself on the back, deployed to production... and then reality hits. Your application is either gasping for resources like a marathon runner at mile 25, or it's hoarding CPU like it's preparing for the computing apocalypse.&lt;/p&gt;

&lt;p&gt;What happens next? In the dark ages of pre-1.33 Kubernetes, you'd have to &lt;em&gt;restart&lt;/em&gt; the poor pod! It was like performing open-heart surgery just to adjust someone's diet. &lt;/p&gt;

&lt;p&gt;But rejoice, fellow Kubernetes wranglers! Version 1.33 has arrived bearing gifts, and the showstopper is &lt;strong&gt;in-place pod vertical scaling&lt;/strong&gt; in beta and enabled by default! 🎁&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is This Sorcery? 🧙‍♂️
&lt;/h2&gt;

&lt;p&gt;In-place pod resizing allows you to change the CPU and memory allocations of running pods without restarting them. Let that sink in for a second.&lt;/p&gt;

&lt;p&gt;No restarts. No connection drops. No disruption. Just smooth resource adjustment while your application keeps humming along.&lt;/p&gt;

&lt;p&gt;It works by making the &lt;code&gt;resources.requests&lt;/code&gt; and &lt;code&gt;resources.limits&lt;/code&gt; in your pod spec mutable - they can be changed on the fly without triggering a pod recreation. This is made possible through the new resize subresource feature and kubelet's ability to dynamically reconfigure container cgroups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Care? 🤔
&lt;/h2&gt;

&lt;p&gt;If you're thinking "neat trick, but why does it matter?", consider these scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stateful Applications&lt;/strong&gt; - Your database pod suddenly needs more memory during a heavy analytics query. Previously: restart required, connections dropped, cache flushed. Now: bump the memory, carry on!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost Optimization&lt;/strong&gt; - Over-provisioning "just in case" becomes unnecessary. Start conservative, scale up only when needed, scale down when the traffic subsides.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Tuning&lt;/strong&gt; - Experiment with different resource allocations to find the sweet spot, without annoying your users or operation teams.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Spiky Workloads&lt;/strong&gt; - That batch job that needs tons of resources for 5 minutes every hour? No more choosing between wasting resources or constant restarts.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Under the Hood: How It Works 🔧
&lt;/h2&gt;

&lt;p&gt;When you resize a pod, here's what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You update the pod's resource specification using the new &lt;code&gt;resize&lt;/code&gt; subresource&lt;/li&gt;
&lt;li&gt;Kubelet validates the request against node capacity&lt;/li&gt;
&lt;li&gt;If approved, kubelet instructs the container runtime to adjust cgroups&lt;/li&gt;
&lt;li&gt;Container runtime updates the limits without restarting&lt;/li&gt;
&lt;li&gt;The pod status is updated to reflect the new allocations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The pod can report its resize status through conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PodResizePending&lt;/strong&gt; - Can't resize right now (reasons include &lt;code&gt;Infeasible&lt;/code&gt; or &lt;code&gt;Deferred&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PodResizeInProgress&lt;/strong&gt; - Changes are being applied&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Show Me the Code! 💻
&lt;/h2&gt;

&lt;p&gt;Here's a simple example that demonstrates this feature in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a resource-monitoring pod&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
apiVersion: v1
kind: Pod
metadata:
  name: resize-demo
spec:
  containers:
  - name: resource-watcher
    image: ubuntu:22.04
    command:
    - "/bin/bash"
    - "-c"
    - |
      apt-get update &amp;amp;&amp;amp; apt-get install -y procps bc
      echo "=== Pod Started: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt; ==="

      # Functions to read container resource limits
      get_cpu_limit() {
        if [ -f /sys/fs/cgroup/cpu.max ]; then
          # cgroup v2
          local cpu_data=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/cpu.max&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
          local quota=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$cpu_data&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
          local period=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$cpu_data&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;

          if [ "&lt;/span&gt;&lt;span class="nv"&gt;$quota&lt;/span&gt;&lt;span class="sh"&gt;" = "max" ]; then
            echo "unlimited"
          else
            echo "&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"scale=3; &lt;/span&gt;&lt;span class="nv"&gt;$quota&lt;/span&gt;&lt;span class="s2"&gt; / &lt;/span&gt;&lt;span class="nv"&gt;$period&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt; cores"
          fi
        else
          # cgroup v1
          local quota=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/cpu/cpu.cfs_quota_us&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
          local period=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/cpu/cpu.cfs_period_us&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;

          if [ "&lt;/span&gt;&lt;span class="nv"&gt;$quota&lt;/span&gt;&lt;span class="sh"&gt;" = "-1" ]; then
            echo "unlimited"
          else
            echo "&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"scale=3; &lt;/span&gt;&lt;span class="nv"&gt;$quota&lt;/span&gt;&lt;span class="s2"&gt; / &lt;/span&gt;&lt;span class="nv"&gt;$period&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt; cores"
          fi
        fi
      }

      get_memory_limit() {
        if [ -f /sys/fs/cgroup/memory.max ]; then
          # cgroup v2
          local mem=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/memory.max&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
          if [ "&lt;/span&gt;&lt;span class="nv"&gt;$mem&lt;/span&gt;&lt;span class="sh"&gt;" = "max" ]; then
            echo "unlimited"
          else
            echo "&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;mem &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;1048576&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="sh"&gt; MiB"
          fi
        else
          # cgroup v1
          local mem=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/memory/memory.limit_in_bytes&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
          echo "&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;mem &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;1048576&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="sh"&gt; MiB"
        fi
      }

      # Print resource info every 5 seconds
      while true; do
        echo "---------- Resource Check: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt; ----------"
        echo "CPU limit: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_cpu_limit&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"
        echo "Memory limit: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_memory_limit&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"
        echo "Available memory: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;free &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;Mem | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $7}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"
        sleep 5
      done
    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired
    - resourceName: memory
      restartPolicy: NotRequired
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "128Mi"
        cpu: "100m"
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After your pod is running, double the CPU:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl patch pod resize-demo &lt;span class="nt"&gt;--subresource&lt;/span&gt; resize &lt;span class="nt"&gt;--patch&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'{"spec":{"containers":[{"name":"resource-watcher", "resources":{"requests":{"cpu":"200m"}, "limits":{"cpu":"200m"}}}]}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the pod logs, and you'll see the CPU limit magically change from 0.100 cores to 0.200 cores without any restart!&lt;/p&gt;

&lt;h2&gt;
  
  
  Control the Resize Behavior with resizePolicy ⚙️
&lt;/h2&gt;

&lt;p&gt;Sometimes you &lt;em&gt;do&lt;/em&gt; want a restart when changing certain resources. For example, many applications can't dynamically adjust to memory changes without restarting. That's where &lt;code&gt;resizePolicy&lt;/code&gt; comes in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;resizePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resourceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cpu&lt;/span&gt;
  &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NotRequired&lt;/span&gt;     &lt;span class="c1"&gt;# Live CPU tweaks!&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resourceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;memory&lt;/span&gt;
  &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RestartContainer&lt;/span&gt;  &lt;span class="c1"&gt;# Safer memory changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this configuration, CPU changes happen in-place, but memory changes trigger a container restart.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fine Print: Limitations 📜
&lt;/h2&gt;

&lt;p&gt;While this feature is cooler than a penguin in sunglasses, it does have some limitations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Windows Support&lt;/strong&gt;: None yet, only Linux containers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Types&lt;/strong&gt;: Only CPU and memory can be resized in-place&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QoS Class Immutability&lt;/strong&gt;: You can't change a pod's QoS class through resizing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Decrease&lt;/strong&gt;: You can't decrease memory limits without a restart (unless using &lt;code&gt;RestartContainer&lt;/code&gt; policy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Special Pods&lt;/strong&gt;: Pods with static CPU/memory management policies can't use this feature&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster Requirements&lt;/strong&gt;: Requires Kubernetes 1.33+ with a compatible container runtime&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What This Means for VPA 🤖
&lt;/h2&gt;

&lt;p&gt;The Vertical Pod Autoscaler has long been the awkward cousin at the Kubernetes scaling family reunion. Its main limitation? It had to recreate pods to adjust resources.&lt;/p&gt;

&lt;p&gt;While VPA doesn't yet support in-place resizing in Kubernetes 1.33, this feature lays the groundwork for future integration. Soon, we might see VPA attempting in-place resizes first, falling back to recreation only when necessary.&lt;/p&gt;

&lt;p&gt;Until then, you can use VPA in "Off" mode to get recommendations, then apply them manually via in-place resizing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: A Smoother Vertical Scaling Future 🚀
&lt;/h2&gt;

&lt;p&gt;In-place pod resizing in Kubernetes 1.33 is a game-changer for vertical scaling workflows. It removes one of the most significant barriers to efficient resource management - the disruption caused by pod recreation.&lt;/p&gt;

&lt;p&gt;Whether you're fine-tuning performance, responding to traffic spikes, or optimizing cloud costs, the ability to adjust resources without service interruption opens up new possibilities for Kubernetes-based applications.&lt;/p&gt;

&lt;p&gt;Give it a try in your non-production clusters, and prepare to say goodbye to the "restart to resize" era of Kubernetes!&lt;/p&gt;




&lt;p&gt;For the full blog post including more details, examples, and the future of vertical scaling in Kubernetes, check out &lt;a href="https://medium.com/@alexeiled/kubernetes-1-33-resizing-pods-without-the-drama-finally-88e4791be8d1" rel="noopener noreferrer"&gt;my article on Medium&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>vpa</category>
      <category>containers</category>
    </item>
    <item>
      <title>KubeIP v2: Assigning Static Public IPs to Kubernetes Nodes Across Cloud Providers</title>
      <dc:creator>Alexei Ledenev</dc:creator>
      <pubDate>Thu, 23 May 2024 14:55:38 +0000</pubDate>
      <link>https://dev.to/alexeiled/kubeip-v2-assigning-static-public-ips-to-kubernetes-nodes-across-cloud-providers-1a57</link>
      <guid>https://dev.to/alexeiled/kubeip-v2-assigning-static-public-ips-to-kubernetes-nodes-across-cloud-providers-1a57</guid>
      <description>&lt;p&gt;Kubernetes nodes can benefit from having dedicated static public IP addresses in certain scenarios. &lt;a href="https://github.com/doitintl/kubeip"&gt;KubeIP&lt;/a&gt;, an open-source utility, fulfills this need by assigning static public IPs to Kubernetes nodes. The latest version, KubeIP v2, extends support from Google Cloud's GKE to Amazon's EKS, with a design ready to accommodate other cloud providers. It operates as a DaemonSet, offering improved reliability, configuration flexibility, and user-friendliness over the previous Kubernetes controller method. KubeIP v2 supports assigning both IPv4 and IPv6 addresses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gaming Applications
&lt;/h3&gt;

&lt;p&gt;In gaming scenarios, a console may need to connect directly to a cloud VM to minimize network hops and latency. Assigning a dedicated public IP to the gaming server's node allows the console to connect directly, improving the gaming experience by reducing latency and packet loss.&lt;/p&gt;

&lt;h3&gt;
  
  
  Whitelisting Agent IPs
&lt;/h3&gt;

&lt;p&gt;If you have multiple agents or services running on Kubernetes that require direct connections to an external server and that server needs to whitelist the agents' IP addresses, using KubeIP to assign stable public IPs to the nodes makes this easier to manage than allowing broader CIDR ranges. This is particularly useful when the external server has strict IP-based access controls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoiding SNAT for Select Pods
&lt;/h3&gt;

&lt;p&gt;By default, pods are assigned private IPs from the VPC CIDR range. When they communicate with external IPv4 addresses, the Amazon VPC CNI plugin translates the pod's IP to the primary private IP of the node's network interface using SNAT (source network address translation). Sometimes, you may want to avoid SNAT for certain pods so that external services see the actual pod IPs. Assigning public IPs to nodes with KubeIP and setting &lt;code&gt;hostNetwork: true&lt;/code&gt; on the pod spec achieves this. The pod can communicate directly with external services using the node's public IP.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct Inbound Connections and Custom Networking Scenarios
&lt;/h3&gt;

&lt;p&gt;Assigning public IPs to nodes with KubeIP enables a variety of networking scenarios. For instance, you can forward traffic directly to pods running on those nodes, which is useful when you need to expose services on the node to the internet without using a traditional load balancer. An example would be running a web server on a pod and forwarding traffic to it using the node's public IP.&lt;/p&gt;

&lt;p&gt;In addition, KubeIP can be used to implement custom networking scenarios that require public IPs on nodes. For example, you could create a custom load balancer that forwards traffic to specific nodes based on the public IP. This flexibility makes KubeIP a powerful tool for testing or deploying custom networking solutions in Kubernetes.&lt;/p&gt;

&lt;h3&gt;
  
  
  IPv6 Support
&lt;/h3&gt;

&lt;p&gt;KubeIP extends its functionality beyond IPv4 by supporting the assignment of static public IPv6 addresses to nodes. This feature is increasingly important as the internet continues transitioning towards IPv6 due to the exhaustion of IPv4 addresses. With KubeIP's IPv6 support, you can assign static public IPv6 addresses to your Kubernetes nodes, enabling them to communicate directly with external services over IPv6. This is particularly beneficial for applications that require IPv6 connectivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;KubeIP v2 is a powerful tool for assigning static public IPs to Kubernetes nodes across cloud providers. It enables a wide range of use cases, from gaming applications to custom networking scenarios, and supports both IPv4 and IPv6 addresses. The extensible design and simplified DaemonSet model make it easy to deploy and manage in your environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Involved
&lt;/h2&gt;

&lt;p&gt;As an open-source &lt;a href="https://github.com/doitintl/kubeip"&gt;project&lt;/a&gt;, we welcome contributions! Submit pull requests, open issues, help with documentation, or spread the word.&lt;/p&gt;

&lt;p&gt;For more details, check out the original &lt;a href="https://engineering.doit.com/kubeip-v2-assigning-static-public-ips-to-kubernetes-nodes-across-cloud-providers-0616f684ef28"&gt;Medium post&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>aws</category>
      <category>devops</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>spotinfo - cli for exploring AWS Spot instances</title>
      <dc:creator>Alexei Ledenev</dc:creator>
      <pubDate>Sun, 16 May 2021 15:17:23 +0000</pubDate>
      <link>https://dev.to/alexeiled/spotinfo-cli-for-exploring-aws-spot-instances-1o1c</link>
      <guid>https://dev.to/alexeiled/spotinfo-cli-for-exploring-aws-spot-instances-1o1c</guid>
      <description>&lt;p&gt;The &lt;a href="https://github.com/alexei-led/spotinfo"&gt;spotinfo&lt;/a&gt; is an open-source command-line tool you can use for exploring AWS Spot instances across multiple AWS regions.&lt;/p&gt;

&lt;p&gt;Under the hood, the tool uses the same data feeds as AWS Spot Advisor and AWS Spot Pricing.&lt;/p&gt;

&lt;p&gt;Compared with the &lt;a href="https://aws.amazon.com/ec2/spot/instance-advisor/"&gt;AWS Spot Advisor&lt;/a&gt;, the &lt;code&gt;spotinfo&lt;/code&gt; supports advanced filtering and sorting (also with regex), in-place spot prices, cross-region comparison, and multiple output formats (plain text, JSON, table, csv).&lt;/p&gt;

&lt;p&gt;A short overview is available on DoiT blogs &lt;a href="https://blog.doit-intl.com/spotinfo-a-new-cli-for-aws-spot-a9748bbe338f"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>devops</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
