<?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: Leo</title>
    <description>The latest articles on DEV Community by Leo (@leobaniak).</description>
    <link>https://dev.to/leobaniak</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%2F3997121%2F061e76e1-0a08-4d9d-827c-24f1e8b9e15b.png</url>
      <title>DEV Community: Leo</title>
      <link>https://dev.to/leobaniak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/leobaniak"/>
    <language>en</language>
    <item>
      <title>CISA rewrites the SBOM floor: hashes are required, and the scope now covers AI and SaaS</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Sun, 02 Aug 2026 00:25:47 +0000</pubDate>
      <link>https://dev.to/leobaniak/cisa-rewrites-the-sbom-floor-hashes-are-required-and-the-scope-now-covers-ai-and-saas-34af</link>
      <guid>https://dev.to/leobaniak/cisa-rewrites-the-sbom-floor-hashes-are-required-and-the-scope-now-covers-ai-and-saas-34af</guid>
      <description>&lt;p&gt;Five years after NTIA set the original floor, CISA has published the 2026 Minimum Elements for a Software Bill of Materials, replacing that baseline with a version that requires component hashes, adds license and generation-context fields, and applies the same minimum to open-source software, AI systems and software-as-a-service. Per the reporting on DevOps.com, the document was drafted with the NSA, the FBI and international partners, and folded in more than ninety public comments plus a 2025 draft that was circulated for the same purpose. For CI/CD teams the operational consequence is simple. What used to be a build-time deliverable is now also a supplier problem and a runtime problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fields that actually changed
&lt;/h2&gt;

&lt;p&gt;The 2026 baseline adds a required component hash algorithm and hash value, plus component license, SBOM tool name, and SBOM generation context. It renames the old "Supplier Name" element to "Component Producer" so the same role reads the same way across the elements. The DevOps.com writeup, quoting an industry summary of the release, lists these as the concrete deltas against the 2021 minimum.&lt;/p&gt;

&lt;p&gt;The hash requirement is the one that matters at build time. A component identifier does not tell you what you shipped. A &lt;code&gt;package@version&lt;/code&gt; string tells your scanner what a library claims to be; it says nothing about the bytes that landed. If an upstream tarball is quietly replaced under the same version, an identifier-only check will still pass and every downstream SCA tool will nod along. Requiring a hash algorithm and value inside the SBOM gives you something to compare against what the build cache actually pulled, and what a signing service actually attested. Without a hash, a diff between "documented" and "deployed" is not computable in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this moves work in the pipeline
&lt;/h2&gt;

&lt;p&gt;Adding license and generation-context fields as a floor is the boring win. License data stops arriving late from a compliance review. Generation context, which tool produced the SBOM and in what mode, makes documents comparable across suppliers instead of subtly different files that all claim to be SBOMs and quietly disagree on what counts as a component.&lt;/p&gt;

&lt;p&gt;The scope change is the harder one. Extending the minimum to AI systems and SaaS means the SBOM stops being something you can produce at &lt;code&gt;docker build&lt;/code&gt;. Mitch Ashley of the Futurum Group put the mechanics to DevOps.com plainly: you cannot hash a binary you never compile, so for SaaS the SBOM has to arrive with the service, which pushes the requirement into procurement contracts and renewal terms. The same shape applies to AI, where a model or a dataset can change without any build event on your side, and any validation program keyed only to release triggers will happily miss it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The half the guidance names but does not solve
&lt;/h2&gt;

&lt;p&gt;CISA describes the document as a floor, not a ceiling. That framing is honest. The harder problem lives one layer up. Justin Beals of Strike Graph, in the same DevOps.com writeup, put the practical version on the table: producing an SBOM is table stakes; proving the components in it are still accurate six months later is where most programs fall over. A document generated at release time drifts against what is actually running the moment anything gets rebuilt, restarted, or replaced.&lt;/p&gt;

&lt;p&gt;For a CI/CD team that translates into two chores that were optional until now. Emit an SBOM with the required hash on every build, and attach it to the artifact. Then run a scheduled re-validation pass that reads the SBOMs currently bound to running artifacts and compares them against fresh scans of the same components. Neither is a new capability. Both used to be justified case by case. The 2026 baseline gives platform owners a specific document to point at when the work needs a budget line.&lt;/p&gt;

&lt;h2&gt;
  
  
  The residual caveat
&lt;/h2&gt;

&lt;p&gt;The 2021 floor was easy to satisfy and easy to ignore. The 2026 floor is harder to satisfy and no easier to ignore, especially for teams shipping to US federal customers or into that supply chain. The scope extension to AI and SaaS is the part to plan around first, because the fix is not in your build system. It is in your contracts.&lt;/p&gt;

</description>
      <category>sbom</category>
      <category>cisa</category>
      <category>supplychainsecurity</category>
      <category>provenance</category>
    </item>
    <item>
      <title>npm walls off 2FA-bypass tokens from account and package management</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Sat, 01 Aug 2026 16:25:07 +0000</pubDate>
      <link>https://dev.to/leobaniak/npm-walls-off-2fa-bypass-tokens-from-account-and-package-management-3ejo</link>
      <guid>https://dev.to/leobaniak/npm-walls-off-2fa-bypass-tokens-from-account-and-package-management-3ejo</guid>
      <description>&lt;h2&gt;
  
  
  A token that skips 2FA is a bad idea (until you need one at 3am)
&lt;/h2&gt;

&lt;p&gt;You automate your npm publishes with a token that skips the 2FA prompt, because robots do not punch codes into a phone. Then the token leaks. Now the attacker has your automation, your account settings, your maintainer list, and a fresh batch of tokens they minted for themselves on the way out. Comforting.&lt;/p&gt;

&lt;p&gt;As of July 31, npm has cut the second half of that story off. Granular access tokens (GATs) configured to bypass 2FA can still do their day job, but they can no longer touch the account behind it. GitHub's changelog puts it plainly: a token that skips 2FA should not also be a way to manage the account.&lt;/p&gt;

&lt;h2&gt;
  
  
  What flipped over
&lt;/h2&gt;

&lt;p&gt;Bypass-2FA GATs now hit an interactive 2FA challenge whenever they try to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create or delete tokens,&lt;/li&gt;
&lt;li&gt;change package access, maintainers, or trusted publishing configuration,&lt;/li&gt;
&lt;li&gt;manage organisation or team membership, or package grants.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those are automation workloads. They are the things an attacker does &lt;em&gt;after&lt;/em&gt; they have your token, to make sure they keep it. A leaked bypass token used to be a foothold that could bootstrap into full account takeover. Now it is a leaked publish key with a much smaller blast radius: still bad, but not "add a new maintainer and quietly retire you" bad.&lt;/p&gt;

&lt;p&gt;Direct publish, at least for the moment, still works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the convenience tier was the whole problem
&lt;/h2&gt;

&lt;p&gt;Every team that ships packages has faced the same three-door choice. Force the CI job to interact with a 2FA prompt (fun). Stash a long-lived credential somewhere the pipeline can read (dangerous). Or mint a token that simply skips 2FA (dangerous, but ergonomic). Enough teams picked door three that "bypass-2FA GAT" became a category of credential, and inevitably a category of credential that leaks.&lt;/p&gt;

&lt;p&gt;The failure mode was never that these tokens could publish. It was that one leaked token could publish, extend itself, add friends, and mint more tokens. That self-perpetuating loop is what npm just broke.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do this week
&lt;/h2&gt;

&lt;p&gt;GitHub's own guidance is blunt: move automated publishing off bearer tokens.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trusted publishing (OIDC).&lt;/strong&gt; The CI runner asks the registry for a short-lived credential using its workload identity, on the fly. No secret in the repository, no long-lived token sitting in a vault waiting to leak. If your pipeline runs anywhere that mints OIDC tokens, this is the path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Staged publishing.&lt;/strong&gt; Push to a staging area, then require an interactive step to promote to the real package. Works well where a human is already in the loop for a release.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you still need a GAT for publishing, at least prune it. Scope it to the exact packages that need pushing. Give it the shortest life you can stomach. And drop the bypass-2FA flag on any GAT whose only real job was management, because there is now no reason to keep it: management is exactly what it can no longer do.&lt;/p&gt;

&lt;p&gt;Two questions your platform team should answer before Friday. Which pipelines currently authenticate to npm with a bypass-2FA GAT? And for each, is that token doing anything other than &lt;code&gt;npm publish&lt;/code&gt;? If yes, that thing just started requiring 2FA, and probably already broke.&lt;/p&gt;

&lt;h2&gt;
  
  
  The January 2027 clock
&lt;/h2&gt;

&lt;p&gt;GitHub has already telegraphed the next step. Bypass-2FA tokens will lose direct publish rights too, targeted for January 2027. The trajectory is not subtle. The registry is walking the ecosystem off long-lived bearer tokens for anything that touches a package, one verb at a time.&lt;/p&gt;

&lt;p&gt;If your OIDC-based publishing rollout has been living on the "we will get to it" board for a while, you have roughly one release cycle to change your mind. Tokens that skip a security control tend to end up on someone else's laptop. Yours, or theirs.&lt;/p&gt;

</description>
      <category>npm</category>
      <category>supplychainsecurity</category>
      <category>2fa</category>
      <category>accesstokens</category>
    </item>
    <item>
      <title>The lh unit sizes to the line, not the font</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Sat, 01 Aug 2026 16:08:41 +0000</pubDate>
      <link>https://dev.to/leobaniak/the-lh-unit-sizes-to-the-line-not-the-font-2b66</link>
      <guid>https://dev.to/leobaniak/the-lh-unit-sizes-to-the-line-not-the-font-2b66</guid>
      <description>&lt;p&gt;Nested a button icon in a paragraph and watched it float half a pixel above the baseline? Bumped up font-size for accessibility and had every ruled-line background lose its grid? These are line-height problems dressed up as font-size problems, and the CSS lh unit is the one that finally names them correctly.&lt;/p&gt;

&lt;p&gt;In a new piece titled "You might need to know about the CSS lh unit," Ahmad Shadeed walks through a tour of use cases for lh on ishadeed.com. The value is small. The shift in mindset is bigger than it looks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What lh actually resolves to
&lt;/h2&gt;

&lt;p&gt;lh is a CSS length unit. It resolves to the computed value of the line-height property on the element it is used on. Not font-size. Not cap-height. The full line box. That distinction is the whole point.&lt;/p&gt;

&lt;p&gt;If you write &lt;code&gt;line-height: 1.5&lt;/code&gt; on a &lt;code&gt;font-size: 16px&lt;/code&gt; element, &lt;code&gt;1lh&lt;/code&gt; on that element is 24px. Push the font-size to 20px and &lt;code&gt;1lh&lt;/code&gt; becomes 30px. Change the line-height and it moves again. The unit tracks what a line of text actually occupies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sizing spacing to the line, not the font
&lt;/h2&gt;

&lt;p&gt;Shadeed opens on the most familiar case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;lh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line of clear space between paragraphs, whatever font-size the reader ends up on. The rem you would normally reach for is anchored to the root font, not the paragraph's own line rhythm. lh is anchored to the paragraph itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruled-paper backgrounds that survive a resize
&lt;/h2&gt;

&lt;p&gt;The pattern that finally sold me on it is the ruled-line background:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;lh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A one-pixel line sits at the bottom of a tile that is exactly one line-height tall. Repeat it, and the rules land under every line. Tune the type up or down and the rules follow. No hand-picked pixel numbers, no re-tuning per breakpoint.&lt;/p&gt;

&lt;p&gt;The mechanism is background-repeat doing its usual work, with the tile height promoted from "some pixel I guessed" to a computed line-box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rounding a floated image to a full line
&lt;/h2&gt;

&lt;p&gt;Here's the one that surprised me. Shadeed shows this pattern for a floated image whose height should snap to the nearest whole line, so wrapped text does not end on a half-line orphan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc-size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;lh&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;round()&lt;/code&gt; is the piece doing the snapping — round up from the intrinsic size in steps of &lt;code&gt;1lh&lt;/code&gt;. Notice what the syntax names: not "one line of type" as an abstract, but the exact line-height of this element. Change the type and the snap changes with it. Nothing in the CSS mentions pixels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fading out a fixed number of lines
&lt;/h2&gt;

&lt;p&gt;For a preview list that stops at line five and fades on the way out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="n"&gt;lh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;mask-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;#000&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;lh&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nb"&gt;transparent&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five lines exactly, and the mask's fade covers the last line's worth of height. Bump the font, and both the clip and the fade stretch together. Try to reproduce that with rem and you will always be one media query short of matching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Icons that live in the same line-box as the label
&lt;/h2&gt;

&lt;p&gt;The button-icon case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.8&lt;/span&gt;&lt;span class="n"&gt;lh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;svg&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;0.8 of a line-box. The icon lives inside the same line-box as the label, and asking it to be a fraction of that line-box is closer to what you meant than any fixed em multiple. Change the button's line-height for a denser toolbar and the icon follows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two mental shifts worth keeping
&lt;/h2&gt;

&lt;p&gt;First: the sizes that belong to text — margins between paragraphs, background ruling, inline icons, wrapped-image snapping — belong to line-height, not font-size. lh is the unit that finally names that.&lt;/p&gt;

&lt;p&gt;Second: the rem-only habit was always a compromise. rem tracks the root font, which is convenient, and wrong at every place where two different type sizes coexist on the same page. lh tracks the actual line box you are in.&lt;/p&gt;

&lt;p&gt;Try one substitution on your next component: swap a rem that lives near text for a lh. If the layout stops drifting when you scale type, you have found one of the places lh is quietly the right unit.&lt;/p&gt;

</description>
      <category>css</category>
      <category>typography</category>
      <category>lhunit</category>
      <category>lineheight</category>
    </item>
    <item>
      <title>Docker Hub gets OIDC federation for GitHub Actions, retiring the PAT-in-a-secret pattern</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Sat, 01 Aug 2026 08:24:56 +0000</pubDate>
      <link>https://dev.to/leobaniak/docker-hub-gets-oidc-federation-for-github-actions-retiring-the-pat-in-a-secret-pattern-1392</link>
      <guid>https://dev.to/leobaniak/docker-hub-gets-oidc-federation-for-github-actions-retiring-the-pat-in-a-secret-pattern-1392</guid>
      <description>&lt;p&gt;Docker has switched on OpenID Connect authentication between GitHub Actions and Docker Hub, letting a workflow exchange a signed per-run identity token for a short-lived Docker credential instead of reading a Personal Access Token out of a repo secret. Per the Docker blog, the feature is available to organizations on Docker Team, Docker Business and Docker Hardened Images plans. For platform teams, this pulls the registry credential out of the "rotate every 90 days and hope" pile and into the same federated-identity pattern already used for AWS, GCP, Azure and HashiCorp Vault.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mechanics, briefly
&lt;/h2&gt;

&lt;p&gt;The pattern is the standard Actions OIDC exchange. When a workflow declares &lt;code&gt;permissions: id-token: write&lt;/code&gt;, GitHub mints a signed JWT for the run that encodes the repository, branch or environment, workflow name and a few other claims. The workflow presents that JWT to Docker, Docker validates the signature against GitHub's public keys, checks the claims against a trust policy configured on the Docker side, and returns a short-lived credential the run uses for the rest of its steps. When the job ends, the credential expires. There is no long-lived Docker token in the repo, in an environment secret, or in an Actions cache. Docker positions the change as eliminating stored credentials in CI/CD pipelines, which is fair as long as you actually delete the PAT afterwards.&lt;/p&gt;

&lt;p&gt;Two things matter about that trust policy. The first is that the scope is expressed in Docker, not in GitHub. If you write a policy that trusts any workflow from &lt;code&gt;my-org/*&lt;/code&gt; on any branch, an attacker who can push a branch to any of your repos can request a Docker credential. The safer shape pins the policy to specific repositories and a specific environment (&lt;code&gt;environment:prod&lt;/code&gt;), then gates the environment behind required reviewers on the GitHub side. The second is that OIDC removes the credential but not the authorization boundary: a leaked token is short-lived, but the trust policy itself is now the long-lived object you need to audit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the credential mattered
&lt;/h2&gt;

&lt;p&gt;Static registry tokens have been the shape of a lot of supply-chain incidents this year. A PAT or OAT that can push to a public tag from a compromised branch is a supply-chain hijack in one API call, and unlike a cloud IAM key it rarely sits behind a workload identity boundary. Rotation helps but is not free: a rotated Docker token has to be redistributed to every Actions repo and self-hosted runner that pushes images, which is why teams rotate less often than their own policy says they should.&lt;/p&gt;

&lt;p&gt;OIDC does not change what a compromised workflow can do inside its own run. It changes the residue. After the job, there is nothing to steal from the secret store. The blast radius of a leaked token collapses from "until the next rotation cycle" to "until the job ends", which for most Actions jobs is minutes. That is the operational read: less to rotate, less to inventory, less to revoke during an incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring it up in a workflow
&lt;/h2&gt;

&lt;p&gt;The Actions side of the exchange is the same shape as every other OIDC integration. Sketch, with placeholders where the release-specific bits go:&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;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&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;push&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;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&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@&amp;lt;full-40-char-sha&amp;gt;&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;Log in to Docker Hub via OIDC&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;docker/login-action@&amp;lt;full-40-char-sha&amp;gt;&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# OIDC-mode inputs per the Docker docs for this feature;&lt;/span&gt;
          &lt;span class="c1"&gt;# no username or password is stored in the repo.&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.io&lt;/span&gt;
          &lt;span class="c1"&gt;# ...OIDC-specific parameters go here...&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;Build and push&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;docker buildx build \&lt;/span&gt;
            &lt;span class="s"&gt;--tag docker.io/$REGISTRY_NAMESPACE/app:${{ github.sha }} \&lt;/span&gt;
            &lt;span class="s"&gt;--push .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two nudges from the pager side. Pin the login action to a full commit SHA, not a floating tag: the credential you are handing this step is time-boxed, but the code inside it is not. And keep the trust policy on the Docker side narrow: repository, branch or environment, and workflow file path. A wildcard here is the equivalent of the old &lt;code&gt;read_write&lt;/code&gt; PAT you were trying to retire.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this does not solve
&lt;/h2&gt;

&lt;p&gt;OIDC federation is a credential-plumbing change. Everything downstream of "the run has a valid token" still needs the usual attention.&lt;/p&gt;

&lt;p&gt;Image provenance is unchanged. A workflow authenticated by OIDC is still the workflow you told it to run, which is still whatever branch the trigger allows. If your policy trusts &lt;code&gt;main&lt;/code&gt;, a self-approved PR that merges to &lt;code&gt;main&lt;/code&gt; gets a Docker credential. Branch protection, required reviewers on the deployment environment, and Actions' hold-for-approval controls on suspected-malicious workflows are all upstream of this.&lt;/p&gt;

&lt;p&gt;Self-hosted runners are the other quiet caveat. GitHub-hosted runners emit the OIDC token as documented. Self-hosted runners can emit it too, but the trust boundary is now "whoever controls the runner host can request Docker credentials scoped to the trust policy". If your self-hosted fleet is not fully isolated per repo or per environment, treat the Docker trust policy at least as carefully as you treat the runner registration token.&lt;/p&gt;

&lt;p&gt;Signing is separate. OIDC removes the push credential, it does not add a signed digest. Cosign or the platform's own attestation flow is still what you reach for if you want the pull side to verify what the push side produced.&lt;/p&gt;

&lt;h2&gt;
  
  
  How other CI platforms handle registry auth
&lt;/h2&gt;

&lt;p&gt;Docker Hub is late to this specific integration but not to the pattern. The registry-auth story looks different depending on where your pipeline runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Actions to GHCR.&lt;/strong&gt; If the registry is &lt;code&gt;ghcr.io&lt;/code&gt;, GitHub Actions has had the tightest story for a while: the built-in &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; scoped to the repo pushes to the org's own container registry, no extra federation to configure. If Docker Hub is not a hard requirement for you, GHCR remains the simpler answer for GitHub-native teams, and this Docker OIDC change does not close that gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitLab CI ID tokens.&lt;/strong&gt; GitLab's own OIDC (&lt;code&gt;id_tokens:&lt;/code&gt; in the pipeline) federates into AWS, GCP, Vault and, via the registry's own OIDC provider, into GitLab's Container Registry natively. For teams already on GitLab, the ID-token workflow is more first-party than any external registry integration and is the better fit if you are picking a stack from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CircleCI OIDC.&lt;/strong&gt; CircleCI issues OIDC tokens on jobs, and the mainstream use is cloud IAM (AWS, GCP). Registry logins on CircleCI still lean on stored context secrets for Docker Hub in most teams' setups, which is exactly the state Docker's change addresses on the GitHub side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buildkite agent OIDC.&lt;/strong&gt; Buildkite's agent-signed OIDC tokens are widely used with AWS and Vault. Registry auth patterns vary by fleet, and the trust boundary of your agent fleet is the more important knob than the token flavor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buddy.&lt;/strong&gt; &lt;a href="https://buddy.works/?utm_source=cicd.deployment.to&amp;amp;utm_medium=article&amp;amp;utm_campaign=docker-oidc" rel="noopener noreferrer"&gt;Buddy&lt;/a&gt; is a hosted CI/CD platform whose Docker pipeline actions include registry login steps that read from Buddy-scoped secrets and short-lived per-pipeline credentials. It is one option if you want the Docker push and the environment gating in the same UI as the rest of your delivery pipeline, rather than assembling it out of Actions and external policy. If your registry needs are entirely on Docker Hub and the rest of your CI already lives in GitHub, Docker's new OIDC flow keeps you inside the tooling you have.&lt;/p&gt;

&lt;p&gt;The through-line: federated identity is now the default assumption for registry auth, not a nice-to-have. Long-lived registry tokens in CI are the exception, and every remaining one is a rotation item on the platform team's backlog.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to watch next
&lt;/h2&gt;

&lt;p&gt;Two things worth tracking. First, whether the trust-policy surface on the Docker side grows the same kinds of misconfiguration blast radius that early AWS-to-Actions OIDC policies did, where overly broad &lt;code&gt;aud&lt;/code&gt; and &lt;code&gt;sub&lt;/code&gt; claims let any workflow in an org assume production roles. The remediation there was tightening the claims, and the same discipline applies here from day one. Second, whether Docker extends the OIDC flow to CI platforms beyond GitHub Actions. The current release, per the announcement, is GitHub Actions and paid Docker Org tiers. Everyone else keeps rotating the PAT.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>githubactions</category>
      <category>oidc</category>
      <category>secrets</category>
    </item>
    <item>
      <title>The CSS infinity keyword, and what it lets you delete</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Sat, 01 Aug 2026 04:08:41 +0000</pubDate>
      <link>https://dev.to/leobaniak/the-css-infinity-keyword-and-what-it-lets-you-delete-3lcc</link>
      <guid>https://dev.to/leobaniak/the-css-infinity-keyword-and-what-it-lets-you-delete-3lcc</guid>
      <description>&lt;p&gt;You have been writing &lt;code&gt;border-radius: 9999px&lt;/code&gt; to fake a pill. It works. But you picked the number, and the box is now lying to the browser about how round it wants to be. There is a name for the number you actually meant. Adam Argyle spent a post on nerdy.dev on July 25, 2026 walking through it — the CSS &lt;code&gt;infinity&lt;/code&gt; keyword.&lt;/p&gt;

&lt;h2&gt;
  
  
  The keyword, and the calc() around it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;infinity&lt;/code&gt; is a number, not a unit. You cannot write &lt;code&gt;border-radius: infinity&lt;/code&gt;. You wrap it in &lt;code&gt;calc()&lt;/code&gt; and multiply by the unit you want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;border-radius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;infinity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the pill. The engine resolves it to the largest length the system will produce, and the corner clamps to half the shorter side of the box. No magic string. No arbitrary 9999. Every pattern in Argyle's post rides the same trick: pin a value to as much as the algorithm allows by multiplying &lt;code&gt;infinity&lt;/code&gt; (or &lt;code&gt;-infinity&lt;/code&gt;) by a unit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The recipes
&lt;/h2&gt;

&lt;p&gt;Freeze on the last frame of an animation by giving its duration an effectively unlimited length:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;animation-duration&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;infinity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;s&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Force-to-the-top stacking without picking a random z-index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;z-index&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;infinity&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An unbounded &lt;code&gt;clamp()&lt;/code&gt; window, when the value only needs a constraint on one side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;inline-size&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;clamp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-infinity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;infinity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A backdrop that fills the viewport without you measuring it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;box-shadow&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;infinity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="err"&gt;#0005&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The boolean pattern
&lt;/h2&gt;

&lt;p&gt;The move that pays for the whole post is the boolean. You want a corner radius that stays 20px until the container gets narrow, then collapses to zero. Not a media query. Not a &lt;code&gt;switch()&lt;/code&gt;. Just a clamp whose middle term is a comparison multiplied by infinity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;border-radius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;clamp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="nt"&gt;cqi&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;infinity&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;100cqi&lt;/code&gt; is bigger than &lt;code&gt;100%&lt;/code&gt;, the middle term is a huge positive length and clamp picks the 20px cap. When it flips negative, the middle is a huge negative length and clamp picks the 0px floor. A comparison turned into a step function. No JavaScript, no query.&lt;/p&gt;

&lt;p&gt;Same shape with &lt;code&gt;sibling-count()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;--solo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;clamp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;2&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;sibling-count&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;infinity&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero when the element has two or more siblings, one otherwise. A boolean written in the cascade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the mechanism actually lives
&lt;/h2&gt;

&lt;p&gt;None of this is a new property. It is one keyword and the fact that &lt;code&gt;calc()&lt;/code&gt; and &lt;code&gt;clamp()&lt;/code&gt; will let you push a very large finite proxy through their arithmetic. The interesting move is not that there is an infinity. It is that CSS math functions are now expressive enough to encode both an upper bound and a boolean as ordinary values. The rest is style.&lt;/p&gt;

&lt;p&gt;Argyle embeds a baseline-status component next to the recipes so you can check where the pieces stand in the browsers you support. Check it before you ship &lt;code&gt;sibling-count()&lt;/code&gt; or a scroll-driven trigger range next to a pill radius, because those primitives arrived later than &lt;code&gt;calc()&lt;/code&gt; did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try one on your next layout
&lt;/h2&gt;

&lt;p&gt;Pick the pill. Delete the 9999. Write &lt;code&gt;calc(infinity * 1px)&lt;/code&gt; and mean it. Next time you reach for a media query to zero out a radius on a narrow card, try the clamp trick instead. The box will decide for itself, and you can leave the answer in one property.&lt;/p&gt;

</description>
      <category>css</category>
      <category>infinity</category>
      <category>calc</category>
      <category>clamp</category>
    </item>
    <item>
      <title>AlloyDB puts agents behind IAM groups so nobody has to share a database password again</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Sat, 01 Aug 2026 00:25:20 +0000</pubDate>
      <link>https://dev.to/leobaniak/alloydb-puts-agents-behind-iam-groups-so-nobody-has-to-share-a-database-password-again-49ii</link>
      <guid>https://dev.to/leobaniak/alloydb-puts-agents-behind-iam-groups-so-nobody-has-to-share-a-database-password-again-49ii</guid>
      <description>&lt;p&gt;Your AI agent has a database password. So do the other twelve agents, and so does the CI job that seeds their fixtures every night. They are all logging in as &lt;code&gt;svc_agents&lt;/code&gt;, they are all reading everything that role can read, and the audit log is going to tell you exactly nothing about which user asked the question that pulled a stranger's ledger into a prompt. On July 30, Google Cloud said the AlloyDB side of that story gets a lever it has been missing: IAM group authentication, in preview, mapping Cloud Identity and Workforce Identity Federation groups to database-level authorization. Cloud SQL has had this; AlloyDB is now catching up.&lt;/p&gt;

&lt;p&gt;Let me say what this actually is before the marketing softens the edges. It is not a new auth protocol. It is a way to stop writing &lt;code&gt;GRANT&lt;/code&gt; for individual humans and start writing it for a group whose membership lives in your IdP. The database checks the caller's group context at the managed-infrastructure layer, then decides at database or table scope whether the caller is in. Google says an organization can wire up to 200 such groups, and points at &lt;code&gt;financial-agents@company.com&lt;/code&gt; and &lt;code&gt;regional-analysts@company.com&lt;/code&gt; as the shape of the thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is actually shipping
&lt;/h2&gt;

&lt;p&gt;The change lands in preview. There is no new client for you to install and no new port to open. What moves is the model of "who is the caller?" Instead of a static database user provisioned by hand, the caller authenticates as themselves via IAM, and the database authorizes them by the groups they belong to. Cloud Identity and Workforce Identity Federation are both in scope, which matters if your workforce identities live in Okta or Entra and only ever visit Google through federation. Google also namechecks VPC Service Controls, Organization Policies, IAM conditions and Private Service Connect, all of which continue to bracket the perimeter around the connection itself. Group auth does not replace those; it slots in where the &lt;code&gt;pg_authid&lt;/code&gt; row used to sit.&lt;/p&gt;

&lt;p&gt;There is no GA date in the announcement, no pricing, and no promise of a specific set of Postgres role primitives on the other side of the mapping. If you were hoping for the exact SQL for "sync group X to role Y," that lives in the setup docs, not the launch post, and you should read it before you build a spike on top of a shape that will change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this changes the CI/CD conversation at all
&lt;/h2&gt;

&lt;p&gt;If your pipeline provisions a database, it also provisions how something logs into that database. Historically the answer has been Secret Manager plus a rotation job plus a promise that only the right workload can pull the right secret. That works, in the sense that it keeps humans out of the credential. It also means every workload that has any access has, in effect, one identity, and any bug that lets one workload talk out of turn drops you into a shared-blast-radius problem.&lt;/p&gt;

&lt;p&gt;The group model moves the trust boundary. The pipeline still provisions the database and still runs migrations, but the day-2 access control that governs what the running app can read is expressed as a group in IAM, not a password baked into a runner's environment. That is a real subtraction from the CI/CD attack surface. Fewer secrets to rotate. Fewer static tokens to leak. Fewer places where "the pipeline can read production" is a sentence anyone has to defend at a review.&lt;/p&gt;

&lt;p&gt;For AI agents specifically, the fix is more surgical. Google names the confused-deputy problem out loud: an agent that runs as a service account and acts on behalf of many end users cannot be audited per end user, and cannot be restricted per end user either. If the calling user's identity is threaded through the agent to the database session, the row-level policy on the other side gets the context it needed all along. That is the useful bit. Skepticism is still healthy: a confused deputy is a design problem, and a database check is only ever the last line, not the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you wire it up
&lt;/h2&gt;

&lt;p&gt;Assume the plumbing works. What still has to be true?&lt;/p&gt;

&lt;p&gt;You need your Postgres roles to reflect real policy, not the shape of your team org chart in 2024. Mapping a group to &lt;code&gt;db_reader&lt;/code&gt; is worth nothing if &lt;code&gt;db_reader&lt;/code&gt; can read PII from six schemas the group has no business touching. A group model rewards a role model that has been thought about; it does not do the thinking for you.&lt;/p&gt;

&lt;p&gt;You need to decide which side of the wire the group check lives on. Group membership resolved at connection time is not the same guarantee as group membership resolved per query, and long-lived connections are common in agent frameworks. Ask what happens when a user is removed from a group while an agent still holds an open session on their behalf. The docs will tell you; the launch post will not.&lt;/p&gt;

&lt;p&gt;You need a session-context contract for your agent framework. Passing the end-user identity down to the database is a distributed-systems problem before it is a Postgres problem: the agent has to receive the identity, refuse to run without one, and stamp it onto every downstream call. If that plumbing is optional, the database check is theatre.&lt;/p&gt;

&lt;p&gt;And you need to keep an eye on what the audit log actually captures. The announcement promises the log will name the data touched and the user on whose behalf it was touched, which is precisely the missing evidence in most agent incidents today. Verify that shape against your SIEM ingestion before you promise anyone a report.&lt;/p&gt;

&lt;h2&gt;
  
  
  The verdict
&lt;/h2&gt;

&lt;p&gt;Passwords for shared service accounts have been the smell in database access for years, and every generation of tooling has tried to hide the smell rather than remove it. Group auth of this shape (IAM in, roles out, identity threaded through agents) is the removal. It is also preview, it is one cloud, and it lives or dies by how honestly your role model was built. Wire it up on a non-critical schema first. Read the audit log. Ask which of your agents is still logging in as &lt;code&gt;svc_agents&lt;/code&gt;, and why.&lt;/p&gt;

&lt;p&gt;If the answer is "because it was easier," you now know what harder buys you.&lt;/p&gt;

</description>
      <category>iam</category>
      <category>databasesecurity</category>
      <category>aiagents</category>
      <category>identity</category>
    </item>
    <item>
      <title>Copilot in VS Code gives each agent session its own git worktree</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Fri, 31 Jul 2026 16:25:27 +0000</pubDate>
      <link>https://dev.to/leobaniak/copilot-in-vs-code-gives-each-agent-session-its-own-git-worktree-3j5n</link>
      <guid>https://dev.to/leobaniak/copilot-in-vs-code-gives-each-agent-session-its-own-git-worktree-3j5n</guid>
      <description>&lt;p&gt;The first time I let two Copilot agents work on the same repo at the same time, one of them stomped on the other's edits inside about ninety seconds. Different tasks, same working tree, same branch. I closed the second session, made a coffee, and went back to running them one at a time like a person from 2024. That is the small, unglamorous frustration the July release notes for Copilot in VS Code aim at. Between v1.127 and v1.131, shipped across July, the Agents window learned to start each session in its own git worktree, and it learned to hold several chats inside a single session without losing the plot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The change, in one paragraph
&lt;/h2&gt;

&lt;p&gt;The Agents window is still in public preview, and the July batch is a spread of small, connected improvements rather than one headline feature. The one I care about most: you can now start a Copilot, Claude, or Codex session in a git worktree, so each session works in an isolated copy of your repository. Multi-chat lands next to it: a single agent session can hold several related chats, each with its own history, title, and model, and you can fork a chat from any point to explore a different approach while the original context stays put. There is a new panel that opens files and diffs next to the conversation, per-file addition and deletion counts, and a toggle between inline and side-by-side diff views. Subagent runs now surface their model, elapsed time, and active tool call, and if a pull request the agent is minding fails a CI check or picks up a new review comment, a banner shows up above the chat input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the worktree bit is the one that matters for CI folks
&lt;/h2&gt;

&lt;p&gt;If you have never used &lt;code&gt;git worktree&lt;/code&gt;, the shape of it is simple: one repository, several checked-out working directories on different branches or commits, each with its own index. Two engineers can be halfway through two unrelated branches on the same machine without any stash-dance. Agents have quietly needed this for a while. An agent that runs a test suite, edits files, and commits is doing exactly what a second engineer would do, and the moment you have two of them, you want two working trees. Otherwise the second agent picks up half-written files from the first, or the first agent's CI run inherits changes it never intended, and you spend an afternoon wondering why a green local run went red in the pipeline.&lt;/p&gt;

&lt;p&gt;Per-session worktrees also change what "parallel" means when you are prototyping fixes for the same bug. Fork a chat, send the fork down a different path, keep both running. When one comes back with a cleaner diff, discard the other. You are effectively branching your thinking without branching your working tree by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small quality-of-life items that add up
&lt;/h2&gt;

&lt;p&gt;A handful of the smaller changes will show up in daily use faster than you expect.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start a chat message with &lt;code&gt;!&lt;/code&gt; to run its contents as a terminal command in the Agents window. Copy a failing command out of your build log, prefix it, hit send. It runs. The agent sees the output.&lt;/li&gt;
&lt;li&gt;Terminal diff output now recognises the standard Git prefixes (&lt;code&gt;i/&lt;/code&gt;, &lt;code&gt;w/&lt;/code&gt;) and lets you click through to the file. That is one less "which file was that" moment when reading &lt;code&gt;git diff&lt;/code&gt; in a terminal pane.&lt;/li&gt;
&lt;li&gt;Bring-your-own-key models are now available in the Agents window, catching it up to the editor, which has had BYOK since v1.99.&lt;/li&gt;
&lt;li&gt;Copilot Business and Enterprise users can see billing-cycle credit usage from the status menu, which is the sort of thing that stops being invisible only after you have blown through your budget once.&lt;/li&gt;
&lt;li&gt;Copilot vision is generally available. Paste an image into chat, drag one in, or add it from the context menu. Screenshot of a broken pipeline dashboard, straight into the conversation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Rough edges I noticed
&lt;/h2&gt;

&lt;p&gt;Two honest caveats. The Agents window itself is still preview, so anything specific to it can move under you between releases; if you build a team workflow around forked chats or subagent tracking today, expect to revisit it. And a few of the interesting-sounding items are still experimental: built-in dictation for chat, editor and terminal ships behind a &lt;code&gt;dictation.enabled&lt;/code&gt; setting, and the LLM-based transcript cleanup that removes filler words is behind &lt;code&gt;dictation.experimental.llmCleanup&lt;/code&gt;. Same story for Markdown editing inside the Agents window, gated as experimental. Nothing wrong with that, it just means "try it on a scratch repo first" rather than "wire it into your main branch flow tomorrow".&lt;/p&gt;

&lt;h2&gt;
  
  
  What I am watching next
&lt;/h2&gt;

&lt;p&gt;The worktree-per-session move quietly assumes something bigger: that a CI-adjacent agent is a first-class collaborator, and needs the same isolation a human collaborator does. Once that assumption is baked in, the next question is whose CI runs when the agent commits from its worktree, and against which policies. I want to see how the pull-request banner behaves the first time an agent's fork has a failing required check that only a human reviewer can bypass. If you have shipped anything with the July build already, I would love to hear how the shared tab bar has actually felt after a week. In my one afternoon with it, I stopped tab-switching to find the diff, which is usually the quiet sign that a release has done its job.&lt;/p&gt;

</description>
      <category>githubcopilot</category>
      <category>vscode</category>
      <category>agents</category>
      <category>devrel</category>
    </item>
    <item>
      <title>Kubernetes 1.37 beta lands a StatefulSet Recreate strategy and CRI pod checkpoint RPCs</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Fri, 31 Jul 2026 08:26:38 +0000</pubDate>
      <link>https://dev.to/leobaniak/kubernetes-137-beta-lands-a-statefulset-recreate-strategy-and-cri-pod-checkpoint-rpcs-2a95</link>
      <guid>https://dev.to/leobaniak/kubernetes-137-beta-lands-a-statefulset-recreate-strategy-and-cri-pod-checkpoint-rpcs-2a95</guid>
      <description>&lt;h2&gt;
  
  
  The headline changes
&lt;/h2&gt;

&lt;p&gt;Kubernetes v1.37.0-beta.0 dropped this week during the release code freeze, and for anyone who owns rollout code the two beats that matter are a new StatefulSet update strategy and a pod-level checkpoint and restore RPC on the CRI interface. The release also promotes PLEGOnDemandRelist and InPlacePodVerticalScalingInitContainers to GA, moves ClusterTrustBundle to stable, and cuts metrics.k8s.io over to v1. A v1.36.3 patch landed alongside it with a fix for a kubelet memory-leak regression that first slipped in during 1.36.&lt;/p&gt;

&lt;p&gt;Per Last Week in Kubernetes Development, both builds use Go 1.26.5 and default to etcd 3.7.0. Code freeze is on. Feature blogs are due August 3, docs freeze on August 5, and the cherry-pick deadline for the next set of patches is August 7.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recreate for StatefulSets, and why deployment tooling has to notice
&lt;/h2&gt;

&lt;p&gt;Until now StatefulSets have had two update strategies to pick from, RollingUpdate and OnDelete. The 1.37 beta adds a third, Recreate, which tears the set down before bringing the new version up. That is the same shape as a Deployment's Recreate strategy, applied to workloads with stable network identity and attached storage.&lt;/p&gt;

&lt;p&gt;The operational read is simple. A rolling update on a StatefulSet has always been the least-bad option for stateful workloads that cannot briefly run mixed versions. Recreate makes the mixed-version window impossible, at the cost of accepting a real downtime window on rollout. That is a valid answer for singletons and for workloads that must never have two versions talking to the same data at once. It is also a footgun for anything that quietly relied on rolling to hide restart cost. If your GitOps sync policy assumes rolling semantics on every StatefulSet in the cluster, audit the manifests before you pin to 1.37 anywhere you cannot easily roll back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pod checkpoint and restore, in the CRI itself
&lt;/h2&gt;

&lt;p&gt;KEP-5823 adds two RPCs to the v1.RuntimeService CRI interface, CheckpointPod and RestorePod. The interface change is worth flagging on its own. It moves what was a kubelet-side and container-runtime-side experiment into the contract that every conforming runtime has to answer, which is the layer where node agents and platform migration tools live.&lt;/p&gt;

&lt;p&gt;The obvious use cases are live pod migration between nodes and long-lived debug workflows where you snapshot state before an intrusive change. The hazards are the same ones that have kept CRIU-style container checkpointing out of most production pipelines up to now: secrets, open file descriptors, and network state that do not survive a serialize and deserialize round trip. The API is landing in beta. Assume the tooling that consumes it will land later, and read the runtime notes before you promise anyone a hot migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else is worth pinning
&lt;/h2&gt;

&lt;p&gt;A short list of the smaller items in v1.37.0-beta.0 that will show up in someone's postmortem eventually.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PLEGOnDemandRelist goes GA. This changes the kubelet's pod lifecycle event loop from a fixed-cadence relist to on-demand, and it is the kind of change that closes one class of stuck-pod bug and opens another. Read the changelog before you shrug at it.&lt;/li&gt;
&lt;li&gt;InPlacePodVerticalScalingInitContainers goes GA. Vertical scaling on init containers without a pod restart is now a supported path, which matters for workloads whose init containers do heavy migration work.&lt;/li&gt;
&lt;li&gt;metrics.k8s.io moves to v1, and ClusterTrustBundle goes stable. Autoscaling controllers and workload identity code that pinned to the beta APIs should be checked.&lt;/li&gt;
&lt;li&gt;PersistentVolumeClaimUnusedSinceTime is on by default in beta. It surfaces PVCs that have not been mounted recently, which is useful for cost work and dangerous if a cron consumer of a PVC has been off for a while.&lt;/li&gt;
&lt;li&gt;A new "/allocatedPods" kubelet endpoint (KEP-6030) exposes what the kubelet believes it has committed to, distinct from what is actually running. That is a real gap for on-call debugging today.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The 1.36.3 note, and where it bites
&lt;/h2&gt;

&lt;p&gt;The v1.36.3 patch is not the headline but it is the one to backport first. It rolls up fixes for DRA scheduling and allocator bugs, the kubelet memory-leak regression that landed during 1.36, server-side apply regressions, and kubeadm etcd learner promotion and join resiliency improvements. If you are still on 1.36 with a leaking kubelet you already know why.&lt;/p&gt;

&lt;p&gt;The Recreate strategy and the CRI checkpoint API are the two changes to test against your deployment stack before 1.37 hits any cluster you have to keep alive at 3am. The rest turn boring once they are shipped, which is the point of a beta.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>release</category>
      <category>statefulset</category>
      <category>cri</category>
    </item>
    <item>
      <title>Route and navigation matching, proposed for CSS</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Fri, 31 Jul 2026 04:08:21 +0000</pubDate>
      <link>https://dev.to/leobaniak/route-and-navigation-matching-proposed-for-css-3bnj</link>
      <guid>https://dev.to/leobaniak/route-and-navigation-matching-proposed-for-css-3bnj</guid>
      <description>&lt;p&gt;You want a slide-left when the user goes from home to about, and a slide-right on the way back. You have multi-page View Transitions in your corner. So you write a &lt;code&gt;pagereveal&lt;/code&gt; listener, read &lt;code&gt;location&lt;/code&gt;, match URL patterns, set &lt;code&gt;types&lt;/code&gt; in script, and remember to set the outbound side in &lt;code&gt;pageswap&lt;/code&gt;. Behaviour lives in JS. The stylesheet only gets to react to whatever names you managed to pin on the DOM in time.&lt;/p&gt;

&lt;p&gt;Bramus's write-up on the new CSS Route and Navigation Matching proposal (draft name css-navigation-1, co-authored with Noam Rosenthal and David Baron) flips that. URLs become named things the stylesheet already knows about, and the transition rules read like any other conditional CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming routes with &lt;a class="mentioned-user" href="https://dev.to/route"&gt;@route&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@route&lt;/code&gt; gives a URL pattern a name. The name is a custom identifier (double-dash prefixed) that other rules can point at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@route&lt;/span&gt; &lt;span class="n"&gt;--home&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url-pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@route&lt;/span&gt; &lt;span class="n"&gt;--about&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url-pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'/about'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@route&lt;/span&gt; &lt;span class="n"&gt;--detail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url-pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'/detail/:id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The matching engine inside &lt;code&gt;url-pattern()&lt;/code&gt; is &lt;code&gt;path-to-regexp&lt;/code&gt;, the same syntax the JavaScript &lt;code&gt;URLPattern&lt;/code&gt; API already uses, so &lt;code&gt;/detail/:id&lt;/code&gt; means what you'd expect. The proposal lists &lt;code&gt;protocol&lt;/code&gt;, &lt;code&gt;hostname&lt;/code&gt;, &lt;code&gt;port&lt;/code&gt;, &lt;code&gt;pathname&lt;/code&gt;, &lt;code&gt;search&lt;/code&gt; and &lt;code&gt;hash&lt;/code&gt; as descriptors, so a single &lt;code&gt;@route&lt;/code&gt; can constrain more than the path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Querying the current move
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@navigation&lt;/code&gt; is where you ask "which navigation is happening right now?" using &lt;code&gt;from:&lt;/code&gt; and &lt;code&gt;to:&lt;/code&gt; conditions against your named routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@navigation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--home&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--about&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@view-transition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;slide-all-to-left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@navigation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--about&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--home&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@view-transition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;slide-all-to-right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that as a CSS-native replacement for the pageswap/pagereveal branch. No JS listener, no manual &lt;code&gt;location.pathname&lt;/code&gt; matching, no imperative call to set the transition types. The types are declared next to the routes that trigger them.&lt;/p&gt;

&lt;p&gt;Two more keywords widen the net. &lt;code&gt;between&lt;/code&gt; matches both directions of a pair, and &lt;code&gt;at&lt;/code&gt; narrows to one side of that pair (the outgoing side or the incoming side), so you can bracket an entire round trip in one block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@navigation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;between&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--home&lt;/span&gt; &lt;span class="n"&gt;--detail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@navigation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--home&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;:nav-source&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;view-transition-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;@navigation&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--detail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;img&lt;/span&gt;&lt;span class="nf"&gt;#hero&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;view-transition-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Two selectors that finally see the navigation
&lt;/h2&gt;

&lt;p&gt;The block above leans on &lt;code&gt;:nav-source&lt;/code&gt;, a pseudo-class that matches the element which initiated the outgoing navigation. It's modelled on &lt;code&gt;NavigateEvent.sourceElement&lt;/code&gt;. That's the piece the declarative approach has been missing: the clicked thumbnail can pick up its own &lt;code&gt;view-transition-name&lt;/code&gt; at the exact moment the transition is being captured, without a script traversing the DOM to hand-tag it first.&lt;/p&gt;

&lt;p&gt;The companion is &lt;code&gt;:link-to()&lt;/code&gt;, a functional selector that matches links by where they'd go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:link-to&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--detail&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;hotpink&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A route name goes in, and every &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; pointing at a URL that matches that route gets styled. Bramus flags that parameter-scoped matching (only &lt;code&gt;--detail&lt;/code&gt; routes where &lt;code&gt;id&lt;/code&gt; equals &lt;code&gt;6&lt;/code&gt;, for example) is still being worked on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this sits
&lt;/h2&gt;

&lt;p&gt;None of the above ships in a browser yet. It's a draft spec on the way to the CSS Working Group F2F meeting in Berlin the week after the post, following the initial CSSWG introduction in January and a developer feedback session at CSS Day in June. The interesting bit for anyone building multi-page apps is the shape it commits to: URLs become part of the same declarative surface as media queries, container queries and &lt;code&gt;@view-transition&lt;/code&gt;. The pageswap/pagereveal handlers stay in the platform, but the common cases stop needing them.&lt;/p&gt;

&lt;p&gt;Two things worth trying in your head before you agree with the direction. First, custom-ident routes are cheap to add (&lt;code&gt;--home&lt;/code&gt;, &lt;code&gt;--detail&lt;/code&gt;, &lt;code&gt;--auth&lt;/code&gt;) but they're global, and route-name collisions across a large codebase will feel like &lt;code&gt;z-index&lt;/code&gt; bugs unless the working group nails a scoping story. Second, the moment &lt;code&gt;:link-to()&lt;/code&gt; lands, "style outbound links to the checkout differently" stops being a data-attribute exercise. That's a small win with a lot of surface area.&lt;/p&gt;

</description>
      <category>css</category>
      <category>viewtransitions</category>
      <category>navigation</category>
      <category>cssworkinggroup</category>
    </item>
    <item>
      <title>Image verification, one layer below admission</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Fri, 31 Jul 2026 00:25:35 +0000</pubDate>
      <link>https://dev.to/leobaniak/image-verification-one-layer-below-admission-17mg</link>
      <guid>https://dev.to/leobaniak/image-verification-one-layer-below-admission-17mg</guid>
      <description>&lt;p&gt;Every admission webhook you rely on has an escape hatch. Nothing scandalous about that, it is just how Kubernetes wires them up. A CNCF post on July 30 pitches a way out: move signature and attestation checking out of the API server and into the container runtime itself, via a Node Resource Interface plugin.&lt;/p&gt;

&lt;p&gt;The pitch, in one paragraph. The Supply Chain NRI Plugin hooks CreateContainer events on the runtime (CRI-O or containerd), pulls the image reference and digest from the runtime annotations, fetches attestations from the OCI registry, and verifies them against a per-namespace policy before the container starts. It checks three artefact types: SLSA provenance, VEX documents, and VSA. If verification fails, the container does not start. The hook fires at create-time, not at image pull, so an image that has been sitting on a node for hours still gets checked before it actually runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why admission is not the whole story
&lt;/h2&gt;

&lt;p&gt;You already know the failure modes. The CNCF post names four. Static pods managed directly by the kubelet skip admission entirely (the mirror pod can fail, the container still runs). Anyone with direct kubelet access bypasses the API server on the way in. Misconfigured namespace selectors silently exempt whole namespaces. And an admission webhook outage forces a bad choice: cluster lockup or silent bypass.&lt;/p&gt;

&lt;p&gt;Kyverno, OPA Gatekeeper and Sigstore Policy Controller all live at the API layer, so they all inherit those escape hatches. The layer is the problem, not the projects. Moving the check down into the runtime closes the loop: every container has to pass through the runtime, whatever path scheduled it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The catch
&lt;/h2&gt;

&lt;p&gt;Runtime-level verification is not a free lunch. You now depend on a plugin being healthy on every node, with the credentials and network path to reach the OCI registry, on every CreateContainer call. Fail open and you have compliance theatre. Fail closed and a registry blip becomes a cluster-wide launch stall. The post does not spell out what the plugin does when the registry is unreachable, or how a policy update rolls out to nodes mid-flight. You are going to want the answer to both before you enable this on anything you care about.&lt;/p&gt;

&lt;p&gt;Verdict: the trust boundary moves in the right direction, and the four bypass paths are real. Bring your own runbook for what happens when the registry has a bad afternoon.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>supplychain</category>
      <category>policy</category>
      <category>attestations</category>
    </item>
    <item>
      <title>GTIG and Mandiant publish a supply-chain hardening playbook aimed at CI/CD teams</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Thu, 30 Jul 2026 16:25:51 +0000</pubDate>
      <link>https://dev.to/leobaniak/gtig-and-mandiant-publish-a-supply-chain-hardening-playbook-aimed-at-cicd-teams-1efn</link>
      <guid>https://dev.to/leobaniak/gtig-and-mandiant-publish-a-supply-chain-hardening-playbook-aimed-at-cicd-teams-1efn</guid>
      <description>&lt;p&gt;Your build pipeline runs code you did not write, from repositories you do not control, with credentials that can push to production. Fine. That is the deal. On July 30, Google's Threat Intelligence Group (GTIG) and Mandiant published a joint mitigation guide for software supply chain compromise, and the interesting bit is not the threat catalog. It is how much of the fix has to happen inside your CI/CD.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the guide is doing on your desk
&lt;/h2&gt;

&lt;p&gt;The write-up, credited to Kelli Vanderlee and Stuart Carrera, opens with a lineup of recent campaigns: a compromise of the widely-used &lt;code&gt;axios&lt;/code&gt; npm package attributed to an actor GTIG tracks as MIDNIGHT NEPTUNE, a longer sequence of typosquatted PyPI, npm and Docker Hub packages from UNC6780, and installer-tampering incidents against Notepad++ and DAEMON Tools. GTIG reports malicious open source package volume grew 1,444% from 2024 to 2025. The &lt;code&gt;axios&lt;/code&gt; malicious versions were pulled from npm within three hours; GTIG still supported customers across at least 15 industry verticals and 13 countries.&lt;/p&gt;

&lt;p&gt;The recommendations sit in four buckets: administrative oversight, vendor lifecycle management, security architecture and engineering controls, and continuous verification and response. That framing is useful for a CISO deck. It is a bit less useful if you are the engineer who has to change the workflow files by Friday.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the pipeline levers actually sit
&lt;/h2&gt;

&lt;p&gt;Strip the categories away and most of the concrete advice lands in familiar places.&lt;/p&gt;

&lt;p&gt;Identity moves first. The guide recommends replacing static, long-lived Personal Access Tokens with dedicated GitHub Apps or short-lived tokens issued via federated OIDC, capping any PATs that remain at a seven-day lifespan, and requiring hardware-backed FIDO2 keys for CLI actions against your repositories. It flags high-privilege triggers like &lt;code&gt;pull_request_target&lt;/code&gt; as something to restrict rather than leave enabled by default. If your Actions runners are shared, GTIG wants them ephemeral: spin up, run one job, tear down.&lt;/p&gt;

&lt;p&gt;Package ingestion is the other half. For Node.js, the guide points at npm and pnpm's &lt;code&gt;minimumReleaseAge&lt;/code&gt; setting (at least 24 hours, or 1440 minutes) so a newly-published version cannot land in a build before anyone has looked at it. It calls for &lt;code&gt;ignore-scripts=true&lt;/code&gt; in &lt;code&gt;.npmrc&lt;/code&gt; to stop postinstall from doing whatever it wants, and cites npm v12's July 2026 change to disable lifecycle scripts by default. For Python, it recommends routing installs through a vetted private index rather than the public PyPI. Two platform-level guardrails from July 2026 get name-checked: Dependabot's default three-day cooldown on version updates and PyPI's 14-day file-upload immutability window.&lt;/p&gt;

&lt;p&gt;Verification is prosaic and unglamorous. Use &lt;code&gt;npm ci&lt;/code&gt; for immutable installs. Run OSV-Scanner in the pipeline. Verify vendor signatures. Restrict runner egress to an allowlist. Add SBOM tracking and, GTIG suggests, an "action bill of materials" inventorying every third-party GitHub Action a workflow depends on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable parts
&lt;/h2&gt;

&lt;p&gt;Most of this is not new. The &lt;code&gt;ignore-scripts=true&lt;/code&gt; recommendation has been in npm's own docs for years. OIDC-federated CI credentials are supported across the major platforms and still only partly adopted. Package cooldowns were the fix everyone reached for after the historical &lt;code&gt;chalk&lt;/code&gt; and &lt;code&gt;debug&lt;/code&gt; hijackings, and they still catch teams flat-footed. What GTIG is really publishing here is a homework list, consolidated, with an incident roster behind it that is hard to argue with.&lt;/p&gt;

&lt;p&gt;Two things worth calling out. The guide leans on the GitHub plus npm plus PyPI stack that most CI/CD teams live in; if your pipeline is JVM-first or Go-first, you will have to translate. And an "action bill of materials" is a nice control on paper. In practice it is another artifact your team has to generate, store, sign and actually consult during an incident. Skip it if you are not going to look at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the wider toolchain answers this
&lt;/h2&gt;

&lt;p&gt;The controls the guide asks for already ship, in pieces, across the ecosystem. Honest picks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OSV-Scanner&lt;/strong&gt; is Google's own reference frontend to the OSV.dev vulnerability database, and it is genuinely straightforward to wire into a CI job. If you want to follow one recommendation from this guide today, this is the low-cost first step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependabot&lt;/strong&gt; and &lt;strong&gt;Renovate&lt;/strong&gt; cover the automated-updates side. Renovate is the better fit if you need cross-registry support and fine-grained cooldown policies without waiting for a platform default; Dependabot is the answer if you live entirely inside GitHub and want less to configure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sigstore&lt;/strong&gt; plus SLSA attestations answer the "verify signatures" bullet, once you have somewhere downstream that actually checks them. Generation is the easy part; verification is where deployments still quietly skip the check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifactory&lt;/strong&gt; and &lt;strong&gt;Sonatype Nexus&lt;/strong&gt; are the mainstream answers for a vetted private index. If a regulator cares about your controlled software supply chain, these have the audit story your platform team will be asked for. They are a heavier lift than any of the above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chainguard Images&lt;/strong&gt; and hardened base images address the container side of the same problem: what you pull down before a build even runs. Not a replacement for any of the above; a layer beneath.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://buddy.works/?utm_source=cicd.deployment.to&amp;amp;utm_medium=referral&amp;amp;utm_campaign=gtig-mandiant-supply-chain-guide" rel="noopener noreferrer"&gt;Buddy&lt;/a&gt;&lt;/strong&gt; exposes ephemeral runners, per-pipeline OIDC credentials and per-step secret scoping as YAML you edit in one place, which is one concrete reason to consider it if the guide's identity bullets are what you are trying to close first. It is not a scanner and not a signer. It is the runner and the wiring around them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your priority is scanner depth or attestation policy, one of the specialist tools above is the better fit; nothing about a pipeline platform closes a CVE for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bit worth remembering
&lt;/h2&gt;

&lt;p&gt;Every recommendation in the GTIG guide is a control that already exists in your stack. The reason your build still runs a stolen token as root is not that the mitigations do not ship. It is that nobody enforced them. Read the list, pick two, and turn them on before the next &lt;code&gt;axios&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>supplychainsecurity</category>
      <category>gtig</category>
      <category>mandiant</category>
      <category>cicdsecurity</category>
    </item>
    <item>
      <title>Google's borderless lakehouse wires AWS, Databricks and Snowflake catalogs into one Iceberg REST surface</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Thu, 30 Jul 2026 08:26:45 +0000</pubDate>
      <link>https://dev.to/leobaniak/googles-borderless-lakehouse-wires-aws-databricks-and-snowflake-catalogs-into-one-iceberg-rest-7a6</link>
      <guid>https://dev.to/leobaniak/googles-borderless-lakehouse-wires-aws-databricks-and-snowflake-catalogs-into-one-iceberg-rest-7a6</guid>
      <description>&lt;p&gt;The scene I keep replaying is from earlier this month. An agent in one of our data pipelines needed a metric that lived in Snowflake, a fact table that sat in an AWS bucket behind Glue, and one Delta table in Databricks that was the source of truth for a currency conversion. Three catalogs, three IAM stories, three flavours of "please copy this into BigQuery first." That copy step is the whole reason your data engineers quietly stopped enjoying weekends. Google Cloud's announcement this week, filed under the name "borderless Lakehouse," is a bet that the copy step can go away, at least for reads, and that it should go away first for the agents already wired into your pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually landed, in one paragraph
&lt;/h2&gt;

&lt;p&gt;Google Cloud is putting three things into preview and one thing into general reach. Catalog federation for AWS Glue, Databricks Unity Catalog and Snowflake Horizon is in preview, sitting on top of an Iceberg REST catalog. A Knowledge Catalog, described as an agentic context engine that syncs with those same three external catalogs, is in preview. Cross-cloud interconnects gain a flat-rate pricing tier from 1G to 100G, and Google says variable egress for AWS reads over that link goes to zero. The one thing outside preview language is the Data Agent Kit, an open-source toolkit with Model Context Protocol tools for BigQuery, Managed Service for Apache Spark and Cloud Storage. Everything else in the post reads as extensions of these primitives.&lt;/p&gt;

&lt;h2&gt;
  
  
  The piece that changes the wiring
&lt;/h2&gt;

&lt;p&gt;I care most about the Iceberg REST catalog underneath. If your data warehouse can be reached through an Iceberg-shaped API, then the federation story stops being "please install this connector." BigQuery, Managed Spark and, per Google, transactional systems like AlloyDB via Lakehouse Federation can point at that surface and see tables that live in the other clouds' catalogs. The company also names Spanner Omni, a Spanner deployment that runs outside Google Cloud, as part of the same story. There is also an intelligent cross-cloud caching layer that keeps repeatedly-read fragments warm on Google's side so you are not paying the round trip on every query.&lt;/p&gt;

&lt;p&gt;The practical version is that a pipeline task that used to be "copy from Snowflake into BigQuery, then read" becomes "read." That is the whole design intent. Whether it is that clean in production is what previews are for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the agents come in
&lt;/h2&gt;

&lt;p&gt;The Data Agent Kit is where this stops being a data-warehouse story and starts being a CI-adjacent one. The kit is open source and ships MCP tools for BigQuery, Managed Spark and Cloud Storage, which is the same protocol your coding agents and IDE hosts already speak. Google also mentions a Conversational Analytics API for building your own agents, and a Gemini Enterprise integration that lets business users query across the federated set in natural language. On the guardrail side, BigQuery gains pre-query token estimation, per-query limits and a cost-optimised mode selector for AI functions. Google claims a 230x reduction in token consumption when the cost-optimised mode is picked. I would treat that number as a vendor claim until you have run it against your own workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rough edges I would want you to see
&lt;/h2&gt;

&lt;p&gt;Three of them, plainly. First, "preview" is doing a lot of work in this post. Catalog federation, the Knowledge Catalog metadata ingestion for external catalogs, and the runtime catalog auto-sync are all in preview at launch, so pinning production reads to any of those is a bet, not a decision. Second, cross-cloud interconnect pricing is flat-rate on the link itself, but the announcement is careful about which egress bill it zeroes out and which it does not. Read the fine print before you tell finance the AWS bill is about to drop. Third, the Knowledge Catalog is pitched as agent-facing context: it auto-translates raw schemas to business terminology and indexes column-level lineage. That is exactly the layer where a wrong synonym or a stale lineage edge turns into a wrong answer with a very confident tone. Whatever review flow you have for prompt context should apply here from day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I am watching next
&lt;/h2&gt;

&lt;p&gt;Two things. Whether the Iceberg REST layer stays honest as more external systems bolt onto it, because REST catalogs that quietly grow proprietary extensions are how portability dies. And whether the Data Agent Kit's MCP tools stabilise fast enough that a CI job can lock a specific tool version the way it locks an SDK, so an agent-driven pipeline does not surprise you on a Monday. If those two hold, the "copy first" step is on its way out for a lot of us, and my next long weekend gets a little more likely.&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>iceberg</category>
      <category>catalogfederation</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
