<?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: puppet</title>
    <description>The latest articles tagged 'puppet' on DEV Community.</description>
    <link>https://dev.to/t/puppet</link>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tag/puppet"/>
    <language>en</language>
    <item>
      <title>Puppetlabs Modules Roundup – June 2026</title>
      <dc:creator>Jason St-Cyr</dc:creator>
      <pubDate>Thu, 09 Jul 2026 19:38:46 +0000</pubDate>
      <link>https://dev.to/puppet/puppetlabs-modules-roundup-june-2026-23da</link>
      <guid>https://dev.to/puppet/puppetlabs-modules-roundup-june-2026-23da</guid>
      <description>&lt;p&gt;In June 2026 we saw the release of a new &lt;code&gt;stdlib 10&lt;/code&gt; that was mentioned &lt;a href="https://dev.to/puppet/what-you-need-to-know-about-the-new-puppetlabs-stdlib-10-in-june-2026-1jah"&gt;in an earlier article&lt;/a&gt; along with numerous modules bumped to use the new stdlib 10. In total, 24 Puppetlabs modules were released to the Forge, ranging from a brand-new Windows security policy module to a large batch of third-party CVE fixes in Security Compliance Management. This roundup pulls the most important changes into one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Highlighted Updates
&lt;/h2&gt;

&lt;h3&gt;
  
  
  New Module: Windows Local Security Policy Management
&lt;/h3&gt;

&lt;p&gt;The new &lt;a href="https://forge.puppet.com/modules/puppetlabs/security_policy/readme" rel="noopener noreferrer"&gt;security_policy module&lt;/a&gt; manages Windows local security policy using the Puppet Resource API, replacing manual &lt;code&gt;secedit&lt;/code&gt;/Local Security Policy editor work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides the &lt;code&gt;security_option&lt;/code&gt; and &lt;code&gt;user_right_assignment&lt;/code&gt; resource types, covering all 45 Windows Privilege Rights and the System Access settings.&lt;/li&gt;
&lt;li&gt;Ships a well-known SID map with a PowerShell fallback for domain and custom accounts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  stdlib 10.x Compatibility Pass
&lt;/h3&gt;

&lt;p&gt;A coordinated maintenance pass loosened the puppetlabs/stdlib dependency constraint across the module set to allow stdlib 10.x, clearing the way for downstream modules to pick up stdlib's latest release.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Affected modules: accounts, apache, apt, chocolatey, concat, docker, firewall, haproxy, inifile, lvm, motd, mysql, ntp, postgresql, wsus_client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More modules will be released in July as we roll out the support for stdlib 10.x.&lt;/p&gt;

&lt;h3&gt;
  
  
  Puppet Core Alignment / Puppet 7 Support Dropped
&lt;/h3&gt;

&lt;p&gt;Several modules completed their Puppet Core alignment pass this month, dropping Puppet 7 support in favor of Puppet 8 as Puppet 7 reaches end-of-life.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Affected modules: accounts, chocolatey, haproxy, java, motd, mysql, postgresql, stdlib.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A note that the change to drop Puppet 7 in postgresql was done in a patch release, instead of a major release. This has been corrected in postgresql 10.6.3 and the change will be done again in a major release of that module.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Compliance Management Patches ~40 CVEs
&lt;/h3&gt;

&lt;p&gt;Security Compliance Management 3.8.0, shipped as both &lt;code&gt;comply&lt;/code&gt; and &lt;code&gt;complyadm&lt;/code&gt;, updates a long list of bundled third-party components — Gorm.io, Protobuf, several Netty codec libraries, react-router, and KeyCloak — to close out roughly 40 CVEs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Also updates the bundled CIS-CAT Pro Assessor to v4.63.0, adding new STIG benchmarks for Amazon Linux 2023, Windows 11, Oracle Linux 9, and RHEL 10.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Updates Happened to Puppetlabs Modules in June 2026?
&lt;/h2&gt;

&lt;p&gt;The following is an alphabetical listing of modules which received updates in June 2026. If a module had multiple versions released, the updates are collected together, numbered with the "latest" version available.&lt;/p&gt;




&lt;h3&gt;
  
  
  accounts 9.0.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-29 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/accounts" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This release drops Puppet 7 support (&lt;strong&gt;BREAKING&lt;/strong&gt;) as part of the module's Puppet Core alignment work, and allows the stdlib dependency to move to 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(CAT-2352) Drop Puppet 7 support (BREAKING) — Puppet Core alignment &lt;a href="https://github.com/puppetlabs/puppetlabs-accounts/pull/509" rel="noopener noreferrer"&gt;#509&lt;/a&gt; (&lt;a href="https://github.com/LukasAud" rel="noopener noreferrer"&gt;LukasAud&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-accounts/pull/513" rel="noopener noreferrer"&gt;#513&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  apache 13.2.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-28 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/apache" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Adds RHEL 10 support, restores the ModSecurity engine on RHEL 10 via EPEL, and allows both stdlib and concat to move to their 10.x releases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add missing parameters to mod_md &lt;a href="https://github.com/puppetlabs/puppetlabs-apache/pull/2621" rel="noopener noreferrer"&gt;#2621&lt;/a&gt; (&lt;a href="https://github.com/smortex" rel="noopener noreferrer"&gt;smortex&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11851) Restore ModSecurity engine on RHEL 10 via EPEL &lt;a href="https://github.com/puppetlabs/puppetlabs-apache/pull/2635" rel="noopener noreferrer"&gt;#2635&lt;/a&gt; (&lt;a href="https://github.com/SugatD" rel="noopener noreferrer"&gt;SugatD&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11739) Add RHEL 10 support &lt;a href="https://github.com/puppetlabs/puppetlabs-apache/pull/2629" rel="noopener noreferrer"&gt;#2629&lt;/a&gt; (&lt;a href="https://github.com/SugatD" rel="noopener noreferrer"&gt;SugatD&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-apache/pull/2632" rel="noopener noreferrer"&gt;#2632&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;puppetlabs/concat: Allow 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-apache/pull/2630" rel="noopener noreferrer"&gt;#2630&lt;/a&gt; (&lt;a href="https://github.com/bastelfreak" rel="noopener noreferrer"&gt;bastelfreak&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  apt 11.3.2
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-26 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/apt" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;A small release that only bumps the stdlib dependency to allow 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-apt/pull/1288" rel="noopener noreferrer"&gt;#1288&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  aws_inventory 0.8.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-17 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/aws_inventory" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This release allows the ruby_task_helper dependency to move to 1.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow ruby_task_helper 1.x &lt;a href="https://github.com/puppetlabs/puppetlabs-aws_inventory/pull/25" rel="noopener noreferrer"&gt;#25&lt;/a&gt; (&lt;a href="https://github.com/bastelfreak" rel="noopener noreferrer"&gt;bastelfreak&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  chocolatey 9.0.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-29 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/chocolatey" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Completes its Puppet Core alignment pass, improves package prefetch caching with case-insensitive matching, and allows stdlib 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(CAT-2369) Puppet Core update &lt;a href="https://github.com/puppetlabs/puppetlabs-chocolatey/pull/378" rel="noopener noreferrer"&gt;#378&lt;/a&gt; (&lt;a href="https://github.com/LukasAud" rel="noopener noreferrer"&gt;LukasAud&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11769) Cache prefetch results and match packages case-insensitively &lt;a href="https://github.com/puppetlabs/puppetlabs-chocolatey/pull/388" rel="noopener noreferrer"&gt;#388&lt;/a&gt; (&lt;a href="https://github.com/skyamgarp" rel="noopener noreferrer"&gt;skyamgarp&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-chocolatey/pull/387" rel="noopener noreferrer"&gt;#387&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  comply 3.8.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-12 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/comply" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Security Compliance Management 3.8.0 addresses roughly 40 CVEs across bundled third-party components, alongside operational fixes and licensing improvements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased the CIS-CAT Pro Assessor license expiry time to a full year.&lt;/li&gt;
&lt;li&gt;Added a &lt;code&gt;license_path&lt;/code&gt; parameter to update the CIS-CAT Pro Assessor license without upgrading SCM.&lt;/li&gt;
&lt;li&gt;Added an &lt;code&gt;assessor_scan_timeout&lt;/code&gt; option to control the task timeout for Windows 2022 domain controllers.&lt;/li&gt;
&lt;li&gt;Added a background scan sweeper to detect and cancel scans stuck in the "running" state.&lt;/li&gt;
&lt;li&gt;Fixed a race condition where timed-out PE job status polls could leave scans permanently stuck.&lt;/li&gt;
&lt;li&gt;Updated Gorm.io, Protobuf, multiple Netty codec libraries, react-router, and KeyCloak to address roughly 40 CVEs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the official &lt;a href="https://help.puppet.com/scm/current/Content/UserGuide/SCM/Release_notes/release_notes.htm#SecurityComplianceManagement380" rel="noopener noreferrer"&gt;release notes for comply 3.8.0&lt;/a&gt; for the full details.&lt;/p&gt;




&lt;h3&gt;
  
  
  complyadm 3.8.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-12 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/complyadm" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Ships the same Security Compliance Management 3.8.0 update as &lt;code&gt;comply&lt;/code&gt;, covering the same ~40 CVE remediations and operational fixes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased the CIS-CAT Pro Assessor license expiry time to a full year.&lt;/li&gt;
&lt;li&gt;Added a &lt;code&gt;license_path&lt;/code&gt; parameter to update the CIS-CAT Pro Assessor license without upgrading SCM.&lt;/li&gt;
&lt;li&gt;Added an &lt;code&gt;assessor_scan_timeout&lt;/code&gt; option to control the task timeout for Windows 2022 domain controllers.&lt;/li&gt;
&lt;li&gt;Added a background scan sweeper to detect and cancel scans stuck in the "running" state.&lt;/li&gt;
&lt;li&gt;Fixed a race condition where timed-out PE job status polls could leave scans permanently stuck.&lt;/li&gt;
&lt;li&gt;Updated Gorm.io, Protobuf, multiple Netty codec libraries, react-router, and KeyCloak to address roughly 40 CVEs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the official &lt;a href="https://help.puppet.com/scm/current/Content/UserGuide/SCM/Release_notes/release_notes.htm#SecurityComplianceManagement380" rel="noopener noreferrer"&gt;release notes for complyadm 3.8.0&lt;/a&gt; for the full details.&lt;/p&gt;




&lt;h3&gt;
  
  
  concat 10.0.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-25 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/concat" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This targeted release was part of the wave of bumps for the stdlib dependency to allow 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-concat/pull/837" rel="noopener noreferrer"&gt;#837&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  docker 10.4.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-28 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/docker" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Removes the upper version limit on the puppetlabs/apt dependency, fixes &lt;code&gt;compose up&lt;/code&gt; argument ordering, and allows stdlib 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do not limit puppetlabs/apt requirement &amp;lt; v12 &lt;a href="https://github.com/puppetlabs/puppetlabs-docker/pull/1056" rel="noopener noreferrer"&gt;#1056&lt;/a&gt; (&lt;a href="https://github.com/mpdude" rel="noopener noreferrer"&gt;mpdude&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fix argument order on compose up &lt;a href="https://github.com/puppetlabs/puppetlabs-docker/pull/1037" rel="noopener noreferrer"&gt;#1037&lt;/a&gt; (&lt;a href="https://github.com/deligatedgeek" rel="noopener noreferrer"&gt;deligatedgeek&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-docker/pull/1057" rel="noopener noreferrer"&gt;#1057&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  edgeops 1.1.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-25 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/edgeops" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Adds host key fingerprint verification for Bolt 5.1.0+ targets, along with a batch of NETCONF/SSH hardening fixes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(PE-43703) Verify host key fingerprints supplied as &lt;code&gt;host-key-fingerprint&lt;/code&gt; in the target hash; takes precedence over &lt;code&gt;host-key-check&lt;/code&gt; and requires Bolt 5.1.0+. &lt;a href="https://github.com/puppetlabs/puppetlabs-edgeops/pull/38" rel="noopener noreferrer"&gt;#38&lt;/a&gt; (&lt;a href="https://github.com/owenbeckles" rel="noopener noreferrer"&gt;owenbeckles&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PE-42584) Correctly handle host key verification parameter &lt;a href="https://github.com/puppetlabs/puppetlabs-edgeops/pull/23" rel="noopener noreferrer"&gt;#23&lt;/a&gt; (&lt;a href="https://github.com/Ziaunys" rel="noopener noreferrer"&gt;Ziaunys&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PE-43619) Clean up SSH on timeout and always raise instead of returning partial data &lt;a href="https://github.com/puppetlabs/puppetlabs-edgeops/pull/33" rel="noopener noreferrer"&gt;#33&lt;/a&gt; (&lt;a href="https://github.com/Ziaunys" rel="noopener noreferrer"&gt;Ziaunys&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PE-43614) Add mutex synchronization for RPC message ID allocation &lt;a href="https://github.com/puppetlabs/puppetlabs-edgeops/pull/29" rel="noopener noreferrer"&gt;#29&lt;/a&gt; (&lt;a href="https://github.com/Ziaunys" rel="noopener noreferrer"&gt;Ziaunys&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PE-43652) Rename netconf_lock task to netconf_check_lock &lt;a href="https://github.com/puppetlabs/puppetlabs-edgeops/pull/35" rel="noopener noreferrer"&gt;#35&lt;/a&gt; (&lt;a href="https://github.com/Ziaunys" rel="noopener noreferrer"&gt;Ziaunys&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  firewall 8.5.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-25 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/firewall" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Two releases this month: 8.4.0 added CONNMARK-based policy routing support and several bugfixes, while 8.5.0 allows stdlib 10.x, drops &lt;code&gt;iptables-services&lt;/code&gt; from EL9+ package defaults, and fixes several ipset and chain-detection edge cases.&lt;/p&gt;

&lt;p&gt;Includes monthly releases: 8.5.0 (2026-06-25), 8.4.0 (2026-06-10).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-firewall/pull/1288" rel="noopener noreferrer"&gt;#1288&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(#1254) Remove iptables-services from EL9+ package defaults &lt;a href="https://github.com/puppetlabs/puppetlabs-firewall/pull/1296" rel="noopener noreferrer"&gt;#1296&lt;/a&gt; (&lt;a href="https://github.com/david22swan" rel="noopener noreferrer"&gt;david22swan&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(feat) Add restore_mark, nfmask, ctmask support for CONNMARK-based policy routing &lt;a href="https://github.com/puppetlabs/puppetlabs-firewall/pull/1291" rel="noopener noreferrer"&gt;#1291&lt;/a&gt; (&lt;a href="https://github.com/david22swan" rel="noopener noreferrer"&gt;david22swan&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(bugfix) Fix ipset idempotency: single-element array not in sync with String equivalent &lt;a href="https://github.com/puppetlabs/puppetlabs-firewall/pull/1286" rel="noopener noreferrer"&gt;#1286&lt;/a&gt; (&lt;a href="https://github.com/david22swan" rel="noopener noreferrer"&gt;david22swan&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(bugfix) Fix log_level idempotency when explicitly setting the iptables default value &lt;a href="https://github.com/puppetlabs/puppetlabs-firewall/pull/1284" rel="noopener noreferrer"&gt;#1284&lt;/a&gt; (&lt;a href="https://github.com/david22swan" rel="noopener noreferrer"&gt;david22swan&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  haproxy 8.2.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-29 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/haproxy" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Completes the module's Puppet 8 upgrade work, drops Puppet 7 support, and allows stdlib 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-haproxy/pull/642" rel="noopener noreferrer"&gt;#642&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(CAT-2373) Remove puppet 7 &lt;a href="https://github.com/puppetlabs/puppetlabs-haproxy/pull/631" rel="noopener noreferrer"&gt;#631&lt;/a&gt; (&lt;a href="https://github.com/gavindidrichsen" rel="noopener noreferrer"&gt;gavindidrichsen&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(CAT-2373)(02) Upgrade module to puppet 8 &lt;a href="https://github.com/puppetlabs/puppetlabs-haproxy/pull/629" rel="noopener noreferrer"&gt;#629&lt;/a&gt; (&lt;a href="https://github.com/gavindidrichsen" rel="noopener noreferrer"&gt;gavindidrichsen&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  inifile 6.4.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-25 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/inifile" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;A small release to allow stdlib 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-inifile/pull/570" rel="noopener noreferrer"&gt;#570&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  java 12.0.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-29 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/java" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Adds CentOS 9 and Debian 13 support, allows stdlib 10.x, and adds support for downloading from a login/password-protected URL.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(CAT-2376) Puppet Core update &lt;a href="https://github.com/puppetlabs/puppetlabs-java/pull/614" rel="noopener noreferrer"&gt;#614&lt;/a&gt; (&lt;a href="https://github.com/LukasAud" rel="noopener noreferrer"&gt;LukasAud&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(CAT-2152) Add support for CentOS 9 &lt;a href="https://github.com/puppetlabs/puppetlabs-java/pull/606" rel="noopener noreferrer"&gt;#606&lt;/a&gt; (&lt;a href="https://github.com/skyamgarp" rel="noopener noreferrer"&gt;skyamgarp&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Add support for Debian 13 (trixie) &lt;a href="https://github.com/puppetlabs/puppetlabs-java/pull/613" rel="noopener noreferrer"&gt;#613&lt;/a&gt; (&lt;a href="https://github.com/mika" rel="noopener noreferrer"&gt;mika&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Feat: Allow downloading from a login/password protected URL &lt;a href="https://github.com/puppetlabs/puppetlabs-java/pull/588" rel="noopener noreferrer"&gt;#588&lt;/a&gt; (&lt;a href="https://github.com/JGodin-C2C" rel="noopener noreferrer"&gt;JGodin-C2C&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-java/pull/626" rel="noopener noreferrer"&gt;#626&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  lvm 4.0.2
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-28 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/lvm" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This lvm release bumps the stdlib dependency to allow 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-lvm/pull/384" rel="noopener noreferrer"&gt;#384&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  motd 8.0.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-29 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/motd" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Adds Bolt 5.0 support, and allows stdlib 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(CAT-2352) Puppet Core update &lt;a href="https://github.com/puppetlabs/puppetlabs-motd/pull/531" rel="noopener noreferrer"&gt;#531&lt;/a&gt; (&lt;a href="https://github.com/LukasAud" rel="noopener noreferrer"&gt;LukasAud&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(CAT-2463) Add bolt 5.0 support &lt;a href="https://github.com/puppetlabs/puppetlabs-motd/pull/535" rel="noopener noreferrer"&gt;#535&lt;/a&gt; (&lt;a href="https://github.com/gavindidrichsen" rel="noopener noreferrer"&gt;gavindidrichsen&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-motd/pull/558" rel="noopener noreferrer"&gt;#558&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  mysql 17.0.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-29 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/mysql" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Fixes the RHEL/CentOS Stream 10 version check, and allows stdlib 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(CAT-2381) Puppet Core update &lt;a href="https://github.com/puppetlabs/puppetlabs-mysql/pull/1688" rel="noopener noreferrer"&gt;#1688&lt;/a&gt; (&lt;a href="https://github.com/LukasAud" rel="noopener noreferrer"&gt;LukasAud&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fix version check for RHEL/CentOS Stream 10 &lt;a href="https://github.com/puppetlabs/puppetlabs-mysql/pull/1686" rel="noopener noreferrer"&gt;#1686&lt;/a&gt; (&lt;a href="https://github.com/kajinamit" rel="noopener noreferrer"&gt;kajinamit&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-mysql/pull/1707" rel="noopener noreferrer"&gt;#1707&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ntp 11.1.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-29 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/ntp" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;A small release that only bumps the stdlib dependency to allow 10.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-ntp/pull/742" rel="noopener noreferrer"&gt;#742&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  peadm 3.38.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-30 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/peadm" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Adds support for installing and upgrading to Puppet Enterprise 2023.8.10 and 2025.11.0, along with a cloud_database_host parameter for cloud-DB-backed installs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding support for PE 2023.8.10 and 2025.11.0 &lt;a href="https://github.com/puppetlabs/puppetlabs-peadm/pull/673" rel="noopener noreferrer"&gt;#673&lt;/a&gt; (&lt;a href="https://github.com/CharithaDunuwille" rel="noopener noreferrer"&gt;CharithaDunuwille&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PE-44022) Add cloud_database_host parameter for cloud-DB-backed installs &lt;a href="https://github.com/puppetlabs/puppetlabs-peadm/pull/665" rel="noopener noreferrer"&gt;#665&lt;/a&gt; (&lt;a href="https://github.com/mcdonaldseanp" rel="noopener noreferrer"&gt;mcdonaldseanp&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PE-44247) Add peadm-path PG-major HA upgrade coverage for replica pe-puppetdb &lt;a href="https://github.com/puppetlabs/puppetlabs-peadm/pull/666" rel="noopener noreferrer"&gt;#666&lt;/a&gt; (&lt;a href="https://github.com/steveax" rel="noopener noreferrer"&gt;steveax&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PE-44595) Don't emit empty dns_alt_names flag in subplans::install &lt;a href="https://github.com/puppetlabs/puppetlabs-peadm/pull/672" rel="noopener noreferrer"&gt;#672&lt;/a&gt; (&lt;a href="https://github.com/steveax" rel="noopener noreferrer"&gt;steveax&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  postgresql 10.6.2
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-29 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/postgresql" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Adds basic EL10 support, allows both stdlib and concat to move to their 10.x releases, and fixes a bug where &lt;code&gt;postgresql_conf&lt;/code&gt; resources set to absent were handled incorrectly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This release also incorrectly removed Puppet 7 support as a breaking change in a patch release. This has since been rolled back in 10.6.3. Removing Puppet 7 support will happen in a future major release.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add EL10 basic support - align EL10 PGSQL 16 default package version &lt;a href="https://github.com/puppetlabs/puppetlabs-postgresql/pull/1650" rel="noopener noreferrer"&gt;#1650&lt;/a&gt; (&lt;a href="https://github.com/ikonia" rel="noopener noreferrer"&gt;ikonia&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;fix: ignore postgresql_conf resource value when set to absent &lt;a href="https://github.com/puppetlabs/puppetlabs-postgresql/pull/1657" rel="noopener noreferrer"&gt;#1657&lt;/a&gt; (&lt;a href="https://github.com/davidassigbi" rel="noopener noreferrer"&gt;davidassigbi&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;grant creation via hiera through server::grant.pp &lt;a href="https://github.com/puppetlabs/puppetlabs-postgresql/pull/1668" rel="noopener noreferrer"&gt;#1668&lt;/a&gt; (&lt;a href="https://github.com/ikonia" rel="noopener noreferrer"&gt;ikonia&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-postgresql/pull/1681" rel="noopener noreferrer"&gt;#1681&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;puppetlabs/concat: Allow 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-postgresql/pull/1669" rel="noopener noreferrer"&gt;#1669&lt;/a&gt; (&lt;a href="https://github.com/bastelfreak" rel="noopener noreferrer"&gt;bastelfreak&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  sce_linux 2.7.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-16 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/sce_linux" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Adds CIS Benchmark support for the RHEL 10 family, fixes an rsyslog configuration file that was unconditionally overwritten, and drops RHEL 7 now that it's end of life.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Support for RHEL 10, AlmaLinux 10, Oracle Linux 10, and Rocky Linux 10.&lt;/strong&gt; Enforces the CIS Benchmark for RHEL 10 (v1.0.1, Server Levels 1 and 2) and CIS Benchmark v1.0.0 for AlmaLinux 10, Oracle Linux 10, and Rocky Linux 10.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rsyslog.conf file unconditionally overwritten.&lt;/strong&gt; Previously overwritten on every Puppet run even when logging configuration was set to ignore, affecting RHEL 7/8/9 and derivatives. No action required from users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Intrusion Detection Environment (AIDE) utility class.&lt;/strong&gt; Fixed incorrect configuration options generated for AIDE 0.19.x on RHEL 9 that caused &lt;code&gt;aide --init&lt;/code&gt; to fail.&lt;/li&gt;
&lt;li&gt;RHEL &lt;strong&gt;7.&lt;/strong&gt; RHEL 7 is end of life and no longer supported.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the official &lt;a href="https://help.puppet.com/sce/current/linux/scel_relnotes_270.htm" rel="noopener noreferrer"&gt;release notes for sce_linux 2.7.0&lt;/a&gt; for the full details.&lt;/p&gt;




&lt;h3&gt;
  
  
  security_policy 1.0.0
&lt;/h3&gt;

&lt;p&gt;🌟 &lt;strong&gt;&lt;em&gt;New Module:&lt;/em&gt;&lt;/strong&gt; 2026-06-25 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/security_policy" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Brand-new module for managing Windows local security policy. Provides the &lt;code&gt;security_option&lt;/code&gt; and &lt;code&gt;user_right_assignment&lt;/code&gt; resource types (Puppet Resource API), covering all 45 Windows Privilege Rights and System Access settings. Initial release contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;security_option&lt;/code&gt; and &lt;code&gt;user_right_assignment&lt;/code&gt; resource types for managing Windows local security policy settings via &lt;code&gt;secedit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;security_policy&lt;/code&gt; class exposing 45 &lt;code&gt;Optional[Array[String]]&lt;/code&gt; parameters (one per privilege right), matching the layout of the legacy &lt;code&gt;dsc/securitypolicydsc&lt;/code&gt; module.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PuppetX::Sid&lt;/code&gt; module: a static well-known SID map covering the 19 standard SIDs, with a PowerShell fallback via &lt;code&gt;Pwsh::Manager&lt;/code&gt; for domain and custom accounts.&lt;/li&gt;
&lt;li&gt;YAML-driven setting metadata loader (&lt;code&gt;PuppetX::SecurityPolicy.all_settings&lt;/code&gt;) instead of a hardcoded settings hash.&lt;/li&gt;
&lt;li&gt;Puppet requirement pinned to &amp;gt;= 8.0.0 &amp;lt; 9.0.0.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  stdlib 10.0.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-30 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/stdlib" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Drops Puppet 7 support, adds CentOS 9 support, extends Sensitive value support to more functions, and fixes &lt;code&gt;has_ip_address&lt;/code&gt;/&lt;code&gt;has_ip_network&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(CAT-2395) Puppet Core upgrade - drop support for Puppet 7 &lt;a href="https://github.com/puppetlabs/puppetlabs-stdlib/pull/1457" rel="noopener noreferrer"&gt;#1457&lt;/a&gt; (&lt;a href="https://github.com/LukasAud" rel="noopener noreferrer"&gt;LukasAud&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(CAT-2152) Add support for CentOS 9 &lt;a href="https://github.com/puppetlabs/puppetlabs-stdlib/pull/1442" rel="noopener noreferrer"&gt;#1442&lt;/a&gt; (&lt;a href="https://github.com/skyamgarp" rel="noopener noreferrer"&gt;skyamgarp&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Support &lt;code&gt;Sensitive&lt;/code&gt; values in more functions &lt;a href="https://github.com/puppetlabs/puppetlabs-stdlib/pull/1463" rel="noopener noreferrer"&gt;#1463&lt;/a&gt; (&lt;a href="https://github.com/alexjfisher" rel="noopener noreferrer"&gt;alexjfisher&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Support sensitive values in &lt;code&gt;to_json_pretty&lt;/code&gt; &lt;a href="https://github.com/puppetlabs/puppetlabs-stdlib/pull/1418" rel="noopener noreferrer"&gt;#1418&lt;/a&gt; (&lt;a href="https://github.com/alexjfisher" rel="noopener noreferrer"&gt;alexjfisher&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fix &lt;code&gt;has_ip_address&lt;/code&gt; and &lt;code&gt;has_ip_network&lt;/code&gt; functions &lt;a href="https://github.com/puppetlabs/puppetlabs-stdlib/pull/1448" rel="noopener noreferrer"&gt;#1448&lt;/a&gt; (&lt;a href="https://github.com/alexjfisher" rel="noopener noreferrer"&gt;alexjfisher&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  wsus_client 6.3.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-06-25 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/wsus_client" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;stdlib 10.x now allowed as part of the dependency range bump.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(MODULES-11840) Allow puppetlabs/stdlib 10.x &lt;a href="https://github.com/puppetlabs/puppetlabs-wsus_client/pull/238" rel="noopener noreferrer"&gt;#238&lt;/a&gt; (&lt;a href="https://github.com/imaqsood" rel="noopener noreferrer"&gt;imaqsood&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Until Next Time!
&lt;/h2&gt;

&lt;p&gt;That wraps up the June 2026 roundup. If any of these modules intersect with your environment — especially the Security Compliance Management CVE fixes — the linked Forge pages and release notes are worth a closer look.&lt;/p&gt;

&lt;p&gt;Feedback on the series is always useful, especially if there are module families or release-note patterns that deserve more attention in future editions.&lt;/p&gt;

&lt;p&gt;More updates coming next month when the July 2026 releases land.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 AI Disclosure
&lt;/h2&gt;

&lt;p&gt;This roundup is produced by a mostly-automated pipeline, with some AI sprinkled in for orchestration and enrichment (or 'Combobulating' and 'Finagling'), followed by a human review (that would be me) before publishing.&lt;/p&gt;

&lt;p&gt;The automation is an &lt;a href="https://github.com/jst-cyr/puppetlabs-modules-roundup-writer" rel="noopener noreferrer"&gt;open-source project&lt;/a&gt; with deterministic python scripts to crawl the Forge and determine which &lt;code&gt;puppetlabs&lt;/code&gt; modules were released during a specific month (and catching when a module gets more than one release in a month). By combining a template, automation scripts, and some AI orchestration the content all gets pulled together for a structured markdown document. I then jump in to double-check the content and update any wording that seems repetitive or irrelevant (and sometimes I need to add some extra context that isn't in the changelog notes).&lt;/p&gt;

</description>
      <category>puppet</category>
    </item>
    <item>
      <title>Puppet Core 8.20 adds Ubuntu 26.04 and Security Fixes</title>
      <dc:creator>Jason St-Cyr</dc:creator>
      <pubDate>Mon, 06 Jul 2026 12:15:19 +0000</pubDate>
      <link>https://dev.to/puppet/puppet-core-820-adds-ubuntu-2604-and-security-fixes-1d84</link>
      <guid>https://dev.to/puppet/puppet-core-820-adds-ubuntu-2604-and-security-fixes-1d84</guid>
      <description>&lt;p&gt;The latest release of Puppet Core was targeted at addressing reported CVEs and adding agent support for Ubuntu 26.04, along with some other items that might be of interest to you! Here's a quick rundown on the highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 26.04 (x86_64 and ARM) support added for Puppet agents&lt;/li&gt;
&lt;li&gt;Updated components for OpenSSL, net-imap, and concurrent-Ruby to address 13 CVEs&lt;/li&gt;
&lt;li&gt;Sensitive values are now handled correctly in &lt;code&gt;transactionstore.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;On macOS, &lt;code&gt;plist&lt;/code&gt; files are now written atomically to prevent partial or zero-length reads.&lt;/li&gt;
&lt;li&gt;PXP agent metadata now uses uniquely named temporary files to resolve file-rename race conditions that were happening for Windows tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was just my quick summary of the notes, so make sure to read the &lt;a href="https://help.puppet.com/core/current/Content/PuppetCore/PuppetReleaseNotes/release_notes_puppet_x-8-20-0.htm" rel="noopener noreferrer"&gt;full release notes for Puppet Core 8.20&lt;/a&gt; to get all the details before you upgrade!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>puppet</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Puppet Enterprise Introduces Database-Backed CA Storage in 2025.11 release</title>
      <dc:creator>Jason St-Cyr</dc:creator>
      <pubDate>Thu, 02 Jul 2026 15:32:28 +0000</pubDate>
      <link>https://dev.to/puppet/puppet-enterprise-introduces-database-backed-ca-storage-in-202511-release-epl</link>
      <guid>https://dev.to/puppet/puppet-enterprise-introduces-database-backed-ca-storage-in-202511-release-epl</guid>
      <description>&lt;p&gt;The latest Puppet Enterprise releases are out and this one has a huge load of improvements, fixes, and security patches included!&lt;/p&gt;

&lt;h2&gt;
  
  
  Puppet Enterprise (PE) 2025.11 released!
&lt;/h2&gt;

&lt;p&gt;The full &lt;a href="https://help.puppet.com/pe/current/topics/release-notes-pe-x-11.htm" rel="noopener noreferrer"&gt;PE 2025.11 release notes&lt;/a&gt; are always the best way to get a full detail on what has changed, but here are some highlights of PE 2025.11!&lt;/p&gt;

&lt;h3&gt;
  
  
  Certificate Authority (CA): Database-backed Storage
&lt;/h3&gt;

&lt;p&gt;This new optional feature adds support for storing CA data in a PostgreSQL database instead of the file system. This improves performance and reliability and introduces API-driven capabilities and enhanced backup and recovery handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  PostgreSQL 17 Supported
&lt;/h3&gt;

&lt;p&gt;PE-managed installations will automatically upgrade from verson 14 to 17 as part of the upgrade process, or you can update yourself before upgrading to PE 2025.11&lt;/p&gt;

&lt;h3&gt;
  
  
  Infra Assistant Goes GPT-5
&lt;/h3&gt;

&lt;p&gt;GPT-5 series models are now running under the hood of Infra Assistant, improving the quality of responses and the consistency for queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced Patching Enhancements
&lt;/h3&gt;

&lt;p&gt;The advanced patching feature now has improvements across a variety of areas&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New &lt;code&gt;puppet_run_concurrency&lt;/code&gt; setting allows you to get better performance out of patch group enrollment&lt;/li&gt;
&lt;li&gt;Improved validation of scheduled and immediate jobs to reduce risk of unintended or skipped executions.&lt;/li&gt;
&lt;li&gt;Cron scheduling has better user experience and improved validation across features.&lt;/li&gt;
&lt;li&gt;New configurable option to enable Puppet to run after patch jobs to refresh &lt;code&gt;pe_patch&lt;/code&gt; facts &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  New Endpoints for Classifier and Activity Service APIs
&lt;/h3&gt;

&lt;p&gt;The Classifier API introduced new &lt;code&gt;tags&lt;/code&gt;, &lt;code&gt;add-tags&lt;/code&gt; and &lt;code&gt;remove-tags&lt;/code&gt; endpoints to manage node group tags. The Activity service API now has &lt;code&gt;subscriptions&lt;/code&gt; endpoints to create subscriptions, list subscriptions, or fetch/delete a specific subscription.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent Platform Updates, Resolved Issues, and Security Fixes
&lt;/h3&gt;

&lt;p&gt;The macOS 26 platform is now supported for both ARM and x86_64, while support has been removed for Ubuntu 18.04 and Ubuntu 20.04.&lt;/p&gt;

&lt;p&gt;Nearly 60 CVEs were addressed in this release, along with many resolved issues. You should definitely check out the &lt;a href="https://help.puppet.com/pe/current/topics/release-notes-pe-x-11.htm" rel="noopener noreferrer"&gt;release notes&lt;/a&gt; for the full list of fixes! &lt;/p&gt;

&lt;h2&gt;
  
  
  PE 2023.8.10 Released
&lt;/h2&gt;

&lt;p&gt;Along with the new 2025.11 release, the latest patches for the LTS version PE 2023.8 has also been put out. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent platforms&lt;/strong&gt; were a big change, as macOS 26 was added, along with removing support for Ubuntu 18 and Ubuntu 20.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nearly 60 CVEs resolved&lt;/strong&gt; in this release, many by component updates.&lt;/li&gt;
&lt;li&gt;Some additional fixes made in 2025.11 have also been inherited for the Activity service API and LDAP group attributes handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Official Release Notes
&lt;/h2&gt;

&lt;p&gt;Read more in the full release notes on help.puppet.com ⬇️&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://help.puppet.com/pe/current/topics/release-notes-pe-x-11.htm" rel="noopener noreferrer"&gt;PE 2025.11 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://help.puppet.com/pe/2023.8/topics/release-notes-pe-x-y-10.htm" rel="noopener noreferrer"&gt;PE 2023.8.10 Release Notes&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.puppet.com/downloads/puppet-enterprise" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Download Puppet Enterprise&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>devops</category>
    </item>
    <item>
      <title>SaaS Globalization Strategy: Technical Preparation for Chinese, European, and US Markets</title>
      <dc:creator>sweet</dc:creator>
      <pubDate>Wed, 24 Jun 2026 00:45:00 +0000</pubDate>
      <link>https://dev.to/feidou/saas-globalization-strategy-technical-preparation-for-chinese-european-and-us-markets-4g8k</link>
      <guid>https://dev.to/feidou/saas-globalization-strategy-technical-preparation-for-chinese-european-and-us-markets-4g8k</guid>
      <description>&lt;p&gt;Expanding a SaaS product into global markets requires more than translation — it demands infrastructure changes (edge deployment for low latency), compliance adaptation (GDPR, PIPL, CCPA), payment localization (Alipay, SEPA, Stripe), and cultural UX adjustments. This guide breaks down the technical and business preparation needed for the three largest SaaS markets: the US, Europe, and China. Each section includes actionable code examples, configuration snippets, and deployment patterns you can implement immediately. See the architecture in action at &lt;a href="https://tanstackship.com" rel="noopener noreferrer"&gt;tanstackship.com&lt;/a&gt;, a production SaaS shipping globally on Cloudflare Workers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Globalization Must Be Built, Not Bolted On
&lt;/h2&gt;

&lt;p&gt;The biggest mistake SaaS founders make with globalization is treating it as a phase-two feature. Retrofitting i18n, multi-currency pricing, and regional compliance onto a monolithic codebase typically costs &lt;strong&gt;3-5x more&lt;/strong&gt; than building it in from the start — and often requires a full rewrite of the database schema, routing layer, and deployment pipeline.&lt;/p&gt;

&lt;p&gt;The three largest SaaS markets have fundamentally different requirements:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;US Market&lt;/th&gt;
&lt;th&gt;European Market&lt;/th&gt;
&lt;th&gt;Chinese Market&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;English&lt;/td&gt;
&lt;td&gt;Multi (DE, FR, ES, etc.)&lt;/td&gt;
&lt;td&gt;Chinese (Simplified)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data regulation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CCPA/State laws&lt;/td&gt;
&lt;td&gt;GDPR (strictest)&lt;/td&gt;
&lt;td&gt;PIPL + CSL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Payment methods&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Credit cards, Stripe&lt;/td&gt;
&lt;td&gt;SEPA, PayPal, credit cards&lt;/td&gt;
&lt;td&gt;Alipay, WeChat Pay, UnionPay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS/US-East&lt;/td&gt;
&lt;td&gt;GDPR-compliant hosting&lt;/td&gt;
&lt;td&gt;ICP license, China-hosted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Google, GitHub&lt;/td&gt;
&lt;td&gt;EU-specific (eIDAS)&lt;/td&gt;
&lt;td&gt;WeChat, Alipay, phone SMS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pricing display&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$ (tax excl.)&lt;/td&gt;
&lt;td&gt;€ (VAT incl.)&lt;/td&gt;
&lt;td&gt;¥ (tax incl.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Content delivery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Global CDN&lt;/td&gt;
&lt;td&gt;Edge (300ms latency target)&lt;/td&gt;
&lt;td&gt;China CDN (ICP + DDoS shield)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Social login&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Google, Apple, GitHub&lt;/td&gt;
&lt;td&gt;Google, Apple, LinkedIn&lt;/td&gt;
&lt;td&gt;WeChat, Alipay, phone OTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Email deliverability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SendGrid, AWS SES&lt;/td&gt;
&lt;td&gt;SendGrid (DPA signed)&lt;/td&gt;
&lt;td&gt;QQ Mail, Alibaba Direct Mail&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Market 1: European Market Preparation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  GDPR Compliance Checklist
&lt;/h3&gt;

&lt;p&gt;GDPR applies to any SaaS processing data of EU residents — regardless of where your company is incorporated. Here is the technical implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/gdpr.ts&lt;/span&gt;
&lt;span class="c1"&gt;// GDPR-compliant data handling utilities&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Explicit consent tracking&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ConsentRecord&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;consentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;analytics&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;marketing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;functional&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;third_party&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;granted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;ipAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="c1"&gt;// Anonymized&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;recordConsent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;consentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConsentRecord&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;consentType&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;granted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`INSERT INTO consent_records (user_id, consent_type, granted, timestamp, ip_address)
     VALUES (?, ?, ?, unixepoch(), ?)`&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;consentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;granted&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;anonymizeIp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getClientIp&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Data portability export&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exportUserData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServerFn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM users WHERE id = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM subscriptions WHERE user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM invoices WHERE user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM analytics_events WHERE user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;exportedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;events&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;span class="c1"&gt;// 3. Right to erasure (GDPR Article 17)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteUserData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServerFn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// Anonymize personal data (keep transactional records for legal retention)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`UPDATE users SET
        email = 'deleted-' || id || '@redacted.com',
        name = 'Deleted User',
        avatar_url = NULL,
        deleted_at = unixepoch()
        WHERE id = ?`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DELETE FROM analytics_events WHERE user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DELETE FROM session_tokens WHERE user_id = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;h3&gt;
  
  
  EU Data Residency
&lt;/h3&gt;

&lt;p&gt;GDPR requires that EU user data stays within the EU or in countries with adequate protection. With Cloudflare Workers, use data localization:&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;# wrangler.jsonc&lt;/span&gt;
&lt;span class="err"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;"name":&lt;/span&gt; &lt;span class="err"&gt;"tanstack-ship",&lt;/span&gt;
  &lt;span class="err"&gt;"compatibility_date":&lt;/span&gt; &lt;span class="err"&gt;"2026-06-01",&lt;/span&gt;
  &lt;span class="err"&gt;"workers_dev":&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
  &lt;span class="err"&gt;"routes":&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;"pattern":&lt;/span&gt; &lt;span class="err"&gt;"tanstackship.com",&lt;/span&gt; &lt;span class="err"&gt;"zone_id":&lt;/span&gt; &lt;span class="err"&gt;"YOUR_ZONE_ID"&lt;/span&gt; &lt;span class="err"&gt;},&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;"pattern":&lt;/span&gt; &lt;span class="err"&gt;"eu.tanstackship.com",&lt;/span&gt; &lt;span class="err"&gt;"zone_id":&lt;/span&gt; &lt;span class="err"&gt;"YOUR_ZONE_ID",&lt;/span&gt; &lt;span class="err"&gt;"custom_domain":&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;],&lt;/span&gt;
  &lt;span class="err"&gt;"d1_databases":&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;"binding":&lt;/span&gt; &lt;span class="err"&gt;"DB",&lt;/span&gt; &lt;span class="err"&gt;"database_id":&lt;/span&gt; &lt;span class="err"&gt;"GLOBAL_DB",&lt;/span&gt; &lt;span class="err"&gt;"database_name":&lt;/span&gt; &lt;span class="err"&gt;"tanstack-ship"&lt;/span&gt; &lt;span class="err"&gt;},&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;"binding":&lt;/span&gt; &lt;span class="err"&gt;"EU_DB",&lt;/span&gt; &lt;span class="err"&gt;"database_id":&lt;/span&gt; &lt;span class="err"&gt;"EU_DB",&lt;/span&gt; &lt;span class="err"&gt;"database_name":&lt;/span&gt; &lt;span class="err"&gt;"tanstack-ship-eu"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Route EU users to EU D1 instance&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getDb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;EU_COUNTRIES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EU_DB&lt;/span&gt; &lt;span class="c1"&gt;// GDPR-compliant D1 instance&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  EU Payment: SEPA Direct Debit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// With Stripe, SEPA is handled via Payment Methods&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymentIntent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentIntents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// €29.99&lt;/span&gt;
  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;payment_method_types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sepa_debit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerId&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;strong&gt;Key EU-specific pricing considerations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Display prices &lt;strong&gt;including VAT&lt;/strong&gt; (unlike the US)&lt;/li&gt;
&lt;li&gt;Register for VAT MOSS to handle cross-border EU sales&lt;/li&gt;
&lt;li&gt;Issue VAT-compliant invoices (required by law for B2B)&lt;/li&gt;
&lt;li&gt;SEPA transfers can take 1-3 business days — do not grant access until payment clears&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Market 2: Chinese Market Preparation
&lt;/h2&gt;

&lt;p&gt;Entering China is the most technically demanding market expansion. Here is what you need:&lt;/p&gt;

&lt;h3&gt;
  
  
  ICP License (Required for Any Hosted Service)
&lt;/h3&gt;

&lt;p&gt;Any website or app hosted on servers in mainland China or using a Chinese CDN must have an ICP (Internet Content Provider) license. The process takes &lt;strong&gt;4-8 weeks&lt;/strong&gt; and requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Registered Chinese company (WFOE or joint venture) or partnership with a Chinese ICP-licensed entity&lt;/li&gt;
&lt;li&gt;ICP filing submission through your cloud provider (Alibaba Cloud, Tencent Cloud, AWS China)&lt;/li&gt;
&lt;li&gt;Domain name already ICP-filed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without an ICP license, your site will be blocked by the Great Firewall of China (GFW).&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure: Bypassing the Great Firewall
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/geo-routing.ts&lt;/span&gt;
&lt;span class="c1"&gt;// Route Chinese users to China-hosted instances&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CHINA_REGIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MO&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getRegionalEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CHINA_REGIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Route to China-hosted instance via Cloudflare China Network&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://tanstack-ship.cn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Global instance&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://tanstackship.com&lt;/span&gt;&lt;span class="dl"&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;strong&gt;Cloudflare China Network&lt;/strong&gt; provides a GFW-compliant CDN layer without requiring your own Chinese servers. All major Chinese cloud providers also offer GPU/CPU instances:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alibaba Cloud (aliyun.com) — largest market share&lt;/li&gt;
&lt;li&gt;Tencent Cloud — second largest, WeChat integration&lt;/li&gt;
&lt;li&gt;AWS China (Beijing/Ningxia) — requires separate account&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Chinese Payment Integration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/payment/china.ts&lt;/span&gt;
&lt;span class="c1"&gt;// Alipay integration via Stripe or direct API&lt;/span&gt;

&lt;span class="c1"&gt;// Option 1: Stripe (simplest, higher fees)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymentIntent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentIntents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;19900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ¥199.00&lt;/span&gt;
  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cny&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;payment_method_types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alipay&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;payment_method_options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;alipay&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;span class="c1"&gt;// Option 2: Direct Alipay API (lower fees, more setup)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AlipaySdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alipay-sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alipaySdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AlipaySdk&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALIPAY_APP_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALIPAY_PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;alipaySdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alipay.trade.create&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;bizContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;out_trade_no&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;total_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;199.00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TanStack Ship - Pro Plan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;product_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FAST_INSTANT_TRADE_PAY&lt;/span&gt;&lt;span class="dl"&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;h3&gt;
  
  
  Chinese Social Login
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/auth/china.ts&lt;/span&gt;
&lt;span class="c1"&gt;// WeChat OAuth&lt;/span&gt;

&lt;span class="c1"&gt;// Step 1: Redirect user to WeChat authorization&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getWeChatAuthUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redirectUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;appid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WECHAT_APP_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;redirectUri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;response_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;snsapi_login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`https://open.weixin.qq.com/connect/qrconnect?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&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="c1"&gt;// Step 2: Exchange code for access token&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getWeChatToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`https://api.weixin.qq.com/sns/oauth2/access_token?`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;appid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WECHAT_APP_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WECHAT_APP_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;grant_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization_code&lt;/span&gt;&lt;span class="dl"&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;openid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;unionid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Chinese Content Delivery Considerations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Censored keywords&lt;/strong&gt;: Your content will be scanned. Common SaaS-blocked terms include sensitive political topics, VPN references, and cryptocurrency mentions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WeChat ecosystem&lt;/strong&gt;: WeChat Mini Programs, WeChat Pay, and WeChat Official Accounts are essential for B2C SaaS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email deliverability&lt;/strong&gt;: QQ Mail and 163.com are the dominant email providers — ensure your SPF, DKIM, and DMARC are configured for Chinese domains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain blocking&lt;/strong&gt;: Some domains are blocked without clear reason. Register a &lt;code&gt;.cn&lt;/code&gt; domain as backup&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Market 3: US Market Preparation
&lt;/h2&gt;

&lt;p&gt;The US market is the most straightforward but still requires specific attention:&lt;/p&gt;

&lt;h3&gt;
  
  
  Tax Compliance: Sales Tax by State
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/tax/us-sales-tax.ts&lt;/span&gt;
&lt;span class="c1"&gt;// Simplified sales tax calculation (use a service like TaxJar for production)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STATE_TAX_RATES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0725&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// California base rate&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.04&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// New York state (county/city adds more)&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0625&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Texas&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.065&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// Washington&lt;/span&gt;
  &lt;span class="c1"&gt;// ... 45+ more states&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateSalesTax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;taxRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;taxAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taxRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;STATE_TAX_RATES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taxAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;taxRate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;taxRate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;taxAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;taxAmount&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;strong&gt;Key considerations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Economic nexus: If you sell &amp;gt;$100K or 200+ transactions in a state, you must collect sales tax&lt;/li&gt;
&lt;li&gt;Use a service like TaxJar, Stripe Tax, or Anrok for automated calculation&lt;/li&gt;
&lt;li&gt;Display prices &lt;strong&gt;excluding tax&lt;/strong&gt; (unlike Europe)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  US Privacy Compliance (CCPA)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/ccpa.ts&lt;/span&gt;
&lt;span class="c1"&gt;// CCPA opt-out mechanism&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleCcpOptOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`INSERT INTO privacy_opt_outs (user_id, regulation, opted_out_at)
     VALUES (?, 'CCPA', unixepoch())`&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Do not sell my personal information endpoint&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;optOutOfSale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServerFn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handleCcpOptOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;
  
  
  Architectural Pattern: Regional Deployment with Edge Workers
&lt;/h2&gt;

&lt;p&gt;For SaaS products targeting all three markets simultaneously, use a regional deployment architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────┐
│                  Cloudflare                      │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────┐ │
│  │  US Worker   │  │  EU Worker   │  │ CN Edge  │ │
│  │  us-east     │  │  eu-west     │  │ (CF CN)  │ │
│  └──────┬───────┘  └──────┬───────┘  └────┬─────┘ │
│         │                 │               │        │
│  ┌──────▼───────┐  ┌──────▼───────┐  ┌────▼─────┐ │
│  │  US D1 DB    │  │  EU D1 DB    │  │ CN D1 DB │ │
│  │  (Virginia)  │  │ (Frankfurt)  │  │ (Beijing) │ │
│  └──────────────┘  └──────────────┘  └──────────┘ │
│         │                 │               │        │
│  ┌──────▼───────┐  ┌──────▼───────┐              │
│  │  US R2       │  │  EU R2       │              │
│  └──────────────┘  └──────────────┘              │
└─────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/middleware/geo-router.ts&lt;/span&gt;
&lt;span class="c1"&gt;// Edge middleware that routes to the correct regional deployment&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;geoRouter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServerFn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MO&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CN_DB&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EU_COUNTRIES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EU_DB&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;US_DB&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;
  
  
  Multi-Currency Pricing Database Schema
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;price_tiers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'US'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'EU'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'CN'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ROW'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'GLOBAL'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="n"&gt;currency&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;unit_amount&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;-- In smallest currency unit (cents, fen, etc.)&lt;/span&gt;
  &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'month'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'year'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="n"&gt;tax_behavior&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'exclusive'&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tax_behavior&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'inclusive'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'exclusive'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="n"&gt;is_active&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unixepoch&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unixepoch&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_price_tiers_region&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;price_tiers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/pricing/regional.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getRegionalPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EU&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ROW&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;month&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;year&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`SELECT unit_amount, currency, tax_behavior
     FROM price_tiers
     WHERE product_id = ? AND region = ? AND interval = ? AND is_active = 1
     ORDER BY created_at DESC
     LIMIT 1`&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Globalization Readiness Checklist
&lt;/h2&gt;

&lt;p&gt;Before launching in a new market, verify each item:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Content is fully translated (not just UI, but transactional emails, docs, error pages)&lt;/li&gt;
&lt;li&gt;[ ] Date/time/number formatting uses &lt;code&gt;Intl.*&lt;/code&gt; APIs (never hardcoded)&lt;/li&gt;
&lt;li&gt;[ ] Currency symbols and formats match local expectations (€29,99 vs ¥199 vs $29.99)&lt;/li&gt;
&lt;li&gt;[ ] Regional payment methods are integrated and tested with sandbox credentials&lt;/li&gt;
&lt;li&gt;[ ] Tax calculation is automated per region (VAT, sales tax, consumption tax)&lt;/li&gt;
&lt;li&gt;[ ] Data residency requirements are met (GDPR EU, PIPL China, CCPA California)&lt;/li&gt;
&lt;li&gt;[ ] Privacy policy covers all applicable regulations (separate versions per region)&lt;/li&gt;
&lt;li&gt;[ ] CDN/edge routing directs users to the nearest regional deployment&lt;/li&gt;
&lt;li&gt;[ ] Email deliverability is configured for regional providers (QQ, 163, GMX, Gmail)&lt;/li&gt;
&lt;li&gt;[ ] Social login options include region-specific providers (WeChat, LINE, LinkedIn)&lt;/li&gt;
&lt;li&gt;[ ] Terms of Service are localized and legally reviewed per jurisdiction&lt;/li&gt;
&lt;li&gt;[ ] Customer support channels cover regional time zones (email, chat, phone)&lt;/li&gt;
&lt;li&gt;[ ] Pricing is localized to market expectations (not just currency conversion)&lt;/li&gt;
&lt;li&gt;[ ] ICP license is obtained for China (allow 4-8 weeks processing time)&lt;/li&gt;
&lt;li&gt;[ ] Cookie consent mechanism is GDPR-compliant (granular, documented)&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Globalizing a SaaS product is a multi-dimensional challenge that touches every part of your stack — from database sharding and edge deployment to payment processing and legal compliance. The key takeaways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build globalization into your architecture from day one&lt;/strong&gt; — retrofitting regional routing, multi-currency pricing, and data residency onto a monolithic system is prohibitively expensive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each market has a dominant constraint&lt;/strong&gt;: GDPR (data privacy) in Europe, ICP/GFW (infrastructure) in China, and sales tax nexus (tax complexity) in the US&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments are the hardest part&lt;/strong&gt; — you need at least 3 payment providers (Stripe, Alipay/WeChat, SEPA) to cover all three markets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge deployment is your friend&lt;/strong&gt; — Cloudflare Workers' regional routing makes data residency compliance much simpler than traditional VPS-based architectures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with real users in each market&lt;/strong&gt; — localization bugs (wrong date formats, truncated text in Chinese characters, missing RTL support) only surface with native users&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The companies that win at globalization are not the ones with the most features — they are the ones that make every user feel like the product was built for their market.&lt;/p&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tanstackship.com/blog/react-internationalization-guide-2026" rel="noopener noreferrer"&gt;React Internationalization Complete Guide (2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanstackship.com/blog/hreflang-tags-ultimate-guide" rel="noopener noreferrer"&gt;hreflang Tags Ultimate Guide: Implementation for Multilingual SPAs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanstackship.com/blog/cloudflare-workers-fullstack-ssr" rel="noopener noreferrer"&gt;Cloudflare Workers Full-Stack SSR: A Complete Guide for SaaS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>saas</category>
      <category>localization</category>
      <category>puppet</category>
      <category>globalization</category>
    </item>
    <item>
      <title>RHEL 10 support now available in Puppet SCE for Linux</title>
      <dc:creator>Jason St-Cyr</dc:creator>
      <pubDate>Thu, 18 Jun 2026 16:16:41 +0000</pubDate>
      <link>https://dev.to/puppet/rhel-10-support-now-available-in-puppet-sce-for-linux-4opi</link>
      <guid>https://dev.to/puppet/rhel-10-support-now-available-in-puppet-sce-for-linux-4opi</guid>
      <description>&lt;p&gt;Version 2.7.0 of Security Compliance Enforcement (SCE) for Linux is now &lt;a href="https://forge.puppet.com/modules/puppetlabs/sce_linux/readme" rel="noopener noreferrer"&gt;available for download from the Forge&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Support for the RHEL 10 family
&lt;/h2&gt;

&lt;p&gt;This release adds Red Hat Enterprise Linux (RHEL) 10 CIS benchmarks (v1.0.1, Server Levels 1 and 2). Teams adopting RHEL 10 or a compatible platform can bring those systems into compliance using the same trusted standards already in place across earlier RHEL versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other improvements
&lt;/h2&gt;

&lt;p&gt;Some other issues were also addressed, including logging issues with the rsyslog configuration file and intrusion detection on RHEL 9.&lt;/p&gt;

&lt;p&gt;For all the details, make sure to read the &lt;a href="https://help.puppet.com/sce/current/linux/scel_relnotes_270.htm" rel="noopener noreferrer"&gt;full release notes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://forge.puppet.com/modules/puppetlabs/sce_linux/readme" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;SCE Module on Puppet Forge&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>Security Compliance Management 3.8.0 Is Now Available</title>
      <dc:creator>Jason St-Cyr</dc:creator>
      <pubDate>Tue, 16 Jun 2026 13:31:38 +0000</pubDate>
      <link>https://dev.to/puppet/security-compliance-management-380-is-now-available-1o26</link>
      <guid>https://dev.to/puppet/security-compliance-management-380-is-now-available-1o26</guid>
      <description>&lt;p&gt;Security Compliance Management (SCM) 3.8.0 is here, with updates focused on keeping compliance scans running reliably with less manual intervention and &lt;strong&gt;an important license update&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This release introduces automatic cleanup for stuck scans, improved control over scan behavior with configurable timeouts, extended CIS-CAT® Pro Assessor license support, and updated benchmark content. It also includes important security fixes across core components.&lt;/p&gt;

&lt;p&gt;⚠️ We recommend upgrading to SCM 3.8.0 before &lt;strong&gt;June 21, 2026&lt;/strong&gt; to avoid disruption, as the CIS-CAT Pro Assessor license included in SCM 3.7.1 expires on that date.&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s changing in SCM 3.8.0
&lt;/h2&gt;

&lt;h3&gt;
  
  
  CIS-CAT Pro Assessor licensing and version
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;SCM 3.8.0 now contains CIS-CAT Pro Assessor v4.63.0&lt;/li&gt;
&lt;li&gt;The bundled &lt;strong&gt;CIS-CAT® Pro Assessor license&lt;/strong&gt; is now valid for &lt;strong&gt;one year&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The license shipped with SCM 3.8.0 is valid until &lt;strong&gt;June 2027&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The license included in &lt;strong&gt;SCM 3.7.1 expires on June 21, 2026&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can now also update the license without upgrading SCM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New &lt;code&gt;license_path&lt;/code&gt; parameter

&lt;ul&gt;
&lt;li&gt;Allows updating the CIS-CAT Pro Assessor license independently
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://help.puppet.com/scm/current/Content/UserGuide/SCM/update_assessor_license.htm" rel="noopener noreferrer"&gt;https://help.puppet.com/scm/current/Content/UserGuide/SCM/update_assessor_license.htm&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Scan management, configuration, and reliability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Added a &lt;strong&gt;background scan sweeper&lt;/strong&gt; to detect and cancel scans stuck in a "running" state&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fixed a race condition where timed-out Puppet Enterprise job status polls could leave scans permanently stuck&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;New &lt;code&gt;assessor_scan_timeout&lt;/code&gt; option controls task timeout for &lt;strong&gt;Windows Server 2022 domain controllers&lt;/strong&gt; (Note: this isn't set by default)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Increased default &lt;strong&gt;Max GraphQL requests limit&lt;/strong&gt; to &lt;strong&gt;300 requests&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Benchmark coverage updates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;New benchmarks added for Amazon Linux 2023 STIG, Microsoft Windows 11 STIG, Oracle Linux 9 STIG, RHEL 10 STIG, and SUSE 16&lt;/li&gt;
&lt;li&gt;Updated benchmarks for: Amazon Linux 2, macOS, Debian, Windows, and Ubuntu (see the release notes for specific benchmark updates)&lt;/li&gt;
&lt;li&gt;Removed benchmarks for:

&lt;ul&gt;
&lt;li&gt;Azure Compute Windows Server 2019 v1.0.1
&lt;/li&gt;
&lt;li&gt;Azure Compute Windows Server 2022 v1.0.0
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Security fixes
&lt;/h2&gt;

&lt;p&gt;This release includes updates to address 40 vulnerabilities across several components. The following components were updated to address the vulnerabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gorm.io&lt;/li&gt;
&lt;li&gt;Keycloak&lt;/li&gt;
&lt;li&gt;netty-codec&lt;/li&gt;
&lt;li&gt;netty-codec-http&lt;/li&gt;
&lt;li&gt;netty-codec-http2&lt;/li&gt;
&lt;li&gt;netty-codec-haproxy&lt;/li&gt;
&lt;li&gt;netty-handler&lt;/li&gt;
&lt;li&gt;Protobuf&lt;/li&gt;
&lt;li&gt;react-router&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Refer to the full release notes for the complete list of CVEs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Upgrade guidance
&lt;/h2&gt;

&lt;p&gt;To avoid scan interruptions, upgrade to &lt;strong&gt;SCM 3.8.0 before June 21, 2026&lt;/strong&gt;. This ensures continued use of the CIS-CAT Pro Assessor, access to updated benchmark content, improved security posture, and improvements in scan processing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Full release notes:
&lt;a href="https://help.puppet.com/scm/current/Content/UserGuide/SCM/Release_notes/release_notes.htm#SecurityComplianceManagement380" rel="noopener noreferrer"&gt;https://help.puppet.com/scm/current/Content/UserGuide/SCM/Release_notes/release_notes.htm#SecurityComplianceManagement380&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have questions or need assistance upgrading, reach out to Puppet Support.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 AI Disclosure
&lt;/h2&gt;

&lt;p&gt;This article was written and reviewed by the author, with the help of AI to assist in pulling together the details from multiple sources and general brand voice alignment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How did I do that?&lt;/strong&gt; For this particular article, I provided Microsoft 365 Copilot with the original release notes, my previous release announcement for 3.7.0, our company brand voice guidelines, and the official product release announcement that went out to customers. The LLM can then pull together the list of things that were updated and create a skeleton of an article. I then rewrite the content as needed to meet with my own tone of voice and get rid of the over-list-based approach that LLMs often take. It's also important to actually check back against the original release notes because sometimes the LLM will change certain words or remove words that change the meaning of what was in the release. I hope this helps if you are also writing with LLMs!&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>security</category>
      <category>devops</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Selenium vs Puppeteer vs Playwright: Complete Guide for Web Scraping, Testing &amp; AI Automation</title>
      <dc:creator>IPFoxy</dc:creator>
      <pubDate>Tue, 16 Jun 2026 12:23:25 +0000</pubDate>
      <link>https://dev.to/ipfoxy/selenium-vs-puppeteer-vs-playwright-complete-guide-for-web-scraping-testing-ai-automation-2619</link>
      <guid>https://dev.to/ipfoxy/selenium-vs-puppeteer-vs-playwright-complete-guide-for-web-scraping-testing-ai-automation-2619</guid>
      <description>&lt;p&gt;In 2026, browser automation has evolved far beyond traditional QA testing and web scraping. With the rise of LLM-powered AI Agents, automation frameworks have become the foundation for autonomous web browsing, task execution, and large-scale AI workflows.&lt;/p&gt;

&lt;p&gt;This guide compares Selenium, Puppeteer, and Playwright across architecture, performance, scalability, AI automation compatibility, and proxy integration.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  I. Introduction to the Three Automation Frameworks
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
&lt;strong&gt;1. Selenium: The Most Established Browser Automation Framework&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Selenium was introduced in 2004 and remains the industry standard for browser automation. With Selenium 5.0, the framework has undergone a major architectural upgrade.&lt;/p&gt;

&lt;p&gt;Key Features:&lt;br&gt;
• Supports Chrome, Firefox, Edge, Safari, and other major browsers.&lt;br&gt;
• Supports Java, Python, C#, JavaScript, and more.&lt;br&gt;
• Large ecosystem and strong enterprise adoption.&lt;/p&gt;

&lt;p&gt;Best For:&lt;br&gt;
Automated testing, enterprise workflows, and projects requiring broad browser compatibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Playwright&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Playwright was open-sourced by Microsoft in 2020 and has become the fastest-growing browser automation framework.&lt;/p&gt;

&lt;p&gt;Key Features:&lt;br&gt;
• Native support for Chromium, Firefox, and WebKit.&lt;br&gt;
• Built-in Auto-Wait mechanism.&lt;br&gt;
• Browser Context support for large-scale concurrency.&lt;/p&gt;

&lt;p&gt;Best For:&lt;br&gt;
Web scraping, AI Agent automation, eCommerce data collection, social media automation, and large-scale browser operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Puppeteer: Google's Native Chrome Automation Tool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Released by the Chrome team in 2017, Puppeteer popularized the Chrome DevTools Protocol (CDP).&lt;/p&gt;

&lt;p&gt;Key Features:&lt;br&gt;
• Simple and intuitive API.&lt;br&gt;
• Fast execution speed.&lt;br&gt;
• Popular in web scraping and browser automation.&lt;/p&gt;

&lt;p&gt;Best For:&lt;br&gt;
Chrome/Chromium automation, web scraping, screenshots, and PDF generation.&lt;/p&gt;
&lt;h2&gt;
  
  
  II. Real-World Performance Comparison
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Startup Speed&lt;/strong&gt;&lt;br&gt;
In raw tests launching a headless browser, Puppeteer and Playwright achieve cold startup speeds of 100-300 milliseconds because they connect directly to the underlying binary protocols via WebSockets. Selenium, however, typically takes 1-2 seconds due to the need to initialize the corresponding Driver process and handle layered HTTP handshakes. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Concurrency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Selenium launches a new browser process for each isolated environment, resulting in significant memory usage.&lt;/p&gt;

&lt;p&gt;Playwright uses BrowserContext, allowing dozens of fully isolated environments to run within a single browser process while reducing resource consumption by over 70%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Stability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modern React and Vue applications render content asynchronously. Selenium often requires manual waits, while Playwright automatically performs Actionability Checks before interacting with elements, improving reliability significantly.&lt;br&gt;
**&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compatibility**
If your enterprise requires testing specific rendering issues on Safari within a physical Mac environment, or if your development team relies strictly on Java/C#, Selenium’s multi-language support and deep-rooted history make it irreplaceable. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;5. AI Agent Compatibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In 2026, AI Agent + Browser Automation has become one of the hottest directions. Projects like Claude Code, OpenAI Agent, and OpenManus all prioritize Playwright by default for three key reasons: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native MCP Protocol Support: Playwright can output the page’s Accessibility Tree directly to the LLM, eliminating the need to parse raw DOM text. &lt;/li&gt;
&lt;li&gt;Highly Efficient Token Usage: Compared to traditional DOM serialization, Playwright's optimized approach reduces token consumption for long sessions by 3 to 4 times. &lt;/li&gt;
&lt;li&gt;Execution Stability: Playwright’s auto-wait and auto-retry mechanisms drastically lower the failure rates of complex agent actions. &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  III. Proxy Integration and Anti-Detection Capabilities
&lt;/h2&gt;

&lt;p&gt;For modern web scraping, accessing a page is only the beginning. Websites commonly evaluate:&lt;/p&gt;

&lt;p&gt;• IP Reputation&lt;br&gt;
• Request Frequency&lt;br&gt;
• Browser Fingerprints&lt;br&gt;
• Geographic Consistency&lt;/p&gt;

&lt;p&gt;Residential Proxies provide a more authentic browsing environment than datacenter IPs and help reduce blocking risks.&lt;br&gt;
IPFoxy offers residential proxies covering 200+ countries, static ISP proxies, mobile 4G/5G proxies, and supports both HTTP(S) and SOCKS5 protocols. It integrates seamlessly with Selenium, Puppeteer, and Playwright to significantly lower blocking risks for long-running scraper projects. &lt;/p&gt;

&lt;p&gt;Here is how to configure an IPFoxy proxy across the three frameworks:&lt;br&gt;
Selenium Proxy Configuration (Python)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selenium.webdriver.chrome.options&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Options&lt;/span&gt;

&lt;span class="n"&gt;chrome_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Configure IPFoxy proxy address (with user/pass authentication)
# Static ISP or rotating residential proxies are recommended for the lowest fraud scores
&lt;/span&gt;&lt;span class="n"&gt;proxy_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://username:password@proxy.ipfoxy.com:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;chrome_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--proxy-server=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;proxy_server&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Launch driver
&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chrome_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://ipinfo.io&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Verify the IP has switched to a clean residential IP from IPFoxy
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Puppeteer Proxy Configuration (JavaScript)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://user:pass@ipfoxy-proxy:port&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`--proxy-server=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;proxy&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playwright Proxy Configuration (Python)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://ipfoxy-proxy:port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  IV. Which Framework Should You Choose in 2026?
&lt;/h2&gt;

&lt;p&gt;Your technical choice should align directly with your business pain points and engineering stack: &lt;br&gt;
&lt;strong&gt;Choose Playwright if:&lt;/strong&gt;&lt;br&gt;
• You are building a new web scraping, data collection, or AI Agent project.&lt;br&gt;
• You need high concurrency and efficient resource usage.&lt;br&gt;
• You work primarily with Python or TypeScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose Puppeteer if:&lt;/strong&gt;&lt;br&gt;
• You are a Node.js-focused developer.&lt;br&gt;
• Your project only targets Chrome/Chromium.&lt;br&gt;
• You require direct access to advanced CDP capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose Selenium if:&lt;/strong&gt;&lt;br&gt;
• Your organization relies heavily on Java or C#.&lt;br&gt;
• You already have large-scale Selenium Grid infrastructure.&lt;br&gt;
• Cross-browser testing is a top priority.&lt;/p&gt;

&lt;h2&gt;
  
  
  V. Conclusion
&lt;/h2&gt;

&lt;p&gt;Selenium, Puppeteer, and Playwright are all excellent browser automation frameworks, but they serve different needs.&lt;/p&gt;

&lt;p&gt;For web scraping, AI Agents, multi-account automation, and large-scale data collection, Playwright is often the most future-proof choice. Combined with high-quality residential proxies, it can significantly improve scraping success rates and long-term stability.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>selenium</category>
      <category>puppet</category>
      <category>playwright</category>
    </item>
    <item>
      <title>Remediating 18 OpenSSL CVEs at Scale with Puppet</title>
      <dc:creator>Jason St-Cyr</dc:creator>
      <pubDate>Mon, 15 Jun 2026 14:10:21 +0000</pubDate>
      <link>https://dev.to/puppet/remediating-18-openssl-cves-at-scale-with-puppet-1abo</link>
      <guid>https://dev.to/puppet/remediating-18-openssl-cves-at-scale-with-puppet-1abo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Written by &lt;a href="https://www.puppet.com/author/paul-reed" rel="noopener noreferrer"&gt;Paul Reed&lt;/a&gt;.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The June 2026 OpenSSL advisory is a big one. &lt;a href="https://www.puppet.com/blog/openssl-cve-2026-45447-patching" rel="noopener noreferrer"&gt;18 vulnerabilities, one rated high severity&lt;/a&gt; with remote code execution potential, and a disclosure credited in part to &lt;a href="https://red.anthropic.com/2026/mythos-preview/" rel="noopener noreferrer"&gt;Anthropic's Mythos model&lt;/a&gt; working alongside researcher Alex Gaynor. Six of those CVEs trace back to that collaboration.&lt;/p&gt;

&lt;p&gt;When an advisory like the OpenSSL one lands, the first question is always the same: where are we exposed? If you run Puppet, you can answer that question across the entire fleet right now, patch it through one mechanism that handles every platform for you, and have it stay patched without anyone watching afterwards. The rest of this article is how.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vulnerability: What CVE-2026-45447 Actually Does
&lt;/h2&gt;

&lt;p&gt;CVE-2026-45447 is a heap use-after-free in &lt;code&gt;PKCS7_verify()&lt;/code&gt;. The bug fires when OpenSSL processes a PKCS#7 or S/MIME signed message where the &lt;code&gt;SignedData.digestAlgorithms&lt;/code&gt; field is an empty ASN.1 SET.&lt;/p&gt;

&lt;p&gt;When OpenSSL encounters this condition, OpenSSL frees a &lt;code&gt;BIO&lt;/code&gt; object that was passed in by the calling application and is still expected to be valid. The calling application then uses the freed pointer. Depending on heap layout, that results in heap corruption, a process crash, or with a controlled heap grooming primitive, code execution.&lt;/p&gt;

&lt;p&gt;Affected ranges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  OpenSSL 3.0.x through 3.3.x (patch to 3.5.1)&lt;/li&gt;
&lt;li&gt;  OpenSSL 1.1.1x (patch to corresponding 1.1.1 update)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The other 17 CVEs in the advisory cover authentication bypass via forged certificates (moderate, roughly a 1-in-256 success rate), ciphertext forgery, private key recovery, root CA replacement, and several DoS vectors. None are trivial in regulated environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Query Your Actual Exposure
&lt;/h2&gt;

&lt;p&gt;Knowing where you're exposed is where Puppet earns its keep on day zero. There's no scanner to stand up and no spreadsheet to chase round the teams. The data is already sitting in PuppetDB.&lt;/p&gt;

&lt;p&gt;Package inventory is fed by Puppet's resource abstraction layer, the same machinery behind &lt;code&gt;puppet resource package&lt;/code&gt;. It enumerates every package provider Puppet Enterprise supports. This means package inventory sees well beyond the system package manager: OS packages across apt, dnf/yum and zypper, and language managers like gem and pip alongside them. One query, every node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;puppet query &lt;span class="s1"&gt;'package_inventory[certname, package_name, version, provider] {
  package_name ~ "(?i)openssl$|libssl$|libcrypto$"
  and
  version ~ "^(3\\.[0-3]\\.|1\\.[0-1]\\.)"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the query against a live environment to review the results. On one fleet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  system libraries across multiple providers (&lt;code&gt;libopenssl3&lt;/code&gt;/&lt;code&gt;libopenssl1_1&lt;/code&gt; via zypper, &lt;code&gt;openssl&lt;/code&gt; and &lt;code&gt;openssl-libs&lt;/code&gt; via dnf/yum, &lt;code&gt;openssl&lt;/code&gt; via apt)&lt;/li&gt;
&lt;li&gt;  the Ruby &lt;code&gt;openssl&lt;/code&gt; gem at several versions&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;openssl&lt;/code&gt; via &lt;code&gt;puppet_gem&lt;/code&gt; on every agent node, because Puppet's own Ruby ships it&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;pyOpenSSL&lt;/code&gt; and &lt;code&gt;python3-openssl&lt;/code&gt; via pip and the OS package manager&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most tools miss the bulk of that, because they only ever look at the system package manager. Here, anything a package manager put on the box is in scope, system libraries and language bindings together.&lt;/p&gt;

&lt;p&gt;Scope the query to an environment by filtering against the inventory endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;puppet query &lt;span class="s1"&gt;'package_inventory[certname, package_name, version, provider] {
  package_name ~ "(?i)openssl$|libssl$|libcrypto$"
  and
  version ~ "^(3\\.[0-3]\\.|1\\.[0-1]\\.)"
  and
  certname in inventory[certname] { environment = "production" }
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Patch It (recommended for the actual remediation)
&lt;/h2&gt;

&lt;p&gt;You don't hand-write a &lt;code&gt;package&lt;/code&gt; resource per platform, and you shouldn't. The package name varies (&lt;code&gt;openssl-libs&lt;/code&gt; on RHEL, &lt;code&gt;libssl3&lt;/code&gt; on Debian, &lt;code&gt;libopenssl3&lt;/code&gt; on SUSE), the versions differ again, and the &lt;code&gt;openssl&lt;/code&gt; CLI isn't even the vulnerable piece, the runtime library is. Let the tooling that already models your estate carry that.&lt;/p&gt;

&lt;p&gt;Use the patching framework: &lt;code&gt;pe_patch&lt;/code&gt; on Puppet Enterprise, &lt;code&gt;os_patching&lt;/code&gt; for open source and Bolt. Classify the class and each node reports its pending updates, including which are security updates. You patch through the PE console or a task, scoped to security updates only if you want the change tight.&lt;/p&gt;

&lt;p&gt;The OS package manager applies the vendor's security update, so the correct library package and version are chosen per platform without you encoding any of it. Reboots, update ordering, and patch and blackout windows are the framework's job. A box that can't be touched in business hours is a blackout window in config, not a workaround.&lt;/p&gt;

&lt;p&gt;The exposure query doubles as your target list. The orchestrator takes PQL directly with &lt;code&gt;-q&lt;/code&gt;, so there's no glue script to write. You hand the orchestrator the query and let it resolve the nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;puppet task run pe_patch::patch_server &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s1"&gt;'package_inventory[certname]{
  package_name ~ "(?i)openssl$|libssl$|libcrypto$"
  and version ~ "^(3\\.[0-3]\\.|1\\.[0-1]\\.)"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's &lt;code&gt;pe_patch::patch_server&lt;/code&gt; on Puppet Enterprise, &lt;code&gt;os_patching::patch_server&lt;/code&gt; for open source and Bolt. The orchestration runs the task against exactly the nodes the query returned and nothing else. Add &lt;code&gt;security_only=true&lt;/code&gt; to keep the run tight, so a node that's already current is a no-op. If you'd rather not touch the command line, wire the same query into a node group in the PE console and drive it from there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Enforce It In Code
&lt;/h2&gt;

&lt;p&gt;If you'd rather declare the state and have Puppet hold the state on every run, use the &lt;code&gt;openssl&lt;/code&gt; module's class. The module knows the package names per platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'openssl'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;package_ensure&lt;/span&gt;         &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;ca_certificates_ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;latest&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;Using &lt;code&gt;latest&lt;/code&gt; keeps the library current; set &lt;code&gt;package_ensure&lt;/code&gt; to a specific version from Hiera if you want a pinned, auditable rollout.&lt;/p&gt;

&lt;p&gt;This enforcement step is something a one-off script and a scanner both miss. A patch run fixes the issue once; declaring the state is what makes it stay fixed. The agent runs on a schedule, every 30 minutes by default, and enforces desired state each time. So when a node drifts back to a vulnerable version, a VM reprovisioned off a stale image, say, or a change someone made by hand and forgot, the next run quietly puts the desired state back. A scanner would notice that regression on its next sweep and open you a ticket. Enforcement just doesn't let the gap stay open that long.&lt;/p&gt;

&lt;p&gt;The two paths aren't either/or. The patching framework is the quicker way to clear the initial backlog; enforcement is what keeps the backlog cleared. Run both and the framework does the first sweep while the module holds the line from then on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Restart Processes That Link the Library
&lt;/h2&gt;

&lt;p&gt;Patching the package is only half the job. A running process keeps the old &lt;code&gt;.so&lt;/code&gt; mapped until the process restarts, and a library swap under a live OpenSSL is exactly the case where that bites.&lt;/p&gt;

&lt;p&gt;If you patched with the framework, it already tracks this for you. &lt;code&gt;pe_patch&lt;/code&gt; reports the processes that require a restart in the node's fact, so you don't go hunting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"reboots"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"app_restart_required"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"apps_needing_restart"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/usr/lib/systemd/systemd --switched-root --system --deserialize 31"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"586"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/usr/lib/systemd/systemd-journald"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"601"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/usr/lib/systemd/systemd-udevd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"657"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/sbin/auditd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"691"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/usr/bin/python3 -s /usr/sbin/firewalld --nofork --nopid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"694"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/usr/sbin/rngd -f --fill-watermark=0 -x pkcs11 -x nist -x qrypt -x namedpipe -x jitter -D daemon:daemon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"696"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/usr/lib/systemd/systemd-logind"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"766"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/usr/sbin/NetworkManager --no-daemon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"863"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sshd: /usr/sbin/sshd -D [listener] 0 of 10-60 startups"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reboot_required"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;apps_needing_restart&lt;/code&gt; is the list of processes still mapping a file that's been replaced underneath them, and &lt;code&gt;reboot_required&lt;/code&gt; flags when a restart of individual services won't cut it. The patch run acts on these according to its reboot policy, so the same job that applies the update also clears the stale library, or tells you precisely which nodes still need a bounce. That's the manual &lt;code&gt;lsof&lt;/code&gt; check in the next section, done for the whole fleet as a fact.&lt;/p&gt;

&lt;p&gt;If you went the module route instead, chain the dependent services onto the class so they refresh when it changes the library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'openssl'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;~&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'nginx'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chaining on the class rather than a &lt;code&gt;Package['...']&lt;/code&gt; title means you don't have to know the package name the module picks per platform. The refresh fires only when something actually changes, so steady-state runs leave your services alone.&lt;/p&gt;

&lt;p&gt;A note on the language-level copies inventory turned up. Where a gem or &lt;code&gt;pyOpenSSL&lt;/code&gt; links the system library, patching &lt;code&gt;libssl&lt;/code&gt;/&lt;code&gt;libcrypto&lt;/code&gt; fixes the underlying crypto and these processes will pick up the patch on restart. The few that statically bundle their own copy get updated through their own toolchain (&lt;code&gt;gem update&lt;/code&gt;, &lt;code&gt;pip&lt;/code&gt;). The OS-packaged bindings ride along with the patch run either way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Verify the Process Has Reloaded the Library
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;reboots&lt;/code&gt; fact above is your fleet-wide answer. When you want to confirm a single box by hand, or you're working somewhere the fact isn't available, &lt;code&gt;lsof&lt;/code&gt; against the actual PID tells you what that process has mapped:&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="nb"&gt;sudo &lt;/span&gt;lsof &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;pgrep nginx | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;libssl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the &lt;code&gt;lsof&lt;/code&gt; query still shows the old path after the run, the restart didn't fire. Check the agent log or the patch run's reboot status. The &lt;code&gt;openssl&lt;/code&gt; CLI version won't help here, the version says nothing about what a long-running daemon has mapped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Confirm Convergence
&lt;/h2&gt;

&lt;p&gt;Re-run the inventory query to verify closure. This query doubles as audit evidence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;puppet query &lt;span class="s1"&gt;'package_inventory[certname, package_name, version, provider] {
  package_name ~ "(?i)openssl$|libssl$|libcrypto$"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anything still on an affected version, across any provider, is your remaining work. Puppet Enterprise users can pull the same data from the compliance dashboard for audit review.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Point Isn't OpenSSL
&lt;/h2&gt;

&lt;p&gt;OpenSSL is this month's fire drill. Next month it's something else, and the one after that hasn't been disclosed yet. None of the steps above were really about OpenSSL. They were about having a tool already in place that answers the questions every advisory asks, before the advisory lands.&lt;/p&gt;

&lt;p&gt;Look back at what each step actually was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The exposure query was inventory: what have I got, and where, as of the last check-in. &lt;/li&gt;
&lt;li&gt;The patch task, scoped from that same query, was remediation. The &lt;code&gt;reboots&lt;/code&gt; fact was reporting, telling you what's still exposed and what needs a bounce. &lt;/li&gt;
&lt;li&gt;Enforcement was the bit that holds the line afterwards, so a reprovisioned box can't slip back in unnoticed. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole vulnerability-response loop, and the worst time to start building it is the morning a CVE lands with the clock already running.&lt;/p&gt;

&lt;p&gt;That's the case for putting the capability in now, while nothing's on fire. The next disclosure becomes a query and a patch run instead of a fortnight of spreadsheets and change tickets. The same loop applies well beyond library upgrades, too. &lt;a href="https://dev.to/puppet/handling-dirty-frag-and-copy-fail-with-puppet-6ff"&gt;We recently showed how to deal with dirty frag and copy-fail with Puppet&lt;/a&gt;, which walks the same detect-mitigate-remediate pattern on a different class of problem.&lt;/p&gt;

&lt;p&gt;Using Puppet provides a consistent way to address OpenSSL vulnerabilities across environments. It'll still be set up and ready when the next vulnerability lands.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 AI Disclosure
&lt;/h2&gt;

&lt;p&gt;This article has been reviewed by a human expert in the subject matter and all code samples have been reviewed by Puppet technical experts. Initial structural content has been generated by Claude AI tools to assist with editing for clarity, structure, grammar, and maintaining brand voice, and then passed through human review.&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>devops</category>
      <category>cybersecurity</category>
      <category>security</category>
    </item>
    <item>
      <title>What You Need to Know About the New puppetlabs-stdlib 10 in June 2026</title>
      <dc:creator>Jason St-Cyr</dc:creator>
      <pubDate>Thu, 11 Jun 2026 14:31:40 +0000</pubDate>
      <link>https://dev.to/puppet/what-you-need-to-know-about-the-new-puppetlabs-stdlib-10-in-june-2026-1jah</link>
      <guid>https://dev.to/puppet/what-you-need-to-know-about-the-new-puppetlabs-stdlib-10-in-june-2026-1jah</guid>
      <description>&lt;h2&gt;
  
  
  The&amp;nbsp;TL;DR&amp;nbsp;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  We are releasing&amp;nbsp;puppetlabs-stdlib&amp;nbsp;10.0.1&amp;nbsp;with a target date of&amp;nbsp;June 30, 2026.&lt;/li&gt;
&lt;li&gt;  This is a major version bump because we are dropping support for Puppet 7 (which&amp;nbsp;reached its end-of-life&amp;nbsp;in&amp;nbsp;February&amp;nbsp;2025) and requiring Ruby 3.1+.&lt;/li&gt;
&lt;li&gt;  If your modules pin&amp;nbsp;stdlib&amp;nbsp;to &amp;lt; 10.0.0, they will continue to work as-is on&amp;nbsp;stdlib&amp;nbsp;9.x.&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;  You do not need to take any immediate action, but we wanted to give the community advance&amp;nbsp;notice&amp;nbsp;so module owners have time to plan.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why a Major Release?&amp;nbsp;
&lt;/h2&gt;

&lt;p&gt;The&amp;nbsp;&lt;a href="https://forge.puppet.com/modules/puppetlabs/stdlib/readme" rel="noopener noreferrer"&gt;puppetlabs-stdlib&amp;nbsp;module&lt;/a&gt; is one of the most widely depended-upon&amp;nbsp;open source&amp;nbsp;modules in the Puppet ecosystem. Because of that, we take major version bumps seriously and want to be transparent about what is changing and why.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;The last release of&amp;nbsp;stdlib&amp;nbsp;(9.7.0) was published in December 2024. Since then, maintenance work has accumulated in the main branch,&amp;nbsp;updates to Puppet Core tooling,&amp;nbsp;CentOS 9 support,&amp;nbsp;Rubocop&amp;nbsp;alignment, CI and testing infrastructure updates, and various bug fixes and enhancements contributed by both Puppet engineers and community members.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Among those accumulated changes is the&amp;nbsp;&lt;strong&gt;removal of Puppet 7 from the supported platforms&lt;/strong&gt;&amp;nbsp;in the module metadata. Since Puppet 7 reached end-of-life over a year ago, this is a natural and expected housekeeping step. However, because removing a previously supported Puppet version is a backwards-incompatible change under&amp;nbsp;semver, it requires a major version bump.&amp;nbsp;By making a major version release along with the removal of Puppet 7 support, this will make a clean delineation for any users still running Puppet 7.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Additionally, the&amp;nbsp;Rubocop&amp;nbsp;and tooling updates in the branch enforce Ruby 3.1+ syntax standards (such as the shorthand hash syntax). While these are primarily code-style changes, they mean the module codebase is no longer guaranteed to run on Ruby versions older than 3.1, which further supports the decision to move to a new major version.&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Included in&amp;nbsp;stdlib&amp;nbsp;10&amp;nbsp;
&lt;/h2&gt;

&lt;p&gt;This release rolls up approximately 18 months of changes. The highlights include:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Puppet 7 support removed:&lt;/strong&gt;&amp;nbsp;The module metadata now requires Puppet 8.0.0 or later. Puppet 7 has been end-of-life since early 2025.&amp;nbsp;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ruby 3.1+&amp;nbsp;required:&lt;/strong&gt;&amp;nbsp;Rubocop&amp;nbsp;and code style enforcement now targets Ruby 3.1 standards. Syntax that is incompatible with older Ruby versions has been adopted throughout the codebase.&amp;nbsp;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CentOS 9 support:&lt;/strong&gt;&amp;nbsp;The module metadata now&amp;nbsp;states&amp;nbsp;support for CentOS 9.&amp;nbsp;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bug fixes and enhancements:&lt;/strong&gt;&amp;nbsp;Multiple community-contributed fixes and improvements that have been pending release.&amp;nbsp;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CI and testing updates:&lt;/strong&gt;&amp;nbsp;Updated&amp;nbsp;testing infrastructure using Puppet Core tooling, updated nightly test matrices, and improved CI workflows.&amp;nbsp;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A full changelog will&amp;nbsp;accompany&amp;nbsp;the release on the Puppet Forge and on GitHub.&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for Module Authors&amp;nbsp;
&lt;/h2&gt;

&lt;p&gt;If you&amp;nbsp;maintain&amp;nbsp;a Puppet module that depends on&amp;nbsp;puppetlabs-stdlib, here is what you need to know.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  If You Do Nothing&amp;nbsp;
&lt;/h3&gt;

&lt;p&gt;Your module will continue to work. Most modules pin their stdlib&amp;nbsp;dependency with an upper bound like &lt;code&gt;"puppetlabs/stdlib": "&amp;gt;= 4.0.0 &amp;lt; 10.0.0"&lt;/code&gt;. The Puppet module resolver will keep you on the latest 9.x release. Nothing breaks, nothing changes.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;If your module did not specify an upper bound, your module will be able to start using version 10 without any changes.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  If You Want to Adopt&amp;nbsp;stdlib&amp;nbsp;10&amp;nbsp;
&lt;/h3&gt;

&lt;p&gt;When you are ready, update the upper bound of your&amp;nbsp;stdlib&amp;nbsp;dependency in your&amp;nbsp;&lt;code&gt;metadata.json&lt;/code&gt;:&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"puppetlabs/stdlib": "&amp;gt;= 4.0.0 &amp;lt; 11.0.0"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will allow your module to resolve either&amp;nbsp;stdlib&amp;nbsp;9.x or 10.x, giving your users flexibility. You should also confirm that your module no longer requires Puppet 7 support and that&amp;nbsp;your&amp;nbsp;testing and CI pipelines use Ruby 3.1 or later.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  Timing Is Up to You&amp;nbsp;
&lt;/h3&gt;

&lt;p&gt;There is no urgency to adopt&amp;nbsp;stdlib&amp;nbsp;10 on day one. The 9.x line will remain available on the Forge. We encourage module owners to update at their own pace, and we are providing this advance notice specifically so you can plan that work into your roadmap rather than being caught off-guard.&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;
  
  
  What&amp;nbsp;Perforce&amp;nbsp;Puppet Is Doing to Prepare&amp;nbsp;
&lt;/h2&gt;

&lt;p&gt;We recognize that&amp;nbsp;stdlib&amp;nbsp;touches a huge&amp;nbsp;portion&amp;nbsp;of the module ecosystem. There are approximately&amp;nbsp;34&amp;nbsp;puppetlabs&amp;nbsp;modules&amp;nbsp;that depend on&amp;nbsp;stdlib. Between now and the release date, we will be updating&amp;nbsp;all of&amp;nbsp;those modules to expand their dependency bounds to accept&amp;nbsp;stdlib&amp;nbsp;10.x. These updated modules will be released to the Forge ahead of or alongside the&amp;nbsp;stdlib&amp;nbsp;10 release so that the transition is as smooth as possible.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Our goal is that by the time&amp;nbsp;stdlib&amp;nbsp;10 lands on the Forge, the&amp;nbsp;puppetlabs&amp;nbsp;module ecosystem will already be ready for it.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeline&amp;nbsp;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Now&lt;/td&gt;
&lt;td&gt;This announcement. Community has advance notice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;June 18–30&lt;/td&gt;
&lt;td&gt;Perforce will release updated dependency bounds across puppetlabs modules.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;June 30, 2026&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Target release date for puppetlabs-stdlib 10 on the Puppet Forge.&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A Note on Communication&amp;nbsp;
&lt;/h2&gt;

&lt;p&gt;We want to acknowledge that an earlier attempt to release&amp;nbsp;stdlib&amp;nbsp;10.0.0 was made without sufficient advance communication to the community. We heard the feedback, rolled that release back, and are now doing this the right way: giving you notice, giving you time, and coordinating the broader module ecosystem before the release lands.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Major&amp;nbsp;stdlib&amp;nbsp;releases have historically been disruptive because of how deeply the module is embedded across the ecosystem. We are committed to making this transition as smooth as possible, and your feedback during this notice period is welcome and appreciated.&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Prepare&amp;nbsp;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Review your dependency bounds:&lt;/strong&gt;&amp;nbsp;Check your&amp;nbsp;metadata.json&amp;nbsp;for your&amp;nbsp;stdlib&amp;nbsp;version constraint.&amp;nbsp; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check your Puppet version support:&lt;/strong&gt;&amp;nbsp;If your module still claims Puppet 7 support, consider whether that is still necessary given Puppet 7 is end-of-life.&amp;nbsp;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check your Ruby version:&lt;/strong&gt;&amp;nbsp;Ensure your testing and CI environments use Ruby 3.1 or later.&amp;nbsp;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test against the main branch:&lt;/strong&gt;&amp;nbsp;If you want to verify compatibility ahead of the release, you can test your module against the main branch of&amp;nbsp;puppetlabs-stdlib&amp;nbsp;on GitHub.&amp;nbsp;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Questions and Feedback&amp;nbsp;
&lt;/h2&gt;

&lt;p&gt;We want to hear from you. If you have questions, concerns, or feedback about this upcoming release, please reach out through any of the following channels:&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Community Slack:&lt;/strong&gt;&amp;nbsp;The &lt;a href="https://slack.puppet.com" rel="noopener noreferrer"&gt;Puppet community Slack workspace&lt;/a&gt;, particularly the &lt;code&gt;#forge-modules&lt;/code&gt; channel where we have already had some discussion on this upcoming change.&amp;nbsp;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Comments on this post:&lt;/strong&gt;&amp;nbsp;We will be&amp;nbsp;monitoring&amp;nbsp;and responding to comments here on dev.to.&amp;nbsp;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Perforce Forums:&lt;/strong&gt; Join the discussion on the&amp;nbsp;&lt;a href="https://portal.perforce.com/s/group/0F9PA000000085d0AA/puppet-product-discussion" rel="noopener noreferrer"&gt;Perforce Puppet forums&lt;/a&gt;, part of the official Perforce Portal.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for being part of the Puppet community!&amp;nbsp;We appreciate your&amp;nbsp;collaboration&amp;nbsp;and your contributions, and we are looking forward to getting&amp;nbsp;this release&amp;nbsp;into your hands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://forge.puppet.com/modules/puppetlabs/stdlib/readme" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;stdlib Forge Module&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>Puppetlabs Modules Roundup – May 2026</title>
      <dc:creator>Jason St-Cyr</dc:creator>
      <pubDate>Wed, 03 Jun 2026 21:48:48 +0000</pubDate>
      <link>https://dev.to/puppet/puppetlabs-modules-roundup-may-2026-2gp2</link>
      <guid>https://dev.to/puppet/puppetlabs-modules-roundup-may-2026-2gp2</guid>
      <description>&lt;p&gt;This time around we look back at May 2026 and the 11 Puppetlabs module releases on the Forge, with an emphasis on the changes most likely to matter in active environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Highlighted Updates
&lt;/h2&gt;

&lt;h3&gt;
  
  
  New Windows audit policy module released!
&lt;/h3&gt;

&lt;p&gt;The new &lt;a href="https://forge.puppet.com/modules/puppetlabs/audit_policy/readme" rel="noopener noreferrer"&gt;audit_policy module&lt;/a&gt; has been released by Perforce as a Ruby replacement for the generated &lt;a href="https://forge.puppet.com/modules/dsc/auditpolicydsc/readme" rel="noopener noreferrer"&gt;DSC community auditpolicydsc module&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This module uses Puppet Resources API for managing Windows audit policy using &lt;code&gt;auditpol.exe&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ruby_task_helper Dependency Bound Update
&lt;/h3&gt;

&lt;p&gt;Five Bolt-adjacent modules all bumped the ruby_task_helper upper bound to &amp;lt; 2.0.0 in a coordinated maintenance pass, helping with dependency resolution failures when using Bolt 5.x.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Affected modules: vault, terraform, http_request, gcloud_inventory, azure_inventory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CentOS 9 Support
&lt;/h3&gt;

&lt;p&gt;Multiple modules added explicit CentOS 9 compatibility, expanding the Linux platform coverage in line with the broader Puppet ecosystem push.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Affected modules: concat, inifile.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Updates Happened to Puppetlabs Modules in May 2026?
&lt;/h2&gt;

&lt;p&gt;The following is an alphabetical listing of modules which received updates in May 2026. If a module had multiple versions released, the updates are collected together, numbered with the "latest" version available.&lt;/p&gt;




&lt;h3&gt;
  
  
  apt 11.3.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-05-19 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/apt" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This release introduced an explicit hash value syntax while also adding a param to support purging keyrings and other community contributions.&lt;/p&gt;

&lt;p&gt;Includes monthly releases: 11.3.1 (2026-05-19), 11.3.0 (2026-05-18).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use explicit hash value syntax instead of shorthand &lt;a href="https://github.com/puppetlabs/puppetlabs-apt/pull/1285" rel="noopener noreferrer"&gt;#1285&lt;/a&gt; (&lt;a href="https://github.com/SugatD" rel="noopener noreferrer"&gt;SugatD&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Add param for purging keyrings &lt;a href="https://github.com/puppetlabs/puppetlabs-apt/pull/1266" rel="noopener noreferrer"&gt;#1266&lt;/a&gt; (&lt;a href="https://github.com/bwitt" rel="noopener noreferrer"&gt;bwitt&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Include components when suite does not end with slash &lt;a href="https://github.com/puppetlabs/puppetlabs-apt/pull/1259" rel="noopener noreferrer"&gt;#1259&lt;/a&gt; (&lt;a href="https://github.com/bwitt" rel="noopener noreferrer"&gt;bwitt&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Bugfix - sources format and ensure =&amp;gt; absent fails &lt;a href="https://github.com/puppetlabs/puppetlabs-apt/pull/1243" rel="noopener noreferrer"&gt;#1243&lt;/a&gt; (&lt;a href="https://github.com/traylenator" rel="noopener noreferrer"&gt;traylenator&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;fix: allow plus signs in ppa &lt;a href="https://github.com/puppetlabs/puppetlabs-apt/pull/1222" rel="noopener noreferrer"&gt;#1222&lt;/a&gt; (&lt;a href="https://github.com/moritz-makandra" rel="noopener noreferrer"&gt;moritz-makandra&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fix and improve DEB822-style template &lt;a href="https://github.com/puppetlabs/puppetlabs-apt/pull/1212" rel="noopener noreferrer"&gt;#1212&lt;/a&gt; (&lt;a href="https://github.com/smortex" rel="noopener noreferrer"&gt;smortex&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  audit_policy 1.0.0
&lt;/h3&gt;

&lt;p&gt;🌟 &lt;strong&gt;&lt;em&gt;New Module:&lt;/em&gt;&lt;/strong&gt; 2026-05-29 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/audit_policy" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This new module allows you to manage Windows audit policy with auditpol.exe as a replacement for the generated DSC community module. Initial release contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;audit_policy_subcategory: manage Windows audit policy subcategories by display name using auditpol.exe&lt;/li&gt;
&lt;li&gt;audit_policy_guid: manage Windows audit policy subcategories by GUID using auditpol.exe&lt;/li&gt;
&lt;li&gt;audit_policy_option: manage global Windows audit policy options (CrashOnAuditFail, FullPrivilegeAuditing, AuditBaseObjects, AuditBaseDirectories)&lt;/li&gt;
&lt;li&gt;audit_policy_csv: manage Windows audit policy by importing settings from an auditpol /backup CSV file&lt;/li&gt;
&lt;li&gt;Support for Windows Server 2016, 2019, 2022, and 2025&lt;/li&gt;
&lt;li&gt;Pure Ruby implementation — no PowerShell dependency; replaces the dsc-auditpolicydsc community module&lt;/li&gt;
&lt;li&gt;Puppet requirement pinned to &amp;gt;= 8.0.0 &amp;lt; 9.0.0&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  azure_inventory 0.5.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-05-14 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/azure_inventory" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This release bumped the ruby_task_helper upper bound to &lt;strong&gt;&amp;lt; 2.0.0&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bump ruby_task_helper upper bound to &amp;lt; 2.0.0 (&lt;a href="https://github.com/puppetlabs/puppetlabs-azure_inventory/pull/16" rel="noopener noreferrer"&gt;#16&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  cd4peadm 5.15.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-05-07 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/cd4peadm" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;A few highlights from this release:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fixed an issue where SAML logins were failing.&lt;/li&gt;
&lt;li&gt;Fixed an issue where CD would not use the configured HTTP timeouts when making calls to the Azure DevOps API, resulting in unexpected timeout failures.&lt;/li&gt;
&lt;li&gt;Fixed an issue where GitLab merge request updates that do not involve code changes would trigger CD pipelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CVE-2026-42198.&lt;/strong&gt; Updated to address this vulnerability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the official &lt;a href="https://help.puppet.com/cdpe/current/Content/UserGuide/CDPE/ReleaseNotes/cd_release_notes.htm#Version5151" rel="noopener noreferrer"&gt;release notes for cd4peadm 5.15.1&lt;/a&gt; for the full details.&lt;/p&gt;




&lt;h3&gt;
  
  
  concat 10.0.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-05-19 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/concat" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This release added support for CentOS 9 while also addressing runner images for Ubuntu 24.04.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redact sensitive content &lt;a href="https://github.com/puppetlabs/puppetlabs-concat/pull/828" rel="noopener noreferrer"&gt;#828&lt;/a&gt; (&lt;a href="https://github.com/smortex" rel="noopener noreferrer"&gt;smortex&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(CAT-2296) Update github runner image to ubuntu-24.04 &lt;a href="https://github.com/puppetlabs/puppetlabs-concat/pull/823" rel="noopener noreferrer"&gt;#823&lt;/a&gt; (&lt;a href="https://github.com/shubhamshinde360" rel="noopener noreferrer"&gt;shubhamshinde360&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(CAT-2152) Add support for CentOS 9 &lt;a href="https://github.com/puppetlabs/puppetlabs-concat/pull/818" rel="noopener noreferrer"&gt;#818&lt;/a&gt; (&lt;a href="https://github.com/skyamgarp" rel="noopener noreferrer"&gt;skyamgarp&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Allow user defined tag or list of tags &lt;a href="https://github.com/puppetlabs/puppetlabs-concat/pull/790" rel="noopener noreferrer"&gt;#790&lt;/a&gt; (&lt;a href="https://github.com/Lightning-" rel="noopener noreferrer"&gt;Lightning-&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Use explicit hash value syntax instead of shorthand &lt;a href="https://github.com/puppetlabs/puppetlabs-concat/pull/835" rel="noopener noreferrer"&gt;#835&lt;/a&gt; (&lt;a href="https://github.com/SugatD" rel="noopener noreferrer"&gt;SugatD&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  gcloud_inventory 0.3.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-05-14 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/gcloud_inventory" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This release bumped the ruby_task_helper upper bound to &lt;strong&gt;&amp;lt; 2.0.0&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bump ruby_task_helper upper bound to &amp;lt; 2.0.0 (&lt;a href="https://github.com/puppetlabs/puppetlabs-gcloud_inventory/pull/14" rel="noopener noreferrer"&gt;#14&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  http_request 0.3.2
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-05-14 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/http_request" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This release bumped the ruby_task_helper upper bound to &lt;strong&gt;&amp;lt; 2.0.0&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bump ruby_task_helper upper bound to &amp;lt; 2.0.0 (&lt;a href="https://github.com/puppetlabs/puppetlabs-http_request/pull/18" rel="noopener noreferrer"&gt;#18&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  inifile 6.4.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-05-19 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/inifile" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The inifile module now supports multiple values per key while also adding support for CentOS 9.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add support for multiple values per key &lt;a href="https://github.com/puppetlabs/puppetlabs-inifile/pull/555" rel="noopener noreferrer"&gt;#555&lt;/a&gt; (&lt;a href="https://github.com/bwitt" rel="noopener noreferrer"&gt;bwitt&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(CAT-2152) Add support for CentOS 9 &lt;a href="https://github.com/puppetlabs/puppetlabs-inifile/pull/549" rel="noopener noreferrer"&gt;#549&lt;/a&gt; (&lt;a href="https://github.com/skyamgarp" rel="noopener noreferrer"&gt;skyamgarp&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  puppet_agent 4.28.0
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-05-07 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/puppet_agent" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Updates to new tooling (Bolt, PDK Templates) as well as support for MacOS 26 and some pre-work for the upcoming Puppet Core 9.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(PA-7824) Use newest Bolt &lt;a href="https://github.com/puppetlabs/puppetlabs-puppet_agent/pull/829" rel="noopener noreferrer"&gt;#829&lt;/a&gt; (&lt;a href="https://github.com/mhashizume" rel="noopener noreferrer"&gt;mhashizume&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PA-7897) Update to pdk-templates 3.6.1.1 &lt;a href="https://github.com/puppetlabs/puppetlabs-puppet_agent/pull/825" rel="noopener noreferrer"&gt;#825&lt;/a&gt; (&lt;a href="https://github.com/joshcooper" rel="noopener noreferrer"&gt;joshcooper&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PA-8250) Allow installation of puppetcore9-nightly packages &lt;a href="https://github.com/puppetlabs/puppetlabs-puppet_agent/pull/822" rel="noopener noreferrer"&gt;#822&lt;/a&gt; (&lt;a href="https://github.com/joshcooper" rel="noopener noreferrer"&gt;joshcooper&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PA-8238) Add support for MacOS 26 in install_shell.sh &lt;a href="https://github.com/puppetlabs/puppetlabs-puppet_agent/pull/821" rel="noopener noreferrer"&gt;#821&lt;/a&gt; (&lt;a href="https://github.com/shubhamshinde360" rel="noopener noreferrer"&gt;shubhamshinde360&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PA-8250) Restore windows command to check puppet service &lt;a href="https://github.com/puppetlabs/puppetlabs-puppet_agent/pull/826" rel="noopener noreferrer"&gt;#826&lt;/a&gt; (&lt;a href="https://github.com/joshcooper" rel="noopener noreferrer"&gt;joshcooper&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PA-8041) Fix puppetcore8-nightly installs on rpm and mac &lt;a href="https://github.com/puppetlabs/puppetlabs-puppet_agent/pull/824" rel="noopener noreferrer"&gt;#824&lt;/a&gt; (&lt;a href="https://github.com/joshcooper" rel="noopener noreferrer"&gt;joshcooper&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;(PA-8247) Add guard against other ruby process when installing &lt;a href="https://github.com/puppetlabs/puppetlabs-puppet_agent/pull/823" rel="noopener noreferrer"&gt;#823&lt;/a&gt; (&lt;a href="https://github.com/AriaXLi" rel="noopener noreferrer"&gt;AriaXLi&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  terraform 0.7.2
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-05-14 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/terraform" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This release bumped the ruby_task_helper upper bound to &lt;strong&gt;&amp;lt; 2.0.0&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bump ruby_task_helper upper bound to &amp;lt; 2.0.0 (&lt;a href="https://github.com/puppetlabs/puppetlabs-terraform/pull/37" rel="noopener noreferrer"&gt;#37&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  vault 0.4.1
&lt;/h3&gt;

&lt;p&gt;📅 Latest release: 2026-05-14 (🌐 &lt;a href="https://forge.puppet.com/modules/puppetlabs/vault" rel="noopener noreferrer"&gt;View on the Forge&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This release bumped the ruby_task_helper upper bound to &lt;strong&gt;&amp;lt; 2.0.0&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bump ruby_task_helper upper bound to &amp;lt; 2.0.0 (&lt;a href="https://github.com/puppetlabs/puppetlabs-vault/pull/19" rel="noopener noreferrer"&gt;#19&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Until Next Time!
&lt;/h2&gt;

&lt;p&gt;That closes out the May 2026 update set. For deeper implementation detail, the linked module pages and release notes remain the best source of truth.&lt;/p&gt;

&lt;p&gt;If you have feedback on the roundup format or want a deeper look at a specific module area, the Perforce Community Slack is still the best place to continue the conversation.&lt;/p&gt;

&lt;p&gt;See you next month with a roundup for June releases!&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Puppet Core 8.19 and PDK 3.7: Security Updates, Dependency Changes, and Windows Fixes</title>
      <dc:creator>Jason St-Cyr</dc:creator>
      <pubDate>Thu, 21 May 2026 16:17:55 +0000</pubDate>
      <link>https://dev.to/puppet/puppet-core-819-and-pdk-37-security-updates-dependency-changes-and-windows-fixes-1j6n</link>
      <guid>https://dev.to/puppet/puppet-core-819-and-pdk-37-security-updates-dependency-changes-and-windows-fixes-1j6n</guid>
      <description>&lt;p&gt;Puppet Core &lt;strong&gt;8.19.0&lt;/strong&gt; focuses largely on security hardening, with some dependency cleanup and a small but important fix for Windows user management.&lt;/p&gt;

&lt;p&gt;If you already run Puppet Core 8, this release is primarily about &lt;strong&gt;keeping your runtime secure and predictable&lt;/strong&gt;, rather than introducing new workflows or configuration changes.&lt;/p&gt;

&lt;p&gt;➡️ Full details: &lt;a href="https://help.puppet.com/core/current/Content/PuppetCore/PuppetReleaseNotes/release_notes_puppet_x-8-19-0.htm" rel="noopener noreferrer"&gt;Puppet Core 8.19.0 release notes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PDK 3.7.0 was also released to improve performance on Windows, update dependencies, and provide other updates for security and known issues.&lt;/p&gt;

&lt;p&gt;➡️ Full details: &lt;a href="https://help.puppet.com/pdk/current/topics/release_notes_pdk.htm#PDK370" rel="noopener noreferrer"&gt;PDK 3.7.0 release notes&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  CSV gem dependency removed
&lt;/h2&gt;

&lt;p&gt;Puppet Core no longer depends on the &lt;strong&gt;CSV&lt;/strong&gt; Ruby gem.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removes an external dependency from the Puppet runtime&lt;/li&gt;
&lt;li&gt;Reduces overall dependency surface area&lt;/li&gt;
&lt;li&gt;Simplifies installation and long-term maintenance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This change does not alter Puppet DSL behavior or require configuration changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security updates
&lt;/h2&gt;

&lt;p&gt;Puppet Core 8.19.0 updates several bundled runtime components to address recently disclosed security vulnerabilities. These updates apply automatically when you upgrade.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ruby&lt;/strong&gt; updated to &lt;strong&gt;3.2.11&lt;/strong&gt; (CVE-2026-27820)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenSSL&lt;/strong&gt; updated to &lt;strong&gt;3.0.20&lt;/strong&gt; (CVE-2026-28387, CVE-2026-28388, CVE-2026-28389, CVE-2026-28390, CVE-2026-31789, CVE-2026-31790)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;libxml2&lt;/strong&gt; updated to &lt;strong&gt;2.15.3&lt;/strong&gt; (CVE-2026-6732)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;curl&lt;/strong&gt; updated to &lt;strong&gt;8.20.0&lt;/strong&gt; (CVE-2026-6253, CVE-2026-6276, CVE-2026-6429, CVE-2026-7009, CVE-2026-7168)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;net-imap&lt;/strong&gt; updated to &lt;strong&gt;0.4.24&lt;/strong&gt; (CVE-2026-42245, CVE-2026-42246, CVE-2026-42256, CVE-2026-42257, CVE-2026-42258)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;erb&lt;/strong&gt; updated to &lt;strong&gt;6.0.4&lt;/strong&gt; (CVE-2026-41316)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ℹ️ Important note for net-imap users
&lt;/h3&gt;

&lt;p&gt;If you use net-imap directly in custom Ruby code, the updated version enforces stricter argument validation. &lt;a href="https://help.puppet.com/core/current/Content/PuppetCore/PuppetReleaseNotes/release_notes_puppet_x-8-19-0.htm" rel="noopener noreferrer"&gt;Check the release notes&lt;/a&gt; for some details on how to manage this gem if you cannot use the upgraded version of net-imap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Windows user passwords now allow colons
&lt;/h2&gt;

&lt;p&gt;When managing &lt;strong&gt;user resources on Windows&lt;/strong&gt;, Puppet Core no longer rejects passwords containing colons (&lt;code&gt;:&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Affects Windows platforms only&lt;/li&gt;
&lt;li&gt;Behavior on other platforms is unchanged&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents unnecessary failures when managing Windows accounts with valid password formats.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installation safeguards for Ruby versions
&lt;/h2&gt;

&lt;p&gt;Puppet Core now prevents installation on unsupported Ruby versions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Puppet Core 8 enforces a &lt;strong&gt;maximum supported Ruby version of 3.x&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Prevents installation attempts using unsupported versions (such as Ruby 4)&lt;/li&gt;
&lt;li&gt;Protects against installation failures like &lt;code&gt;can't modify frozen Hash&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This applies when installing Puppet Core via &lt;strong&gt;bundler or gem commands&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  PDK 3.7.0 changes
&lt;/h2&gt;

&lt;p&gt;This version of PDK was updated to help prevent security issues and reduce test failures, and made macOS 15 downloads available.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rexml updated to version 3.4.4 to address CVE-2025-58767&lt;/li&gt;
&lt;li&gt;macOS 15 downloads now available&lt;/li&gt;
&lt;li&gt;Windows performance has been improved&lt;/li&gt;
&lt;li&gt;YAML file validation issues should be resolved&lt;/li&gt;
&lt;li&gt;Several &lt;code&gt;puppet_forge&lt;/code&gt; gems and dependencies were updated&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Should you upgrade?
&lt;/h2&gt;

&lt;p&gt;Upgrading to Puppet Core &lt;strong&gt;8.19.0&lt;/strong&gt; is recommended for Puppet Core 8.x users if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want current security fixes for bundled runtime dependencies&lt;/li&gt;
&lt;li&gt;You manage Windows users with complex passwords&lt;/li&gt;
&lt;li&gt;You want to avoid accidental installation on unsupported Ruby versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want to get started? Here are the install/upgrade guides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://help.puppet.com/core/current/Content/PuppetCore/installing_and_upgrading.htm" rel="noopener noreferrer"&gt;Install docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.puppet.com/core/current/Content/PuppetCore/upgrade.htm" rel="noopener noreferrer"&gt;Upgrade docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For full details and CVE listings, see the release notes:&lt;/p&gt;

&lt;p&gt;➡️ &lt;a href="https://help.puppet.com/core/current/Content/PuppetCore/PuppetReleaseNotes/release_notes_puppet_x-8-19-0.htm" rel="noopener noreferrer"&gt;Puppet Core 8.19.0 release notes&lt;/a&gt;&lt;br&gt;
➡️ &lt;a href="https://help.puppet.com/pdk/current/topics/release_notes_pdk.htm#PDK370" rel="noopener noreferrer"&gt;PDK 3.7.0 release notes&lt;/a&gt;&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>devops</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Creating Successful Migration Workflows with Puppet</title>
      <dc:creator>Tony Green</dc:creator>
      <pubDate>Mon, 18 May 2026 10:11:59 +0000</pubDate>
      <link>https://dev.to/puppet/creating-successful-migration-workflows-with-puppet-4b8n</link>
      <guid>https://dev.to/puppet/creating-successful-migration-workflows-with-puppet-4b8n</guid>
      <description>&lt;h2&gt;
  
  
  The goal isn't to move things. It's to only move them once.
&lt;/h2&gt;

&lt;p&gt;I've been doing this for over thirty years.&lt;/p&gt;

&lt;p&gt;Sysadmin, ops lead, global teams, and more data centre migrations than I'd like to admit. Site to site, P2V, V2V, cloud, hybrid, all of it.&lt;/p&gt;

&lt;p&gt;Every migration gets sold as a clean, well-planned transition. None of them are.&lt;/p&gt;

&lt;p&gt;They go wrong in very predictable ways. Not because moving infrastructure is especially difficult, but because nobody ever has a clear, current view of what's actually running, what's changed, and what still matters. So people fall back to spreadsheets, SSH sessions, scripts written at 2am, and a lot of "we think this is right".&lt;/p&gt;

&lt;p&gt;That's where migrations fail. Not in the move itself, but in the loss of control around it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why migrations go wrong
&lt;/h3&gt;

&lt;p&gt;After you've done a few of these, the pattern is obvious. Three things show up every time.&lt;/p&gt;

&lt;p&gt;The first is drift. Most environments look well understood on paper. In reality, versions don't match, configurations have wandered off in their own directions, "identical" servers aren't, and there are systems no one owns but everyone is afraid to touch. I once worked on an estate where three "identical" app servers were running three different JVM versions, and nobody could tell me which one production traffic was actually landing on. You start the migration with an incomplete picture and everything after that is guesswork. It gets worse once the migration is underway, because people make manual fixes to keep things moving, and nothing lasts as long as a temporary solution.&lt;/p&gt;

&lt;p&gt;The second is manual execution. No matter how good the plan looks in the slide deck, at some point it turns into a human running commands on a list of hosts they pasted out of a spreadsheet. Sequencing becomes tribal knowledge. Parallelisation becomes guesswork. The rollback plan, if anyone wrote one down, is usually a single line that says "restore from backup".&lt;/p&gt;

&lt;p&gt;The third is lost accountability. During a migration more people have access, more changes are happening, and less of it gets tracked properly. So when something breaks at 3am, nobody is quite sure what changed, or who changed it, or how to undo it. By the time you've worked it out, the on-call engineer has lost an evening and the project has lost a week of trust.&lt;/p&gt;

&lt;p&gt;None of this is new. What's interesting is that all three problems have the same root cause. There's no consistent way to know what your estate looks like and operate it predictably while it's moving.&lt;/p&gt;

&lt;h3&gt;
  
  
  The four things you need to stay in control
&lt;/h3&gt;

&lt;p&gt;Fixing this comes down to four things. You need to know what's actually out there. You need a way to say what it should look like. You need to be able to operate it safely at scale. And you need to know who's allowed to do what. Most teams have bits of all four scattered across different tools, which is why nothing quite lines up when it matters. &lt;a href="https://www.puppet.com/products/puppet-enterprise" rel="noopener noreferrer"&gt;Puppet Enterprise&lt;/a&gt; gives you all four in one place. One model, one set of controls, one audit trail. Not four tools you have to glue together yourself and pray they agree with each other.&lt;/p&gt;

&lt;h4&gt;
  
  
  Visibility
&lt;/h4&gt;

&lt;p&gt;Most migrations start with a discovery phase that's stale before the spreadsheet is finished.&lt;/p&gt;

&lt;p&gt;What you actually want is continuous visibility, and that's what &lt;code&gt;facter&lt;/code&gt; does. &lt;code&gt;facter&lt;/code&gt; runs on every node, collects system information, and reports it back centrally dozens of times a day. Out of the box you get the obvious things (OS, kernel, memory, network) but the part that matters during a migration is &lt;a href="https://help.puppet.com/core/current/Content/PuppetCore/custom_facts.htm" rel="noopener noreferrer"&gt;custom facts&lt;/a&gt;. You can teach &lt;code&gt;facter&lt;/code&gt; to collect anything you care about.&lt;/p&gt;

&lt;p&gt;Here's a small one that reports the version of an in-house payments app by reading a file the deploy pipeline drops on disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Facter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:payments_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;setcode&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/payments/VERSION'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/payments/VERSION'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that fact exists, every node running the app reports its version on every Puppet run. Five minutes later you can ask Puppet Enterprise "show me every node where payments_version is older than 4.2" and get a real answer, not a guess. Multiply that across the dozen things you actually care about (JVM version, storage layout, mount points, whether a vendor agent is running, whether a system is genuinely in use) and you've replaced your discovery spreadsheet with something that updates itself.&lt;/p&gt;

&lt;p&gt;That's the bit most teams never quite get to. Half the battle in a migration is just knowing what you've got.&lt;/p&gt;

&lt;h4&gt;
  
  
  State
&lt;/h4&gt;

&lt;p&gt;Once you know what you've got, the next problem is making sure it behaves the way you expect.&lt;/p&gt;

&lt;p&gt;This is the part Puppet has been doing for years. You define the desired state in code, and that code gets applied consistently across the old data centre, the new one, and any cloud environment you're bringing into the mix. No golden images quietly drifting. No "we rebuilt it by hand and it's nearly the same".&lt;/p&gt;

&lt;p&gt;The reason this matters during a migration is that it lets you stand up the new environment alongside the old one and keep them honest. Same roles, same profiles, same definitions. The question stops being "did we build this correctly?" and becomes "does it match the defined state?". The second question has an answer. The first one rarely does.&lt;/p&gt;

&lt;h4&gt;
  
  
  Execution
&lt;/h4&gt;

&lt;p&gt;At some point you actually have to do things. Stop services, drain load balancers, run pre-flight checks, kick off data sync, validate the result, move on. This is the bit that usually collapses into SSH and good intentions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.puppet.com/docs/pe/2025.0/running_jobs_with_puppet_orchestrator_overview.html" rel="noopener noreferrer"&gt;Puppet Tasks and Plans&lt;/a&gt; give you a way to do it properly. Tasks run actions across large numbers of nodes. Plans sequence those actions and react to what they return. You target nodes by what Puppet already knows about them, not by a hand-built host list, which means the targeting stays correct as the estate changes underneath you.&lt;/p&gt;

&lt;p&gt;Here's roughly what a cutover step looks like as a Plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="nf"&gt;migration::drain_web&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;TargetSpec&lt;/span&gt; &lt;span class="nv"&gt;$nodes&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="nv"&gt;$targets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_targets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'systemctl stop nginx'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$targets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;run_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'healthcheck::wait_for_drain'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$targets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;timeout&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"drained &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;{targets.size} nodes"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing clever. The point is that it's the same plan whether you're draining four nodes or four hundred, it runs through the orchestrator with the same RBAC and the same logging as everything else, and you can call it from another plan that drains the web tier, then the app tier, then flips DNS. The chaos of cutover night turns into something you can rehearse.&lt;/p&gt;

&lt;h4&gt;
  
  
  Control
&lt;/h4&gt;

&lt;p&gt;The last piece is the one most people only think about after something has already broken.&lt;/p&gt;

&lt;p&gt;During a migration more people have access, more changes are happening, and the blast radius of a mistake is bigger. You need to know who can define behaviour, who can execute it, and who can see what's going on. Puppet Enterprise handles that through &lt;a href="https://docs.puppet.com/pe/latest/rbac_intro.html" rel="noopener noreferrer"&gt;RBAC&lt;/a&gt; and its reporting layer. You can let a team run specific tasks against specific systems without handing them the keys to the kingdom, and you can give read-only visibility to the people who need to know what's happening without putting them in the room.&lt;/p&gt;

&lt;p&gt;The same controls apply no matter how the work is triggered. A change ticket in ServiceNow, a step in a CI/CD pipeline, an approval workflow, or someone clicking a button in the console all run the same plans against the same nodes. Same RBAC checks, same audit trail. That matters during a migration because cutover work doesn't all come from one place. Some of it is planned change, some is automated, some is "the network team needs us to drain that rack in the next ten minutes", and you don't want each route to have its own access model and its own log.&lt;/p&gt;

&lt;p&gt;If you're running PE Advanced, the same model extends into &lt;a href="https://www.puppet.com/products/security-compliance-enforcement" rel="noopener noreferrer"&gt;compliance&lt;/a&gt;. Continuous reporting, CIS benchmarks running on every node, and the ability to show an auditor that the new environment was in the expected state on the day you cut over. During a migration that evidence is the difference between "we think it went well" and "here's the report".&lt;/p&gt;

&lt;h4&gt;
  
  
  A few things that come along for the ride
&lt;/h4&gt;

&lt;p&gt;If part of your migration is moving workloads into the cloud, PE Advanced ships with CloudOps and FinOps capabilities that are genuinely useful in flight, not just once you've landed.&lt;/p&gt;

&lt;p&gt;Once your cloud accounts are connected, you get visibility of what's actually running, what it's costing, and what's been left switched on by mistake. Migrations are very good at producing all three: orphaned instances, oversized VMs that someone picked "to be safe", and test environments that nobody remembered to turn off.&lt;/p&gt;

&lt;p&gt;The bit that makes this useful rather than just another dashboard is that it's tied to the same node inventory you're already managing with Puppet. So the cost story doesn't end up as a separate project six months after cutover, run by people who don't know which servers were meant to be there in the first place.&lt;/p&gt;

&lt;p&gt;A couple of other things in PE Advanced are worth knowing about for the same reason. The &lt;a href="https://www.puppet.com/products/observability" rel="noopener noreferrer"&gt;data connector&lt;/a&gt; pushes Puppet's facts, reports, and events into whatever SIEM or observability platform you already run, so the migration shows up in the same dashboards your SOC and SREs are already watching, instead of being a parallel universe nobody looks at. And the &lt;a href="https://www.puppet.com/integrations/servicenow" rel="noopener noreferrer"&gt;ServiceNow integration&lt;/a&gt; syncs bi-directionally with your CMDB, which means the live view Puppet has of the estate stops drifting away from the system of record the rest of the business is using. Both of those matter more during a migration than at any other point in the life of an environment, because both are usually where the wheels come off after cutover.&lt;/p&gt;

&lt;h3&gt;
  
  
  You don't have to throw away what you've got
&lt;/h3&gt;

&lt;p&gt;One thing worth saying clearly, because it comes up in every conversation I have about this. You don't need to rip out your existing tooling to do any of it.&lt;/p&gt;

&lt;p&gt;Most environments I see are a mix of on-prem and cloud, multiple operating systems, and whatever automation has grown over the years. Usually that includes Ansible, a pile of homegrown scripts, and a fair bit of "don't touch that, it works". Trying to standardise all of that during a migration is a mistake. It adds risk at exactly the moment you want less of it.&lt;/p&gt;

&lt;p&gt;A more practical approach is to orchestrate what you already have. Puppet Tasks can call existing scripts and Ansible playbooks, sequence them alongside Puppet-managed actions, and target the right systems based on facts and roles. Instead of maintaining multiple inventories and disconnected workflows, you get one control layer driving everything you already run.&lt;/p&gt;

&lt;p&gt;Puppet isn't replacing those tools. It's coordinating them. That lets you keep what works, cut the duplication, and converge on a more consistent model over time, which is a much safer place to be in the middle of a migration than mid-rewrite.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final thought
&lt;/h3&gt;

&lt;p&gt;A data centre migration isn't really a logistics problem. It's a control problem.&lt;/p&gt;

&lt;p&gt;If you know what's out there, what state it's in, and who's changing it, you can move systems safely, validate them properly, and recover when things don't go to plan. If you can't, it doesn't matter how good the plan looked at kickoff. You'll be discovering the gaps the hard way, one outage at a time.&lt;/p&gt;

&lt;p&gt;The goal of a migration isn't to move things. It's to only have to move them once.&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>automation</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
