<?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: Greg Miller</title>
    <description>The latest articles on DEV Community by Greg Miller (@shrinkray).</description>
    <link>https://dev.to/shrinkray</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F262460%2F2e469f85-6b67-49e9-b87f-9904611fd657.jpg</url>
      <title>DEV Community: Greg Miller</title>
      <link>https://dev.to/shrinkray</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shrinkray"/>
    <language>en</language>
    <item>
      <title>Why email forwarding broke</title>
      <dc:creator>Greg Miller</dc:creator>
      <pubDate>Wed, 28 Oct 2026 12:30:00 +0000</pubDate>
      <link>https://dev.to/shrinkray/why-email-forwarding-broke-1n0e</link>
      <guid>https://dev.to/shrinkray/why-email-forwarding-broke-1n0e</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvm83sz15gdh1x3r30sck.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvm83sz15gdh1x3r30sck.png" alt="representation of email forwarding and bouncing back to the institution." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“My American Express e-mail used to forward. &lt;strong&gt;Now it bounces!&lt;/strong&gt; Everything they send me gets kicked back. Please research this.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That was a message from a client this week, and this has been a reoccuring conversation over the past few years, since he became my client. He has forwarded his business email to a personal account for his entire working life, around 30 years. Until the 2020s, it reliably worked. Things have changed. His bank’s messages bounce, and he believes his email provider is responsible for fixing the problem. When I took him on, I definately wanted to move him off of his longtime email provider, &lt;strong&gt;Network Solutions&lt;/strong&gt; , they did not help, but their email service was not the cause of &lt;strong&gt;this&lt;/strong&gt; problem.&lt;/p&gt;

&lt;p&gt;A habit that worked for three decades broke, and he refuses to believe that he has to change his behavior. But it’s bad business practice to forward all of your email to a personal account. It’s a security risk, and it’s not good for your brand. The problem isn’t the mail service provider. It’s the forwarding itself. It would happen with any provider, and the proof is a single command you can run in a few seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;h1&gt;
  
  
  Open Table of contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/why-email-forwarding-broke#what-changed" rel="noopener noreferrer"&gt;What changed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/why-email-forwarding-broke#why-forwarding-breaks-the-checks" rel="noopener noreferrer"&gt;Why forwarding breaks the checks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/why-email-forwarding-broke#prove-it-with-one-command" rel="noopener noreferrer"&gt;Prove it with one command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/why-email-forwarding-broke#dont-go-cold-turkey" rel="noopener noreferrer"&gt;Don’t go cold turkey&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/why-email-forwarding-broke#why-forwarding-is-a-security-risk" rel="noopener noreferrer"&gt;Why forwarding is a security risk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/why-email-forwarding-broke#need-help-with-this" rel="noopener noreferrer"&gt;Need help with this?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;Email was built on trust. Any server could claim to send mail from any address, and for a long time that mostly worked. Then phishing became a business, and banks became the favorite thing to impersonate.&lt;/p&gt;

&lt;p&gt;The industry answered with three layers of checks, added over about fifteen years:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SPF&lt;/strong&gt; (mid-2000s): the domain publishes a list of servers allowed to send its mail. The receiver checks whether the sending server is on the list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DKIM&lt;/strong&gt; (2007): the sender attaches a cryptographic signature to the message. The receiver checks that the message still matches it. Think of a wax seal on a letter: if the contents were changed, the seal won’t match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DMARC&lt;/strong&gt; (2012): the domain publishes a policy that says what to do when those checks fail. &lt;code&gt;p=none&lt;/code&gt; means “just report it,” &lt;code&gt;p=quarantine&lt;/code&gt; means “send it to spam,” and &lt;code&gt;p=reject&lt;/code&gt; means “refuse it outright.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Adoption was slow until providers got serious. In 2014, &lt;strong&gt;Yahoo&lt;/strong&gt; and &lt;strong&gt;AOL&lt;/strong&gt; published &lt;code&gt;p=reject&lt;/code&gt; for their own domains, and it famously broke mailing lists overnight. In 2024, Google and Yahoo started requiring authentication from bulk senders. Each step made receivers less forgiving of mail that can’t prove where it came from.&lt;/p&gt;

&lt;p&gt;That’s why this “suddenly” broke. Forwarding didn’t change. The rules around it did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why forwarding breaks the checks
&lt;/h2&gt;

&lt;p&gt;When you forward a message, you are a middleman. Your server re-sends someone else’s email, and that causes two problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SPF fails.&lt;/strong&gt; The message now comes from your forwarding server, which isn’t on the bank’s approved list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DKIM sometimes fails.&lt;/strong&gt; If anything in the message changes along the way (a footer, a subject tag), the seal no longer matches.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If both checks fail and the bank has told the world &lt;code&gt;p=reject&lt;/code&gt;, the receiving system is doing exactly what the bank asked: refusing the message. Receivers differ in how strictly they enforce it, and &lt;strong&gt;AOL&lt;/strong&gt; is one of the stricter ones.&lt;/p&gt;

&lt;p&gt;There are fixes on the forwarder’s side (SRS rewrites the sender address, and ARC lets a forwarder vouch for what it saw), but they depend on every system in the chain supporting them. You don’t control that, and neither does your bank.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa35gpwsddckf26fgozsv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa35gpwsddckf26fgozsv.png" alt="Amex or Chase sends directly to ModernMail, where the signature stays intact. Forwarding to AOL breaks the signature, and the rejection returns to the bank." width="680" height="270"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;ModernMail stands in for the mailbox. Direct delivery keeps the signature.&lt;br&gt;
    The forward to AOL breaks it, and the bounce goes back to the bank.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Swap that mailbox for any other host and the check still fails. The strict receiver is what bounces it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prove it with one command
&lt;/h2&gt;

&lt;p&gt;Don’t take my word for it. Your bank publishes its policy in public DNS. On a Mac or Linux terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig TXT _dmarc.americanexpress.com +short
dig TXT _dmarc.chase.com +short
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nslookup &lt;span class="nt"&gt;-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;TXT _dmarc.americanexpress.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I ran these, both returned &lt;code&gt;p=reject&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"v=DMARC1;p=reject;fo=1;rua=mailto:..."
"v=DMARC1; p=reject; pct=100; rua=mailto:..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;pct=100&lt;/code&gt; on the second one means the policy applies to every message. These policies can change, so run the check yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t go cold turkey
&lt;/h2&gt;

&lt;p&gt;The cleanest fix is to stop forwarding. But telling someone to abandon a 30-year habit overnight is a good way to lose their trust. Here is a staged approach that worked for this client.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deliver mail directly to a real mailbox.&lt;/strong&gt; Mail for the business address lands in a proper hosted mailbox first, with no forwarding step. The bank’s messages arrive intact. Any decent provider works; the point is that the bank’s message goes straight to the place you read it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep forwarding for the mail he actually wants elsewhere.&lt;/strong&gt; Instead of forwarding everything except the bank, flip it: forward only a short list of trusted senders (clients, family). Anything a rule misses stays in the new inbox instead of silently bouncing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Give financial accounts a private alias.&lt;/strong&gt; An address like &lt;code&gt;finance@yourdomain.com&lt;/code&gt; that goes to the same inbox but isn’t published anywhere. Point your banks to it, and write one rule that catches everything sent to that alias. It’s also harder for scammers to guess or spoof.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set a review date.&lt;/strong&gt; After 30 days, check whether the old inbox still has anything important. Most people find they’ve stopped opening it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One caution: “forward everything except the bank” looks easier, but it’s fragile. Banks send from several addresses, and other organizations (payment processors, insurers, government agencies) use the same strict policies. A bounce goes to the sender, so you never see what you missed.&lt;/p&gt;

&lt;p&gt;As a bonus, once mail is sent from the proper address, the “I may be replying from a personal device” line in a signature isn’t needed anymore. What people mail to is what they get replies from. &lt;em&gt;ModernMail&lt;/em&gt; makes a great mobile app. Users may find it a better fit for their needs than a traditional, &lt;em&gt;You’ve got mail!&lt;/em&gt; inbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why forwarding is a security risk
&lt;/h2&gt;

&lt;p&gt;A business owner owes that standard to the clients who trust the company with their money, and to the people inside the organization who have to live with the setup. Forwarding business mail into a personal inbox is a security problem. Bank alerts, invoices, and client threads land in an account the business does not control. The checks that prove a message is real get stripped on the way, and a bounce goes back to the sender, so a fraud notice can vanish without anyone at the company seeing it. Replying from a personal address also trains customers to trust mail that did not come from the company domain. That is the habit phishers count on.&lt;/p&gt;

&lt;p&gt;The address on the door is part of the brand. When a message arrives, gets answered, and comes back from the same domain, the business looks like it is paying attention. Missed mail, bounced bank notices, and a signature that apologizes for a personal device do the opposite: they make a careful company look careless. Cleaner email protects the clients and the staff, and it makes the business easier to trust with the next invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Need help with this?
&lt;/h2&gt;

&lt;p&gt;If you have a client, a family member, or your own business email forwarding through &lt;strong&gt;Gmail&lt;/strong&gt; , &lt;strong&gt;AOL&lt;/strong&gt; , or &lt;strong&gt;Yahoo&lt;/strong&gt; and you’re seeing bounces, spam folder surprises, or “I never got that” moments, this is a fixable problem. I help small businesses build their brand, establish stronger email security, improve their email deliverability, and more. I help clients move to reliable email, set up the DNS records (SPF, DKIM, DMARC) behind it, and make the transition gentle enough that people actually stick with it. If you want to learn more about &lt;em&gt;ModernMail&lt;/em&gt;, I’m happy to share what we’re using to help our clients.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://shrinkraylabs.com/contact-me/" rel="noopener noreferrer"&gt;&lt;strong&gt;Shrinkray Interactive&lt;/strong&gt;&lt;/a&gt; can take that handoff: the mailbox, the DNS, and a plan the person will still be using a month later.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Facts in this post were checked against public sources on October 27, 2026. DMARC policies change, so verify with &lt;code&gt;dig&lt;/code&gt; before telling a client.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>email</category>
      <category>security</category>
      <category>dmarc</category>
    </item>
    <item>
      <title>Turn Your Idle Terminal into a Retro Screensaver with Native Zsh and Homebrew</title>
      <dc:creator>Greg Miller</dc:creator>
      <pubDate>Thu, 15 Oct 2026 19:00:00 +0000</pubDate>
      <link>https://dev.to/shrinkray/turn-your-idle-terminal-into-a-retro-screensaver-with-native-zsh-and-homebrew-466l</link>
      <guid>https://dev.to/shrinkray/turn-your-idle-terminal-into-a-retro-screensaver-with-native-zsh-and-homebrew-466l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9cnmt9bjk46m22en89la.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9cnmt9bjk46m22en89la.webp" alt="A retro CRT monitor displaying an animated ASCII terminal screensaver with glowing text reading Automated ASCII Screensavers in Native Zsh" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Terminal customization usually focuses on status prompts, completions, and syntax highlighting. Adding animations when you step away from your keyboard brings back classic desktop nostalgia without bloating your dotfiles.&lt;/p&gt;

&lt;p&gt;Many developers search for Oh My Zsh screensaver plugins, only to hit broken clone URLs, abandoned repositories, or unmaintained dependencies. Zsh already contains the primitives needed to detect idle state and launch commands.&lt;/p&gt;

&lt;p&gt;This guide demonstrates how to configure automatic, randomized terminal screensavers using native Zsh parameters and Homebrew.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;h1&gt;
  
  
  Open Table of contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/zsh-terminal-screensaver-homebrew#the-problem-with-shellsaver-plugins" rel="noopener noreferrer"&gt;The problem with shellsaver plugins&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/zsh-terminal-screensaver-homebrew#step-1-install-terminal-animation-packages" rel="noopener noreferrer"&gt;Step 1: Install terminal animation packages&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/zsh-terminal-screensaver-homebrew#step-2-configure-the-native-zsh-trap" rel="noopener noreferrer"&gt;Step 2: Configure the native Zsh trap&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/zsh-terminal-screensaver-homebrew#step-3-exit-keys-and-terminal-state-recovery" rel="noopener noreferrer"&gt;Step 3: Exit keys and terminal state recovery&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/zsh-terminal-screensaver-homebrew#step-4-verification-checklist" rel="noopener noreferrer"&gt;Step 4: Verification checklist&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/zsh-terminal-screensaver-homebrew#pro-tip-temporarily-disabling-the-screensaver" rel="noopener noreferrer"&gt;Pro-tip: Temporarily disabling the screensaver&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/zsh-terminal-screensaver-homebrew#summary" rel="noopener noreferrer"&gt;Summary&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The problem with shellsaver plugins
&lt;/h2&gt;

&lt;p&gt;Searching for terminal screensaver plugins often leads to dead ends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fatal: repository 'https://github.com/luisico/shellsaver.git/' not found
[oh-my-zsh] plugin 'shellsaver' not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Framework plugins frequently go unmaintained because external wrappers add unnecessary complexity. Zsh ships with a built-in idle detection mechanism: &lt;code&gt;TMOUT&lt;/code&gt; and &lt;code&gt;TRAPALRM&lt;/code&gt;. Bypassing third-party plugin managers yields three distinct benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero framework lock-in:&lt;/strong&gt; The logic runs on vanilla Zsh, Oh My Zsh, Prezto, or custom dotfiles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant process recovery:&lt;/strong&gt; Pure shell traps trigger reliably without background daemon memory leaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pluggable CLI ecosystem:&lt;/strong&gt; Any program that prints to stdout functions as a screensaver.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: Install terminal animation packages
&lt;/h2&gt;

&lt;p&gt;Homebrew provides several polished, low-overhead animation utilities. Install a foundational collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;cmatrix pipes-sh asciiquarium cbonsai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The animation suite
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;cmatrix:&lt;/strong&gt; The classic green cascading code rain from The Matrix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pipes.sh:&lt;/strong&gt; The nostalgic animated pipe maze from Windows 95 screensavers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;asciiquarium:&lt;/strong&gt; An animated marine ecosystem with fish, ducks, sharks, and seaweed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cbonsai:&lt;/strong&gt; An ASCII bonsai tree generator featuring procedural branch growth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test each tool manually in your shell before configuring the automation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cmatrix &lt;span class="nt"&gt;-s&lt;/span&gt;
pipes.sh &lt;span class="nt"&gt;-R&lt;/span&gt;
asciiquarium
cbonsai &lt;span class="nt"&gt;-S&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press &lt;code&gt;q&lt;/code&gt; or &lt;code&gt;Ctrl + C&lt;/code&gt; to exit any active animation (or tap any key if running &lt;code&gt;cmatrix -s&lt;/code&gt; or &lt;code&gt;cbonsai -S&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Configure the native Zsh trap
&lt;/h2&gt;

&lt;p&gt;Zsh tracks idle time via the special shell integer variable &lt;code&gt;TMOUT&lt;/code&gt;. When the terminal remains inactive at an empty prompt for the number of seconds defined by &lt;code&gt;TMOUT&lt;/code&gt;, Zsh triggers an internal &lt;code&gt;ALRM&lt;/code&gt; signal. The shell function &lt;code&gt;TRAPALRM()&lt;/code&gt; intercepts this signal and executes custom commands.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Good to know:&lt;/strong&gt; &lt;code&gt;TMOUT&lt;/code&gt; only counts down while the shell is idle at an interactive prompt. It will never interrupt running builds, test suites, or active foreground processes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shell-local parameter:&lt;/strong&gt; Always set &lt;code&gt;TMOUT=120&lt;/code&gt; without &lt;code&gt;export&lt;/code&gt;. Exporting &lt;code&gt;TMOUT&lt;/code&gt; into your environment causes child processes (such as &lt;code&gt;bash&lt;/code&gt; scripts or subshells) to inherit the timeout without your &lt;code&gt;TRAPALRM&lt;/code&gt; handler, causing them to terminate after 120 seconds of inactivity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before configuring the trap, check if your shell already defines an existing &lt;code&gt;TRAPALRM&lt;/code&gt; handler:&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;typeset&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; TRAPALRM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your terminal prints an existing function body (such as an auto-logout policy or a prompt redraw timer), you can chain it so both behaviors run without conflict.&lt;/p&gt;

&lt;p&gt;Open your Zsh configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Append this block to the bottom of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ==========================================&lt;/span&gt;
&lt;span class="c"&gt;# Terminal Screensaver on Idle&lt;/span&gt;
&lt;span class="c"&gt;# ==========================================&lt;/span&gt;

&lt;span class="c"&gt;# Idle duration in seconds before trigger (120 = 2 minutes)&lt;/span&gt;
&lt;span class="c"&gt;# Keep TMOUT shell-local so child subshells do not inherit it&lt;/span&gt;
&lt;span class="nv"&gt;TMOUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;120

&lt;span class="c"&gt;# Screensaver core routine&lt;/span&gt;
_zsh_screensaver&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# Ensure execution only occurs inside interactive shell prompts&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt; interactive]]&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# Define candidate animation commands&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;savers&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
            &lt;span class="s2"&gt;"cmatrix -abs"&lt;/span&gt;
            &lt;span class="s2"&gt;"pipes.sh -p 4 -R"&lt;/span&gt;
            &lt;span class="s2"&gt;"asciiquarium"&lt;/span&gt;
            &lt;span class="s2"&gt;"cbonsai -S"&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# Select a random item from the array (1-indexed in Zsh)&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;pick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;savers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; RANDOM &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;savers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;# Clear screen, execute selected screensaver, then clear again&lt;/span&gt;
        clear
        &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pick&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        clear
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Preserve any external TRAPALRM handler (refreshes if an external handler was edited)&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;typeset&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; TRAPALRM &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$functions&lt;/span&gt;&lt;span class="s2"&gt;[TRAPALRM]"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;*&lt;/span&gt;_zsh_screensaver&lt;span class="k"&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;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; functions &lt;span class="nt"&gt;-c&lt;/span&gt; TRAPALRM _previous_trapalrm &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;TRAPALRM&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# Execute existing handler first; guard with || true to prevent aborts under ERR_RETURN / ERR_EXIT&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;typeset&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; _previous_trapalrm &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;_previous_trapalrm &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
    &lt;/span&gt;&lt;span class="k"&gt;fi

    &lt;/span&gt;_zsh_screensaver
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save changes (&lt;code&gt;Ctrl + O&lt;/code&gt;, &lt;code&gt;Enter&lt;/code&gt;) and exit nano (&lt;code&gt;Ctrl + X&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Reload your shell environment:&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;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step away from your keyboard. After two minutes of inactivity, Zsh triggers an animation at random.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Exit keys and terminal state recovery
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Stopping an active screensaver
&lt;/h3&gt;

&lt;p&gt;Exit keys vary slightly depending on how each binary handles raw input:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Any keypress:&lt;/strong&gt; Exits &lt;code&gt;cmatrix -abs&lt;/code&gt; and &lt;code&gt;cbonsai -S&lt;/code&gt; instantly, restoring your prompt the moment you resume typing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Press &lt;code&gt;q&lt;/code&gt;:&lt;/strong&gt; Safest exit for &lt;code&gt;pipes.sh&lt;/code&gt; and &lt;code&gt;asciiquarium&lt;/code&gt;. This key triggers clean curses teardown routines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Press &lt;code&gt;Ctrl + C&lt;/code&gt; (SIGINT):&lt;/strong&gt; Sends a termination signal if standard keys fail to respond.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fixing invisible cursors (stty sane)
&lt;/h3&gt;

&lt;p&gt;Interactive CLI programs modify terminal driver flags, hiding the cursor or turning off echo mode. An abrupt exit via &lt;code&gt;Ctrl + C&lt;/code&gt; occasionally interrupts terminal teardown, leaving your prompt invisible or garbled.&lt;/p&gt;

&lt;p&gt;When your prompt ceases to display typed input or hides the cursor, run:&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;stty &lt;/span&gt;sane
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or run the deeper terminal initialization command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;stty sane&lt;/code&gt; instantly restores canonical terminal settings:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;State restored&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Renders typed keys on screen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;icanon&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restores standard line editing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cursor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restores visibility indicator&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 4: Verification checklist
&lt;/h2&gt;

&lt;p&gt;Validate your implementation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Syntax check:&lt;/strong&gt; Run &lt;code&gt;typeset -f TRAPALRM&lt;/code&gt; in your shell. The terminal must print the function body defined in your &lt;code&gt;.zshrc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idle execution:&lt;/strong&gt; Set &lt;code&gt;TMOUT=5&lt;/code&gt; temporarily in your active terminal. Wait five seconds. Confirm execution of a randomized animation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor persistence:&lt;/strong&gt; Terminate the animation. Verify that your command prompt returns cleanly with a visible cursor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean profile:&lt;/strong&gt; Reset &lt;code&gt;TMOUT=120&lt;/code&gt; for routine daily use.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Pro-tip: Temporarily disabling the screensaver
&lt;/h2&gt;

&lt;p&gt;If you are giving a live demo, screen sharing, or recording a tutorial, you might not want animations interrupting your pauses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Temporarily disable idle triggers in the current shell session&lt;/span&gt;
&lt;span class="nb"&gt;unset &lt;/span&gt;TMOUT

&lt;span class="c"&gt;# Re-enable when finished&lt;/span&gt;
&lt;span class="nv"&gt;TMOUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;120
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Custom terminal screensavers require neither heavy dependencies nor unmaintained plugins. By pairing Zsh’s native &lt;code&gt;TMOUT&lt;/code&gt; and &lt;code&gt;TRAPALRM&lt;/code&gt; with Homebrew CLI tools, you gain a dependable, modular idle screensaver that protects your workflow while adding personality to your desk setup.&lt;/p&gt;

</description>
      <category>zsh</category>
      <category>terminal</category>
      <category>cli</category>
      <category>macos</category>
    </item>
    <item>
      <title>Taming WordPress Multisite Bottlenecks: Bot Mitigation &amp; Cron Health with a Custom Plugin</title>
      <dc:creator>Greg Miller</dc:creator>
      <pubDate>Tue, 28 Jul 2026 12:50:00 +0000</pubDate>
      <link>https://dev.to/shrinkray/taming-wordpress-multisite-bottlenecks-bot-mitigation-cron-health-with-a-custom-plugin-9i3</link>
      <guid>https://dev.to/shrinkray/taming-wordpress-multisite-bottlenecks-bot-mitigation-cron-health-with-a-custom-plugin-9i3</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6r3vkzs2vgckmnrjm94e.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6r3vkzs2vgckmnrjm94e.webp" alt="A featured cover illustration depicting server traffic flow transitioning from chaotic bot requests into an organized, protected network pipeline" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Managing high-traffic WordPress Multisite networks often reveals unique performance challenges. In large setups, standard off-the-shelf security or optimization plugins can introduce their own bloat, compounding the very issues they are meant to solve.&lt;/p&gt;

&lt;p&gt;At the end of June 2026, we deployed a custom-engineered solution— &lt;strong&gt;UXPA Network Performance &amp;amp; Guard Suite&lt;/strong&gt; —to address two persistent bottlenecks identified in server log audits: aggressive, automated bot harvesting and recurring database option table serialization locks.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Root Performance Bottlenecks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Automated Credential Harvesting via Username Enumeration
&lt;/h3&gt;

&lt;p&gt;Bots continuously probe WordPress sites searching for valid administrative and author usernames. They typically target default query endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query strings targeting author archives: &lt;code&gt;/?author=N&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Core REST API user endpoints: &lt;code&gt;/wp-json/wp/v2/users&lt;/code&gt; or &lt;code&gt;/wp-json/wp/v2/users/me&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While standard security utilities can block these requests, letting every malicious scan boot up the full WordPress core, query the database, and execute complex plugin chains causes significant CPU and memory spikes. On a multisite network, this traffic degrades performance for genuine users across every sub-site.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cron Array Serialization Failures
&lt;/h3&gt;

&lt;p&gt;WordPress stores scheduled tasks inside a single serialized database option (&lt;code&gt;cron&lt;/code&gt;). When background libraries like Action Scheduler accumulate tens of thousands of historic, failed, or duplicate events, this option row balloons in size.&lt;/p&gt;

&lt;p&gt;Eventually, writing updates to this database row hits MySQL packet limits or thread locks, resulting in repeated system errors:&lt;code&gt;Cron reschedule event error for hook: action_scheduler_run_queue... Error code: could_not_set&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;action_scheduler_run_queue&lt;/code&gt; fails to save its state, background processing stalls, creating a cascading backlog.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: UXPA Network Performance &amp;amp; Guard Suite
&lt;/h2&gt;

&lt;p&gt;To solve these issues without adding third-party plugin overhead, we engineered a dedicated, lightweight network suite.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Architectural Capabilities
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Early-Exit Firewall:&lt;/strong&gt; Intercepts username enumeration attempts during the early &lt;code&gt;init&lt;/code&gt; hook execution phase. It drops connection requests immediately before WordPress initializes heavy database queries or theme resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cron Database Health &amp;amp; Pruning:&lt;/strong&gt; Keeps the scheduler array lean by automatically stripping duplicate task hooks and offering a single-click queue purge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action Scheduler Retention:&lt;/strong&gt; Limits completed action log retention to 7 days (down from the 30-day default), keeping table sizes under control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threat Intelligence Integration:&lt;/strong&gt; Outbound links within the dashboard allow administrators to audit offending IP addresses directly on &lt;a href="https://www.abuseipdb.com/" rel="noopener noreferrer"&gt;AbuseIPDB&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flwl8uexzmt5h6o5qdd0i.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flwl8uexzmt5h6o5qdd0i.webp" alt="A screenshot of the UXPA Network Performance &amp;amp; Guard Suite welcome dashboard in WordPress admin, displaying key plugin feature cards for early-exit bot protection and cron optimization" width="799" height="368"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Investigating Threats with AbuseIPDB
&lt;/h2&gt;

&lt;p&gt;When an IP triggers our early-exit firewall, the dashboard provides direct lookup links to verify its threat profile before applying permanent edge or host-level blocks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flca4gtvubbndlnbh383c.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flca4gtvubbndlnbh383c.webp" alt="A screenshot of the IP security dashboard showing blocked request logs, AbuseIPDB confidence scores, and IP lookup controls for high-volume offending addresses" width="799" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, looking up an offending IP (&lt;code&gt;102.220.160.79&lt;/code&gt;) shows a &lt;strong&gt;100% Abuse Confidence Score&lt;/strong&gt; with 427 recent reports of failed WordPress logins and web application attacks. This confirms that the early-exit firewall is catching legitimate malicious actors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Managing Security &amp;amp; Blocking Strategies
&lt;/h2&gt;

&lt;p&gt;The plugin dashboard aggregates threat statistics and offers quick export options for off-site auditing.&lt;/p&gt;

&lt;p&gt;When an IP demonstrates persistent malicious behavior, we employ a multi-layered blocking strategy:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Edge-Level Blocking (Cloudflare)
&lt;/h3&gt;

&lt;p&gt;Offloading blocked IPs to Cloudflare WAF drops malicious traffic at the CDN edge, preventing requests from reaching our web server entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Host-Level Rules (WP Engine)
&lt;/h3&gt;

&lt;p&gt;For persistent subnet probes, we configure access rules directly within the WP Engine control panel.&lt;/p&gt;

&lt;p&gt;Adding IP deny rules at the host level stops the traffic at the web server layer (Nginx/Apache), protecting PHP execution limits.&lt;/p&gt;




&lt;h2&gt;
  
  
  Restoring Scheduler Health
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Cron Health&lt;/strong&gt; tab provides immediate visibility into the underlying state of the WordPress scheduler array.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Diagnostics Delivered:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cron Option Size Tracking:&lt;/strong&gt; Keeps the serialized byte count visible (currently stabilized at a clean 8.12 KB).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduled Event Auditing:&lt;/strong&gt; Monitors the total number of active queue items (49 total events).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate Event Cap:&lt;/strong&gt; Enforces strict limits on repetitive hook registrations to prevent option row bloat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual Queue Purge:&lt;/strong&gt; Provides a one-click mechanism to purge historic Action Scheduler logs instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvaza9r6uic9d890rb1ti.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvaza9r6uic9d890rb1ti.webp" alt="A screenshot of the Cron Health diagnostic interface displaying total active cron array size in kilobytes, event queue counts, and manual queue cleanup triggers" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Opportunities for Future Improvement
&lt;/h2&gt;

&lt;p&gt;While the suite has successfully stabilized background queue processing and short-circuited bot traffic, there are several areas targeted for future enhancement:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Dedicated Custom Database Table (&lt;code&gt;dbDelta&lt;/code&gt; Migration):&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Current State:&lt;/em&gt; Telemetry data uses bounded &lt;code&gt;update_site_option()&lt;/code&gt; writes. In WordPress Multisite, &lt;code&gt;sitemeta&lt;/code&gt; options preload across the entire network on every page request.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Enhancement:&lt;/em&gt; Transition log storage to a dedicated custom table (&lt;code&gt;wp_uxpa_security_logs&lt;/code&gt;). This will remove telemetry writes from the preloaded network memory path entirely, allow longer log retention windows, and enable fast indexed per-IP querying.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Automated Cloudflare API Integration:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Current State:&lt;/em&gt; High-volume offending IPs are reviewed manually before copying them into Cloudflare or host firewall lists.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Enhancement:&lt;/em&gt; Add an optional Cloudflare API endpoint integration to allow one-click IP banning directly from the plugin dashboard into Cloudflare WAF rulesets.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Automated Cron Array Auto-Recovery:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Enhancement:&lt;/em&gt; Implement an automated health check that detects if the core &lt;code&gt;cron&lt;/code&gt; option string becomes corrupted or un-serializable, automatically executing an array repair routine to prevent execution stalls entirely.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let me build a custom plugin for your community or multisite website
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F73amohaiwz8fo6mnfhmh.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F73amohaiwz8fo6mnfhmh.webp" alt="Tired of evaluating freemium plugins that don't quite serve your needs? Let me build one that works perfectly for you. Contact Shrinkray Interactive!" width="600" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Need a custom WordPress plugin tailored to your community, business, or multisite website? &lt;a href="https://shrinkraylabs.com/contact-me/" rel="noopener noreferrer"&gt;&lt;strong&gt;Shrinkray Interactive&lt;/strong&gt;&lt;/a&gt; is here to help. We research your specific needs and design, evaluate, implement, and continuously improve your white-labeled plugin.&lt;/p&gt;

&lt;p&gt;Ready to lock down your network? Reach out to Greg to get your own custom plugin developed today!&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>multisite</category>
      <category>php</category>
      <category>performance</category>
    </item>
    <item>
      <title>Reasons to set up a Cloudflare Tunnel</title>
      <dc:creator>Greg Miller</dc:creator>
      <pubDate>Sat, 20 Jun 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/shrinkray/reasons-to-set-up-a-cloudflare-tunnel-54ee</link>
      <guid>https://dev.to/shrinkray/reasons-to-set-up-a-cloudflare-tunnel-54ee</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2mpdfsj74blvdetvknp1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2mpdfsj74blvdetvknp1.webp" alt="A diagram illustrating how Cloudflare Tunnel works, showing a local service connected outbound to Cloudflare's edge network, with traffic flowing securely between them" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s say you are running a service on your home network—maybe a smart home dashboard (like Home Assistant), a private website you’re coding, or a home file server. You want to be able to access it when you are away from home, or you want a friend to look at it.&lt;/p&gt;

&lt;p&gt;To let the outside internet in, the old-school method is called &lt;strong&gt;port forwarding&lt;/strong&gt;. This is the digital equivalent of unlocking a specific window in your house, publishing your home address to the world, and hoping no burglars notice the open window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt; changes the game. Instead of unlocking any windows, your home computer reaches out to Cloudflare’s secure servers in the cloud and establishes a private, encrypted hallway between them. When someone wants to visit your site, they talk to Cloudflare, and Cloudflare passes the message back to your computer through this private hallway. No windows are unlocked, and your home address remains hidden.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Do I need this?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you are running a self-hosted app, home server, or development site and need it safely accessible from the internet, a Cloudflare Tunnel is the gold standard. If you’d rather not deal with the configuration, DNS routing, or firewall settings yourself, &lt;strong&gt;Shrinkray Interactive&lt;/strong&gt; can handle the entire setup for you.&lt;/p&gt;

&lt;p&gt;To learn how tunnels work and why they are so secure, see below.&lt;/p&gt;

&lt;p&gt;Ready to secure your network? &lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#custom-tunnel" rel="noopener noreferrer"&gt;Reach out to Greg to get your own custom tunnel configured today&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;h1&gt;
  
  
  Open Table of contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#you-stop-opening-doors-on-your-router" rel="noopener noreferrer"&gt;You stop opening doors on your router&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#if-they-cant-find-your-door-they-cant-knock-on-it" rel="noopener noreferrer"&gt;If they can’t find your door, they can’t knock on it&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#changing-home-addresses-stops-being-a-headache" rel="noopener noreferrer"&gt;Changing home addresses stops being a headache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#free-security-padlocks-without-the-headaches" rel="noopener noreferrer"&gt;Free security padlocks without the headaches&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#your-small-project-gets-enterprise-grade-shields" rel="noopener noreferrer"&gt;Your small project gets enterprise-grade shields&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#add-a-bouncer-to-your-site-in-two-clicks" rel="noopener noreferrer"&gt;Add a bouncer to your site in two clicks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#easy-integrations-for-developers-webhooks" rel="noopener noreferrer"&gt;Easy integrations for developers (Webhooks)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#it-works-even-when-your-internet-provider-blocks-you" rel="noopener noreferrer"&gt;It works even when your internet provider blocks you&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#the-free-plan-is-actually-free-and-powerful" rel="noopener noreferrer"&gt;The free plan is actually free and powerful&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#when-is-a-tunnel-not-the-right-tool" rel="noopener noreferrer"&gt;When is a tunnel &lt;em&gt;not&lt;/em&gt; the right tool?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#time-to-lock-the-doors" rel="noopener noreferrer"&gt;Time to lock the doors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcharms.com/posts/reasons-to-set-up-a-cloudflare-tunnel#let-me-build-your-own-custom-tunnel" rel="noopener noreferrer"&gt;Let me build your own Custom Tunnel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  You stop opening doors on your router
&lt;/h2&gt;

&lt;p&gt;Ports are like doors on your internet router. By default, your router blocks all incoming traffic to keep you safe. Port forwarding means you carved a small pet door into your firewall and pointed it directly at your device (like a home storage drive or computer).&lt;/p&gt;

&lt;p&gt;The problem is that automated bots crawl the internet 24/7, shaking every door handle they can find. If you leave a port open, those bots will find it and try to exploit outdated software or guess your passwords.&lt;/p&gt;

&lt;p&gt;With a tunnel, you don’t open any doors. The tunnel software (a tiny program running on your computer) makes an &lt;strong&gt;outbound&lt;/strong&gt; connection. It’s like your computer calling out to Cloudflare and keeping the phone line open. Traffic comes in through that phone line, meaning your router’s doors remain locked tight.&lt;/p&gt;

&lt;p&gt;For homelabs, side projects, and small business gear sitting on a consumer connection, that is a meaningful security upgrade without buying enterprise hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  If they can’t find your door, they can’t knock on it
&lt;/h2&gt;

&lt;p&gt;Attackers can’t hack what they can’t see. When you use port forwarding, your public IP address is exposed, meaning anyone can scan your connection. If there is a bug (a security vulnerability) in your home software, they can exploit it.&lt;/p&gt;

&lt;p&gt;With a tunnel, your home setup is invisible to the public internet. Visitors only see Cloudflare’s massive network. Think of Cloudflare as a highly trained security guard standing at the entrance of a gated community. If a bot or hacker tries to spam your site or send malicious traffic, the guard blocks them at the gate before they ever get near your actual home network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing home addresses stops being a headache
&lt;/h2&gt;

&lt;p&gt;Every home internet connection has an IP address—think of it as your house’s mailing address. However, home internet providers constantly change this address every few days or weeks (this is called a “dynamic IP”). If you are hosting a website, this means your site’s address is constantly changing, making it impossible for people to reliably connect to it.&lt;/p&gt;

&lt;p&gt;Normally, you have to buy a costly “static IP” or set up complicated updater tools (Dynamic DNS) that break when you least expect it.&lt;/p&gt;

&lt;p&gt;Because a Cloudflare Tunnel keeps a constant connection open from your computer &lt;em&gt;out&lt;/em&gt; to Cloudflare, it doesn’t matter if your home’s mailing address changes. Cloudflare always knows where your computer is because the computer is the one keeping the connection alive. Your website name (like &lt;code&gt;myproject.com&lt;/code&gt;) stays online without you needing to lift a finger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free security padlocks without the headaches
&lt;/h2&gt;

&lt;p&gt;When you visit a website, you expect to see the secure padlock icon in your browser address bar. This is powered by HTTPS and an SSL/TLS certificate—a digital security badge proving your site is secure.&lt;/p&gt;

&lt;p&gt;Setting up these certificates on your own home computer is notoriously frustrating. You have to install renewal tools, make sure they run every few months, and configure complex server software. If something breaks, visitors get a scary “This site is not secure” warning.&lt;/p&gt;

&lt;p&gt;When you use a tunnel, Cloudflare automatically provides and manages the SSL certificate for you. Visitors get a secure, encrypted connection to Cloudflare, and Cloudflare encrypts the rest of the journey to your home computer. You get the padlock icon instantly without configuring any certificate tools at home.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your small project gets enterprise-grade shields
&lt;/h2&gt;

&lt;p&gt;Your home computer or a cheap virtual server was never designed to handle thousands of visits at once. If your site gets popular, or if a malicious group tries to overwhelm it with fake traffic (a &lt;strong&gt;DDoS attack&lt;/strong&gt; ), your home internet will crash.&lt;/p&gt;

&lt;p&gt;By routing your traffic through Cloudflare’s tunnel, you get to borrow their massive global network. If a flood of traffic hits your site, Cloudflare absorbs it and only sends the legitimate visits down the tunnel to your home computer. It’s like having a giant buffer that prevents your home internet from being knocked offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a bouncer to your site in two clicks
&lt;/h2&gt;

&lt;p&gt;Just because your project is online doesn’t mean you want the public to see it. Maybe it’s a private family photo album or a dashboard for your home smart devices.&lt;/p&gt;

&lt;p&gt;Normally, keeping these secure means setting up a complex VPN (Virtual Private Network) or managing logins inside your app.&lt;/p&gt;

&lt;p&gt;With Cloudflare, you can turn on a feature called &lt;strong&gt;Cloudflare Access&lt;/strong&gt;. This acts like a bouncer at the door. Before anyone is even allowed to see your website, they have to log in using an approved method—like their Google account, GitHub, or a one-time email code. If they aren’t on the list, Cloudflare turns them away before they ever reach your computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Easy integrations for developers (Webhooks)
&lt;/h2&gt;

&lt;p&gt;If you are building apps, you often need external services to send messages to your local computer. For example, when someone pays you on &lt;strong&gt;Stripe&lt;/strong&gt; or pushes code to &lt;strong&gt;GitHub&lt;/strong&gt; , those services need to send a notification (a “webhook”) to your project.&lt;/p&gt;

&lt;p&gt;Getting these notifications to reach a computer in your living room is incredibly hard because home networks block outside requests by default.&lt;/p&gt;

&lt;p&gt;A tunnel gives you a simple, public link that connects directly to the app running on your computer. You can test payment systems, chat bots, and other tools on your own computer without having to pay for online servers or mess with insecure router settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  It works even when your internet provider blocks you
&lt;/h2&gt;

&lt;p&gt;Many modern internet providers use a technology called &lt;strong&gt;CGNAT&lt;/strong&gt; (Carrier-Grade NAT). Essentially, they share a single public IP address among hundreds of different homes in your neighborhood. Because of this, traditional port forwarding is physically impossible—you don’t have your own “front door” to unlock.&lt;/p&gt;

&lt;p&gt;Similarly, if you are at an office, a university, or a coffee shop, their firewalls will block any incoming connections.&lt;/p&gt;

&lt;p&gt;But because a Cloudflare Tunnel is an &lt;strong&gt;outbound&lt;/strong&gt; connection (your computer initiating the call to Cloudflare), CGNAT and strict network firewalls don’t block it. If your computer can browse the web, it can host a tunnel.&lt;/p&gt;

&lt;h2&gt;
  
  
  The free plan is actually free and powerful
&lt;/h2&gt;

&lt;p&gt;Usually, security tools like this are expensive or have severely limited free plans. Fortunately, Cloudflare’s tunnel feature is completely free for personal use, side projects, and small teams. You can run multiple tunnels, connect different projects, and secure them with login screens without paying a cent.&lt;/p&gt;

&lt;h2&gt;
  
  
  When is a tunnel &lt;em&gt;not&lt;/em&gt; the right tool?
&lt;/h2&gt;

&lt;p&gt;Tunnels are amazing for making a specific app or website reachable. But they aren’t a cure-all:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If you need access to your entire home network:&lt;/strong&gt; If you want to connect to your home printer, your smart TV, and all your computers at once while away, a VPN (like WireGuard or Tailscale) is a better choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you have strict data privacy needs:&lt;/strong&gt; Since all traffic goes through Cloudflare’s servers to be inspected for security, companies with strict government compliance rules may not want their data passing through a third party.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Time to lock the doors
&lt;/h2&gt;

&lt;p&gt;Setting up a tunnel usually takes under ten minutes. You install a small helper program, log in to your Cloudflare account, name your website, and point it at the app running on your computer.&lt;/p&gt;

&lt;p&gt;Once you do, you can log into your home router and lock every single inbound port. You stop thinking about your home’s public IP address as your front door. Cloudflare becomes your front door, your bouncer, and your security system—giving you peace of mind that your home network is completely secure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let me build your own Custom Tunnel
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1nj5hu4wgk5d2fsyryw9.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1nj5hu4wgk5d2fsyryw9.webp" alt="Secure your website with Cloudflare Tunnels" width="600" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to secure your self-hosted websites, home servers, or private projects but don't want to wrestle with command line tools, DNS setups, or firewall settings, &lt;a href="https://shrinkraylabs.com/contact-me/" rel="noopener noreferrer"&gt;&lt;strong&gt;Shrinkray Interactive&lt;/strong&gt;&lt;/a&gt; is here to help. We can design and implement secure outbound tunnel architectures, configure authentication bouncers, and audit your remote access setups to make sure your home or business network stays completely protected.&lt;/p&gt;

&lt;p&gt;Ready to lock down your network? Reach out to Greg to get your own custom tunnel configured today!&lt;/p&gt;

</description>
      <category>cloudflare</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>Hunting Dictionary Bots: Inside a Sophisticated WordPress Membership Attack</title>
      <dc:creator>Greg Miller</dc:creator>
      <pubDate>Wed, 27 May 2026 12:00:00 +0000</pubDate>
      <link>https://dev.to/shrinkray/hunting-dictionary-bots-inside-a-sophisticated-wordpress-membership-attack-1ihb</link>
      <guid>https://dev.to/shrinkray/hunting-dictionary-bots-inside-a-sophisticated-wordpress-membership-attack-1ihb</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo9xiqat5xr9pjo6tzw59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo9xiqat5xr9pjo6tzw59.png" alt="A screenshot of a WordPress membership signup form with a CAPTCHA and a WAF blocking a dictionary bot" width="799" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Automated registration spam is shifting away from traditional brute-force tactics. Standard security layers often fail against modern bot scripts that pass visual challenges and bypass baseline web application firewalls. These advanced campaigns mimic legitimate human patterns to compromise open registration endpoints.&lt;/p&gt;

&lt;p&gt;This post examines the mechanics of &lt;strong&gt;Dictionary Attack Bots&lt;/strong&gt; , details the concrete risks they pose to membership platform integrity, and provides implementation code to secure your user tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Membership Threat: Impact by the Numbers
&lt;/h2&gt;

&lt;p&gt;Allowing automated registrations to pool in your database creates immediate operational hazards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;42% Database Inflation:&lt;/strong&gt; Automated accounts distort core engagement metrics. They skew active user KPIs, churn rates, retention tracking, and email marketing segmentation analytics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Origin Server Overhead:&lt;/strong&gt; Bulk account creation triggers repetitive &lt;code&gt;INSERT&lt;/code&gt; queries, invalidates user cache clusters, and drives high CPU utilization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8.3x Email Delivery Risk:&lt;/strong&gt; Automated sign-ups generate high bounce rates and trigger spam complaints during onboarding automation. This degrades your transactional email domain authority and lands critical system emails in user spam folders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downstream Vulnerabilities:&lt;/strong&gt; Attackers weaponize these dormant accounts later for credential stuffing, automated API scraping, or content/comment injection.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Anatomy of the Exploit: Dictionary Word Pairing
&lt;/h2&gt;

&lt;p&gt;Traditional registration scripts rely on randomized character strings (e.g., &lt;code&gt;xj947_ktr&lt;/code&gt;). Modern security filters identify and block these strings easily. To circumvent detection, this script uses coordinated dictionary pairings.&lt;/p&gt;

&lt;p&gt;The bot engine generates identifiers by combining two distinct English dictionary terms, appended with a single or double digit: &lt;code&gt;[Noun/Adj] + [Noun] + [Digit]&lt;/code&gt;. This structure targets validation suites that assume standard dictionary terms indicate human input.&lt;/p&gt;

&lt;p&gt;Data from an active database trace reveals clear structural patterns across separate registration batches:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;User ID&lt;/th&gt;
&lt;th&gt;Registered Username&lt;/th&gt;
&lt;th&gt;Target Domain Name&lt;/th&gt;
&lt;th&gt;Structural Formula&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4372&lt;/td&gt;
&lt;td&gt;&lt;code&gt;basketoption7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;koiot.de&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[Noun] + [Noun] + [Digit]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3537&lt;/td&gt;
&lt;td&gt;&lt;code&gt;basketperch1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;koiot.de&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[Noun] + [Noun] + [Digit]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4341&lt;/td&gt;
&lt;td&gt;&lt;code&gt;basketriver9&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;koiot.de&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[Noun] + [Noun] + [Digit]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4481&lt;/td&gt;
&lt;td&gt;&lt;code&gt;basketwomen0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;koiot.de&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[Noun] + [Noun] + [Digit]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4528&lt;/td&gt;
&lt;td&gt;&lt;code&gt;actman4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;koiot.de&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[Noun] + [Noun] + [Digit]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;&lt;code&gt;adultmiddle4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;calculator.city&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[Adj] + [Noun] + [Digit]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;&lt;code&gt;actormuseum8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bitingmites.org&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[Noun] + [Noun] + [Digit]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The script rotates target domains across separate waves, altering infrastructure footprints while maintaining the underlying username generation logic. The email handles often use standard human name formats (e.g., &lt;code&gt;johne@calculator.city&lt;/code&gt;) to blend in with valid registrations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Defensive Engineering: Programmatic Remediation
&lt;/h2&gt;

&lt;p&gt;Because these registration payloads exist entirely within the body of incoming &lt;code&gt;POST&lt;/code&gt; requests, standard proxy layers cannot always parse or drop them based on payload content alone. Remediating this threat requires a defense-in-depth approach across your technical stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Application Layer Domain Restrictions
&lt;/h3&gt;

&lt;p&gt;The fastest method for stopping active registration waves involves validating the registration array directly inside WordPress. Hooking into the &lt;code&gt;registration_errors&lt;/code&gt; filter intercepts the registration process before the application executes a database transaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cd"&gt;/**
 * Restrict known malicious domains from creating WordPress user accounts.
 * Drops the registration request before hitting the database.
 */&lt;/span&gt;
&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'registration_errors'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'devcharms_restrict_spam_domains'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;devcharms_restrict_spam_domains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$sanitized_user_login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user_email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$banned_domains&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'24faw.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'igtikrenai.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'koiot.de'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'temz.net'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'emailmin.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'calculator.city'&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nv"&gt;$email_parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user_email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$email_parts&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="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;strtolower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;$banned_domains&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$errors&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'banned_domain_error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;strong&amp;gt;ERROR&amp;lt;/strong&amp;gt;: Registration parameters rejected.'&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="nv"&gt;$errors&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;
  
  
  2. Remediate Existing Users (Purge the Spam Already Inside)
&lt;/h3&gt;

&lt;p&gt;Once these accounts exist, blocking new registrations won’t fix the damage already done to your tables and analytics. The simplest remediation path is to go to &lt;strong&gt;Admin → Users&lt;/strong&gt; , search by the offending domain (for example &lt;code&gt;@koiot.de&lt;/code&gt;), and bulk-delete the matching accounts.&lt;/p&gt;

&lt;p&gt;This approach (Admin UI bulk delete or WP-CLI) is almost always safer than “just deleting rows” in the database. User emails and IDs can be duplicated across core and plugin tables, and a manual DB purge can leave orphaned metadata, broken relationships, or stale activity records—especially on community sites running BuddyPress, bbPress, membership plugins, or LMS/ecommerce add-ons.&lt;/p&gt;

&lt;p&gt;Common places these users can persist include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core WordPress&lt;/strong&gt; : &lt;code&gt;wp_users&lt;/code&gt;, &lt;code&gt;wp_usermeta&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BuddyPress&lt;/strong&gt; : &lt;code&gt;wp_bp_activity&lt;/code&gt;, &lt;code&gt;wp_bp_activity_meta&lt;/code&gt;, &lt;code&gt;wp_bp_friends&lt;/code&gt;, &lt;code&gt;wp_bp_groups*&lt;/code&gt;, &lt;code&gt;wp_bp_notifications&lt;/code&gt;, &lt;code&gt;wp_bp_messages*&lt;/code&gt;, &lt;code&gt;wp_bp_xprofile*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bbPress&lt;/strong&gt; : forum/topic/reply content in &lt;code&gt;wp_posts&lt;/code&gt; plus related post meta in &lt;code&gt;wp_postmeta&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other plugins&lt;/strong&gt; : their own custom tables and user meta keys (memberships, LMS, ecommerce, forms, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have WP-CLI access, you can do the same cleanup much faster from the command line by deleting users whose email matches a domain pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp user delete &lt;span class="si"&gt;$(&lt;/span&gt;wp user list &lt;span class="nt"&gt;--search&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*@koiot.de"&lt;/span&gt; &lt;span class="nt"&gt;--field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ID&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>security</category>
      <category>wordpress</category>
      <category>automation</category>
    </item>
    <item>
      <title>Ditch the post-it notes: protect your business secrets with a password vault</title>
      <dc:creator>Greg Miller</dc:creator>
      <pubDate>Mon, 11 May 2026 20:00:00 +0000</pubDate>
      <link>https://dev.to/shrinkray/ditch-the-post-it-notes-protect-your-business-secrets-with-a-password-vault-30jj</link>
      <guid>https://dev.to/shrinkray/ditch-the-post-it-notes-protect-your-business-secrets-with-a-password-vault-30jj</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffyism5kxz380lpzvlghr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffyism5kxz380lpzvlghr.png" alt="A cluttered desk with post-it note passwords on one side contrasted with a clean workspace and a digital vault on the other" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A password on a post-it note stuck to a monitor is not a security strategy. Neither is a shared spreadsheet, a notebook in a desk drawer, or a Slack message that says “hey, what’s the AWS login again?” Yet these habits persist in companies of every size, from startups to publicly traded firms.&lt;/p&gt;

&lt;p&gt;The reasoning usually sounds practical: “We only have five people,” or “I’ll remember it,” or “We’ve never had a problem.” That last one is survivorship bias wearing a name badge. The problem has not happened &lt;em&gt;yet&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;h1&gt;
  
  
  Open Table of contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/ditch-the-post-it-notes-protect-your-passwords#the-real-cost-of-just-remembering-it" rel="noopener noreferrer"&gt;The real cost of “just remembering it”&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/ditch-the-post-it-notes-protect-your-passwords#ai-has-changed-the-math-on-password-guessing" rel="noopener noreferrer"&gt;AI has changed the math on password guessing&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/ditch-the-post-it-notes-protect-your-passwords#what-a-vault-like-1password-actually-gives-you" rel="noopener noreferrer"&gt;What a vault like 1Password actually gives you&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/ditch-the-post-it-notes-protect-your-passwords#were-too-small-for-this" rel="noopener noreferrer"&gt;“We’re too small for this”&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://devcharms.com/posts/ditch-the-post-it-notes-protect-your-passwords#the-habit-shift" rel="noopener noreferrer"&gt;The habit shift&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The real cost of “just remembering it”
&lt;/h2&gt;

&lt;p&gt;When credentials live in someone’s head—or worse, on a scrap of paper—three things quietly go wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;People reuse passwords.&lt;/strong&gt; If the company WiFi password is also someone’s email password, one breach unzips everything. Verizon’s 2024 Data Breach Investigations Report found that stolen or weak credentials were involved in nearly half of all breaches. That statistic has barely moved in a decade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Onboarding and offboarding become dangerous.&lt;/strong&gt; A new hire asks around for the staging server password. Someone texts it. When that person leaves six months later, nobody rotates the credential. The door stays open and everyone forgets it exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no audit trail.&lt;/strong&gt; If something goes wrong, you cannot answer the most basic question: who had access to what, and when?&lt;/p&gt;

&lt;h2&gt;
  
  
  AI has changed the math on password guessing
&lt;/h2&gt;

&lt;p&gt;Brute-forcing a short, predictable password has always been feasible. What has changed is that large language models and purpose-built cracking tools can now make &lt;em&gt;educated&lt;/em&gt; guesses at scale.&lt;/p&gt;

&lt;p&gt;Researchers at Home Security Heroes demonstrated in 2023 that an AI-powered tool called PassGAN could crack over half of common passwords in under a minute and 71 percent of them in less than a day. It works by learning the patterns humans fall into—capitalizing the first letter, appending a year, substituting &lt;code&gt;@&lt;/code&gt; for &lt;code&gt;a&lt;/code&gt;—and generating candidates that feel random to us but are statistically predictable to a model.&lt;/p&gt;

&lt;p&gt;If your company’s convention is &lt;code&gt;CompanyName2025!&lt;/code&gt; or &lt;code&gt;Summer2026#&lt;/code&gt;, an attacker armed with a fine-tuned model, a list of leaked hashes, and commodity GPUs does not need luck. They need patience measured in hours, not years.&lt;/p&gt;

&lt;p&gt;A password vault solves this at the source. It generates long, truly random strings—&lt;code&gt;x7$qL!9mWv@kR2pZ&lt;/code&gt;—that no human would choose and no model can predict from patterns, because there are no patterns to learn.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a vault like 1Password actually gives you
&lt;/h2&gt;

&lt;p&gt;A password vault is not just a digital notebook with a lock. It is an infrastructure layer. Here is what it provides that you would never build or sustain on your own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secure sharing without exposure
&lt;/h3&gt;

&lt;p&gt;Need to give a contractor access to a staging API key? In 1Password, you share a vault or a single item. The contractor sees the credential inside the app but never copies it into a text file. When the engagement ends, you revoke access in one click. Compare that to the alternative: sending a password over email, hoping they delete it, and wondering six months later if they did.&lt;/p&gt;

&lt;h3&gt;
  
  
  Watchtower: breach detection you would never do manually
&lt;/h3&gt;

&lt;p&gt;1Password’s Watchtower feature cross-references your stored credentials against known breach databases (like Have I Been Pwned) and flags weak, reused, or compromised passwords automatically. No human is going to check 200 service accounts against a breach list every morning. Watchtower does it continuously and silently, surfacing only the items that need attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secrets automation for development teams
&lt;/h3&gt;

&lt;p&gt;For engineering teams, 1Password offers a Secrets Automation platform and a CLI that inject credentials into builds, CI/CD pipelines, and server environments without ever writing them to disk or committing them to a repository. A &lt;code&gt;.env&lt;/code&gt; file checked into Git is an open invitation. A reference to a 1Password secret that is resolved at runtime is not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Travel Mode
&lt;/h3&gt;

&lt;p&gt;Crossing an international border where devices may be inspected? 1Password’s Travel Mode lets you temporarily remove sensitive vaults from a device. Only vaults marked “safe for travel” remain visible. Once you arrive, you toggle Travel Mode off and everything reappears. Try replicating that with a notebook.&lt;/p&gt;

&lt;h3&gt;
  
  
  Passkey and MFA management
&lt;/h3&gt;

&lt;p&gt;Passwords are already giving way to passkeys in many services. 1Password stores and autofills passkeys alongside traditional credentials, so the transition does not mean juggling yet another tool. It also serves as an authenticator for TOTP two-factor codes, consolidating what would otherwise be a mess of separate apps and backup codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  “We’re too small for this”
&lt;/h2&gt;

&lt;p&gt;You are not. A freelancer with a handful of client logins is exactly the profile that suffers most from a single breach—because there is no security team to contain the damage and no PR department to manage the fallout.&lt;/p&gt;

&lt;p&gt;1Password’s pricing starts at a few dollars per user per month. The time you save not resetting forgotten passwords, not onboarding people through DMs, and not lying awake wondering if the intern still has the production database password will pay for itself before the first quarterly review.&lt;/p&gt;

&lt;h2&gt;
  
  
  The habit shift
&lt;/h2&gt;

&lt;p&gt;Moving to a vault is less a technology decision than a habit change. The hardest part is the first week: importing existing passwords, agreeing on a vault structure, and convincing the person who “has a system” that their system is a liability.&lt;/p&gt;

&lt;p&gt;After that, the daily experience is &lt;em&gt;easier&lt;/em&gt; than what it replaces. You stop typing passwords. You stop resetting passwords. You stop wondering if that Google Doc full of credentials is shared with the right people.&lt;/p&gt;

&lt;p&gt;Post-it notes are great for reminding yourself to buy milk. They are not where your business secrets belong.&lt;/p&gt;

</description>
      <category>security</category>
      <category>productivity</category>
      <category>teamwork</category>
    </item>
    <item>
      <title>How CSS cascade layers let you win with lower specificity</title>
      <dc:creator>Greg Miller</dc:creator>
      <pubDate>Mon, 30 Mar 2026 12:00:00 +0000</pubDate>
      <link>https://dev.to/shrinkray/how-css-cascade-layers-let-you-win-with-lower-specificity-3m8p</link>
      <guid>https://dev.to/shrinkray/how-css-cascade-layers-let-you-win-with-lower-specificity-3m8p</guid>
      <description>&lt;p&gt;Specificity is a useful tool until it becomes a &lt;strong&gt;scoreboard&lt;/strong&gt;. You add a class, then a parent selector, then another class, and soon “winning” the cascade means out-gunning yesterday’s selectors instead of expressing intent. &lt;strong&gt;Cascade layers&lt;/strong&gt; (&lt;code&gt;@layer&lt;/code&gt;) change the game: they add a &lt;strong&gt;new step&lt;/strong&gt; in the cascade— &lt;strong&gt;layer order&lt;/strong&gt; —that is evaluated &lt;strong&gt;before&lt;/strong&gt; specificity. That lets you keep selectors shallow and predictable while still controlling what wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;h1&gt;
  
  
  Open Table of contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;What &lt;code&gt;@layer&lt;/code&gt; actually does&lt;/li&gt;
&lt;li&gt;The main benefit: order beats specificity across layers&lt;/li&gt;
&lt;li&gt;Fewer reasons to use &lt;code&gt;!important&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Clear mental model for teams&lt;/li&gt;
&lt;li&gt;How this pairs with modern CSS stacks&lt;/li&gt;
&lt;li&gt;Practical cautions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;@layer&lt;/code&gt; actually does
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;&lt;code&gt;@layer&lt;/code&gt;&lt;/strong&gt; at-rule groups styles into &lt;strong&gt;named layers&lt;/strong&gt;. You declare an order once—often from low-level to high-level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules in &lt;code&gt;utilities&lt;/code&gt; are considered &lt;strong&gt;after&lt;/strong&gt; rules in &lt;code&gt;components&lt;/code&gt; in the cascade, &lt;strong&gt;regardless of selector specificity&lt;/strong&gt; (for normal declarations in author stylesheets). Only after layers are compared does the engine fall back to specificity, then source order.&lt;/p&gt;

&lt;p&gt;So layers are not a replacement for specificity &lt;strong&gt;inside&lt;/strong&gt; a layer; they are a way to &lt;strong&gt;structure conflict&lt;/strong&gt; between &lt;strong&gt;kinds&lt;/strong&gt; of styles (foundation vs. components vs. overrides) without turning every file into a specificity arms race.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main benefit: order beats specificity across layers
&lt;/h2&gt;

&lt;p&gt;Classic pain: a third-party or legacy block uses &lt;code&gt;.sidebar .nav li a.active&lt;/code&gt; and you need a one-off tweak. Without layers, you either match or beat that specificity, reach for &lt;code&gt;!important&lt;/code&gt;, or duplicate structure in your selector.&lt;/p&gt;

&lt;p&gt;With layers, you might put that legacy chunk in &lt;code&gt;@layer legacy&lt;/code&gt; and your app overrides in &lt;code&gt;@layer app&lt;/code&gt;. A single class in &lt;code&gt;app&lt;/code&gt; can override a long chain in &lt;code&gt;legacy&lt;/code&gt; because &lt;strong&gt;the layer wins first&lt;/strong&gt;. You are not “reducing specificity” in the mathematical sense for rules in the &lt;strong&gt;same&lt;/strong&gt; layer—you are &lt;strong&gt;making specificity less often the deciding factor&lt;/strong&gt; between &lt;strong&gt;layers&lt;/strong&gt; you own.&lt;/p&gt;

&lt;p&gt;That is the practical payoff: &lt;strong&gt;simpler selectors&lt;/strong&gt; for the code you write every day, because &lt;strong&gt;where&lt;/strong&gt; it lives in the stack is explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fewer reasons to use &lt;code&gt;!important&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;!important&lt;/code&gt; often appears when specificity and source order leave no clean escape hatch. Layers give you a &lt;strong&gt;structured&lt;/strong&gt; escalation path: add a higher layer (for example &lt;code&gt;utilities&lt;/code&gt; or &lt;code&gt;overrides&lt;/code&gt;) instead of marking every declaration important.&lt;/p&gt;

&lt;p&gt;You still should not create dozens of micro-layers without reason— &lt;strong&gt;broad, stable layers&lt;/strong&gt; (reset → base → components → utilities) are easier to reason about than a layer per file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clear mental model for teams
&lt;/h2&gt;

&lt;p&gt;Layers encode &lt;strong&gt;intent&lt;/strong&gt; in the stylesheet architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;reset&lt;/code&gt; / &lt;code&gt;normalize&lt;/code&gt;&lt;/strong&gt; — browser quirks, low precedence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;base&lt;/code&gt;&lt;/strong&gt; — element defaults and typography.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;components&lt;/code&gt;&lt;/strong&gt; — BEM-style blocks, cards, nav patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;utilities&lt;/code&gt;&lt;/strong&gt; — small, single-purpose helpers (spacing, visibility).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;New contributors can read &lt;code&gt;@layer&lt;/code&gt; declarations like a &lt;strong&gt;table of contents&lt;/strong&gt; for the cascade. That reduces “why did this style lose?” debugging sessions that end in a 0–4–2 specificity lecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this pairs with modern CSS stacks
&lt;/h2&gt;

&lt;p&gt;Frameworks and build tools increasingly respect layers (for example, importing Tailwind with layered preflight and utilities). Whether you author layers by hand or adopt a layered preset, the underlying idea is the same: &lt;strong&gt;separate concerns by layer&lt;/strong&gt; , keep selectors inside each concern as simple as the design allows, and let &lt;strong&gt;layer order&lt;/strong&gt; carry cross-cutting priority.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical cautions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unlayered&lt;/strong&gt; author styles still sit &lt;strong&gt;above&lt;/strong&gt; layered styles in the cascade for normal rules—so stray global CSS outside &lt;code&gt;@layer&lt;/code&gt; can still surprise you. Prefer moving everything into layers once you adopt them, or be deliberate about what stays unlayered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same-layer&lt;/strong&gt; conflicts still use specificity and source order; layers do not remove the need for sensible selectors inside a layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoped styles&lt;/strong&gt; (for example in Shadow DOM) follow their own encapsulation rules; layers apply within the same origin and tree as usual, but component boundaries still matter for what actually applies.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Cascade layers are not magic—they are &lt;strong&gt;structure&lt;/strong&gt;. If your project fights the cascade with ever-longer selectors, &lt;code&gt;@layer&lt;/code&gt; gives you a way to &lt;strong&gt;lower the temperature&lt;/strong&gt; : define a small number of layers, put resets and utilities in the right slots, and let &lt;strong&gt;order&lt;/strong&gt; do what you used to do with specificity gymnastics.&lt;/p&gt;

</description>
      <category>css</category>
      <category>cascade</category>
      <category>webstandards</category>
    </item>
  </channel>
</rss>
