<?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: Deb</title>
    <description>The latest articles on DEV Community by Deb (@medghosh).</description>
    <link>https://dev.to/medghosh</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%2F3859722%2Fe1fb3dd3-92c4-4413-96d4-2ed08210533c.png</url>
      <title>DEV Community: Deb</title>
      <link>https://dev.to/medghosh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/medghosh"/>
    <language>en</language>
    <item>
      <title>Replaying dead-letter messages across Azure, AWS and GCP, and saying "verification required" when it can't be proven</title>
      <dc:creator>Deb</dc:creator>
      <pubDate>Wed, 14 Oct 2026 18:38:17 +0000</pubDate>
      <link>https://dev.to/medghosh/replaying-dead-letter-messages-across-azure-aws-and-gcp-and-saying-verification-required-when-39gj</link>
      <guid>https://dev.to/medghosh/replaying-dead-letter-messages-across-azure-aws-and-gcp-and-saying-verification-required-when-39gj</guid>
      <description>&lt;p&gt;It is 2am. A queue backs up and messages start landing in the dead-letter queue. Everyone's instinct is the same: replay them.&lt;/p&gt;

&lt;p&gt;And then the questions start, the ones you usually can't answer quickly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why did these fail?&lt;/strong&gt; One error, or five different ones mixed together?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the cause actually fixed?&lt;/strong&gt; If not, you are about to send the same messages straight back into the same failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who replayed what, and when?&lt;/strong&gt; Next week nobody will remember.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did the replay work?&lt;/strong&gt; Or are the messages quietly sitting in the dead-letter queue again?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hit these on every cloud I work with, so I built &lt;strong&gt;ServiceHub&lt;/strong&gt;: a self-hosted, MIT-licensed tool for &lt;strong&gt;Azure Service Bus, AWS SQS/SNS and GCP Pub/Sub&lt;/strong&gt;. This post is the pain first, then what ServiceHub does about each part of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it in your browser, nothing to install:&lt;/strong&gt; &lt;a href="https://debdevops.github.io/servicehub/" rel="noopener noreferrer"&gt;https://debdevops.github.io/servicehub/&lt;/a&gt; (made-up data, no server, connects to nothing).&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%2F81xfrghb7s9wey9idm6m.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%2F81xfrghb7s9wey9idm6m.png" alt="All three clouds on one Home page: Azure, AWS and Google Cloud, each with its own dead-letter count" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The pain is different on each cloud
&lt;/h2&gt;

&lt;p&gt;Replaying sounds simple until you meet the details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Azure Service Bus:&lt;/strong&gt; every queue and subscription has its own dead-letter queue, so a bad hour is spread across many places.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS SQS:&lt;/strong&gt; there is no non-destructive peek. Every read is a receive, and it counts toward the queue's &lt;code&gt;maxReceiveCount&lt;/code&gt;. Looking at a dead letter is not free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GCP Pub/Sub:&lt;/strong&gt; reading a dead letter counts as a delivery attempt too, and Pub/Sub records no failure reason, so you see the message but not why it failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three clouds, three behaviours, and the same 2am question: can I safely put these back?&lt;/p&gt;

&lt;h2&gt;
  
  
  Pain 1: "I can't see what failed, or why"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What ServiceHub does:&lt;/strong&gt; one &lt;strong&gt;Dead letters&lt;/strong&gt; page per cloud, with failures grouped by reason, so five different problems no longer look like one big pile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure&lt;/strong&gt; is watched automatically:&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%2Ff9vubdxpz2omejqzqojv.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%2Ff9vubdxpz2omejqzqojv.png" alt="Azure dead letters, grouped by failure reason" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS&lt;/strong&gt; and &lt;strong&gt;Google Cloud&lt;/strong&gt; only know what you ask them to look at, because looking has a cost. ServiceHub never reads their dead letters on its own. You press &lt;strong&gt;Look now&lt;/strong&gt;; it reads up to 100 per queue and keeps them, so you can open, read and replay them.&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%2Feivnlun3x21rcxzx67ue.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%2Feivnlun3x21rcxzx67ue.png" alt="AWS dead letters after pressing Look now" width="800" height="450"&gt;&lt;/a&gt;&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%2F6guw5rz9x179c55o85x0.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%2F6guw5rz9x179c55o85x0.png" alt="Google Cloud dead letters. Pub/Sub records no failure reason, and the page says so" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where a cloud gives no reason, the page says "no reason recorded" instead of guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pain 2: "Replaying is a leap of faith"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What ServiceHub does:&lt;/strong&gt; every replay, whether a person clicks it or the Agent does it, goes through &lt;strong&gt;one gate&lt;/strong&gt;. Before anything is sent it shows a proposal: scope, a sample, the risk, the policy and the stop condition. If a check can't run, the replay is &lt;strong&gt;blocked&lt;/strong&gt;, not waved through.&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%2Fdgcgg39ip4abe2ocdjn2.gif" 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%2Fdgcgg39ip4abe2ocdjn2.gif" alt="Home to a single replay: the same flow on Azure" width="760" height="428"&gt;&lt;/a&gt;&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%2F1h0z96fnh33biixwn4ie.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%2F1h0z96fnh33biixwn4ie.png" alt="AWS: the preview of replaying one message, before anything is sent" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Replaying everything is previewed too, grouped by failure reason, with a pace and a &lt;strong&gt;Stop now&lt;/strong&gt;:&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%2F7ljo1tqi8u3p41480kqx.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%2F7ljo1tqi8u3p41480kqx.png" alt="Google Cloud: Replay All preview. Nothing has run yet" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pain 3: "I can't tell whether the replay worked"
&lt;/h2&gt;

&lt;p&gt;This is the part I care about most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure can confirm&lt;/strong&gt; whether a replayed message stayed out of the dead-letter queue. &lt;strong&gt;AWS and GCP can't&lt;/strong&gt;, without extra infrastructure. So on those clouds a replay reads &lt;strong&gt;"verification required"&lt;/strong&gt;, never "verified". A tool that says "fixed" when it doesn't know is worse than no tool.&lt;/p&gt;

&lt;p&gt;Messages that fail again are labelled for what they are. Here is Azure's Replayed page in my test setup, where the sample messages fail on purpose:&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%2Fo1g26jke918qwed9eqdm.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%2Fo1g26jke918qwed9eqdm.png" alt="Azure Replayed page: of the replays that could be checked, 33 came back and needed a look because they failed the same way again" width="800" height="450"&gt;&lt;/a&gt;&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%2F2hubs4t526lqw5n00mud.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%2F2hubs4t526lqw5n00mud.png" alt="Google Cloud Replayed page: the same layout, with the result of each replay (came back, or still being watched)" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4.2.0 adds an opt-in, per-namespace setting, &lt;strong&gt;Confirm fixes on this cloud&lt;/strong&gt;. If ServiceHub gets a complete view of the cloud's dead-letter queue, a replay can read &lt;em&gt;recovered&lt;/em&gt; or &lt;em&gt;came back&lt;/em&gt;. A view either saw everything or the replay stays "verification required". There is no confidence score.&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%2Fl6r00myr0opddb7837tu.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%2Fl6r00myr0opddb7837tu.png" alt="Settings, Connections: Confirm fixes on this cloud, per namespace" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pain 4: "Automation that replays on its own can loop"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What ServiceHub does:&lt;/strong&gt; the Agent replays on its own only for a &lt;strong&gt;failure signature&lt;/strong&gt; it has earned trust on, per signature, not globally. Until then the messages wait for a person. It has brakes: a circuit breaker, an emergency stop (type &lt;code&gt;STOP&lt;/code&gt;, give a reason, both recorded), and one place to approve or decline waiting approvals. And in 4.2.0, Auto Replay backs off by itself if 2 of a failure's last 10 verified replays didn't hold.&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%2Fytzhv2ja9bhha4w6lazi.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%2Fytzhv2ja9bhha4w6lazi.png" alt="AWS Auto Replay rules: each rule is per failure signature, and messages wait for a person until trust is earned" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pain 5: "Nobody can say afterwards what happened"
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What ServiceHub does:&lt;/strong&gt; every recovery is written to a hash-chained ledger you can read in Advanced.&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%2Faoi4f0ltxyjpiweeycxs.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%2Faoi4f0ltxyjpiweeycxs.png" alt="Advanced, Recovery Ledger" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it yourself
&lt;/h2&gt;

&lt;p&gt;Pick whichever is easiest. All three are free and local.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. In the browser (no install)
&lt;/h3&gt;

&lt;p&gt;Open &lt;a href="https://debdevops.github.io/servicehub/" rel="noopener noreferrer"&gt;https://debdevops.github.io/servicehub/&lt;/a&gt; and press &lt;strong&gt;Demo&lt;/strong&gt;. Six namespaces across three clouds, about 750 dead letters. Replay, approve, switch on an Auto Replay rule, press Emergency stop. Each action says "Demo - nothing was sent".&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Clone and run from source
&lt;/h3&gt;

&lt;p&gt;For macOS, Linux, or a WSL 2 terminal on Windows. You only need &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt; (or &lt;code&gt;wget&lt;/code&gt;), &lt;code&gt;tar&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt; and an internet connection. &lt;code&gt;./run.sh&lt;/code&gt; downloads the .NET 10 SDK and Node.js for you if they are missing (into &lt;code&gt;~/.servicehub/tools&lt;/code&gt;, no &lt;code&gt;sudo&lt;/code&gt;, checksum-verified).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/debdevops/servicehub.git
&lt;span class="nb"&gt;cd &lt;/span&gt;servicehub
./run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first start takes a few minutes while it downloads and builds. When you see &lt;strong&gt;ServiceHub is ready&lt;/strong&gt;, open &lt;strong&gt;&lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/strong&gt; and press &lt;strong&gt;Demo&lt;/strong&gt;. Press &lt;strong&gt;Ctrl-C&lt;/strong&gt; to stop everything it started. If port 3000 or 5153 is busy it picks the next free one and tells you.&lt;/p&gt;

&lt;p&gt;This mode is for trying and developing. If you already have .NET 10 and Node 22 and want nothing downloaded, use &lt;code&gt;./run.sh --no-install&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Docker
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SERVICEHUB_ENCRYPTION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="c"&gt;# keep it: it protects the cloud credentials you store&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; servicehub &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:8080:8080 &lt;span class="nt"&gt;-v&lt;/span&gt; servicehub-data:/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;SECURITY__ENCRYPTIONKEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICEHUB_ENCRYPTION_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  ghcr.io/debdevops/servicehub:4.2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;strong&gt;&lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;&lt;/strong&gt; and press &lt;strong&gt;Demo&lt;/strong&gt;. The image is public and runs on &lt;code&gt;linux/amd64&lt;/code&gt; and &lt;code&gt;linux/arm64&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then connect a real cloud
&lt;/h3&gt;

&lt;p&gt;Open &lt;strong&gt;Connections&lt;/strong&gt; and add a cloud: an Azure Service Bus connection string, an AWS access key for one account and region, or a Google project and service account. Credentials are encrypted and stay on your machine. Azure needs a Manage-level policy to list queues and count messages (make a dedicated policy); replay uses Send.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else is new in 4.2.0
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A 13-stop guided tour&lt;/strong&gt; that drives the real screens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interrupted replays are no longer sent twice.&lt;/strong&gt; They become &lt;em&gt;outcome unknown&lt;/em&gt;, block further attempts on that message, and an Approver closes them by recording what they found.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failures are told apart by error message&lt;/strong&gt;, so two different failures on one queue no longer share trust.&lt;/li&gt;
&lt;li&gt;A read-only &lt;strong&gt;MCP server&lt;/strong&gt; (ten fixed GET addresses; no tool that replays, purges or approves).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also found and fixed a real bug against live SQS: the whole-queue check had never worked on a real queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits, stated plainly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Replay on Production namespaces is &lt;strong&gt;blocked for everyone&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;No sign-in: keep it on localhost, a private network, or behind an authenticating proxy.&lt;/li&gt;
&lt;li&gt;It stores the first 500 characters of each message body in a local SQLite file in plain text. Protect the data folder.&lt;/li&gt;
&lt;li&gt;4.1.0 and later is a rewrite with &lt;strong&gt;no upgrade path from 4.0.0&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Azure, AWS and GCP were exercised live on 2026-10-14 for the screenshots above. Not observed live: a cured replay on GCP, and 1,000+ message queues. The &lt;code&gt;infra/&lt;/code&gt; Terraform modules have not been run against a real account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repo and release notes: &lt;a href="https://github.com/debdevops/servicehub" rel="noopener noreferrer"&gt;https://github.com/debdevops/servicehub&lt;/a&gt;. If something confused you or broke on first run, open an issue. I want to hear it.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>aws</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I built a forensic debugger for Azure Service Bus dead-letter queues — here's how it works</title>
      <dc:creator>Deb</dc:creator>
      <pubDate>Fri, 01 May 2026 08:33:50 +0000</pubDate>
      <link>https://dev.to/medghosh/i-built-a-forensic-debugger-for-azure-service-bus-dead-letter-queues-heres-how-it-works-3lip</link>
      <guid>https://dev.to/medghosh/i-built-a-forensic-debugger-for-azure-service-bus-dead-letter-queues-heres-how-it-works-3lip</guid>
      <description>&lt;p&gt;It's 2 AM. Your monitoring fires. &lt;strong&gt;5,000 messages&lt;/strong&gt; in the Dead-Letter Queue.&lt;br&gt;
You need to know three things immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's actually inside those messages&lt;/li&gt;
&lt;li&gt;Which failure pattern caused this&lt;/li&gt;
&lt;li&gt;Which messages are safe to replay right now&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That investigation used to take me 6 hours. It now takes 45 minutes. Not because I got faster — because I built the right tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ServiceHub&lt;/strong&gt; is an open-source, self-hosted web application that treats dead-letter queue debugging as the forensic investigation it actually is.&lt;/p&gt;

&lt;p&gt;Here's what it does that a standard management console cannot:&lt;br&gt;
&lt;strong&gt;Full message body inspection&lt;/strong&gt; — click any message, active or dead-letter, and see the complete JSON with syntax highlighting, every system property, and the exact broker error description.&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.amazonaws.com%2Fuploads%2Farticles%2F5b8rb9qn8i5mhiec4qgo.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.amazonaws.com%2Fuploads%2Farticles%2F5b8rb9qn8i5mhiec4qgo.png" alt=" " width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client-side AI pattern detection&lt;/strong&gt; — clusters your DLQ messages into error groups with confidence scores. Runs entirely in your browser. Zero data exfiltration. No API key needed.&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.amazonaws.com%2Fuploads%2Farticles%2Fc2unxrqsytskpu53wouk.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.amazonaws.com%2Fuploads%2Farticles%2Fc2unxrqsytskpu53wouk.png" alt=" " width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-replay rules&lt;/strong&gt; — define conditions, set rate limits, add exponential backoff. The engine runs autonomously and shows live Pending → Replayed → Success counters.&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.amazonaws.com%2Fuploads%2Farticles%2Fmoms3687co3ejnu6shfk.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.amazonaws.com%2Fuploads%2Farticles%2Fmoms3687co3ejnu6shfk.png" alt=" " width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;30-day &lt;strong&gt;DLQ Intelligence&lt;/strong&gt; — persists every scan to local SQLite. Know whether tonight's spike is a new incident or the same pattern from last Tuesday.&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.amazonaws.com%2Fuploads%2Farticles%2F9b0zat7fwmhnhva59et8.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.amazonaws.com%2Fuploads%2Farticles%2F9b0zat7fwmhnhva59et8.png" alt=" " width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Correlation Explorer&lt;/strong&gt; — paste any Correlation ID and trace that message across every queue, topic, and namespace in one search.&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.amazonaws.com%2Fuploads%2Farticles%2Fzysmkuuy9d6osre34vip.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.amazonaws.com%2Fuploads%2Farticles%2Fzysmkuuy9d6osre34vip.png" alt=" " width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The entire thing is read-only by design. Uses PeekMessagesAsync — your consumers are never affected. Works with Listen permission only.&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
git clone &lt;a href="https://github.com/debdevops/servicehub.git" rel="noopener noreferrer"&gt;https://github.com/debdevops/servicehub.git&lt;/a&gt;&lt;br&gt;
cd servicehub &amp;amp;&amp;amp; ./run.sh&lt;/p&gt;

&lt;p&gt;One command. Auto-installs .NET 10 and Node.js. &lt;/p&gt;

&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Or try the hosted demo in 30 seconds — no install:&lt;br&gt;
👉 &lt;a href="https://app-servicehub-prod.azurewebsites.net/" rel="noopener noreferrer"&gt;https://app-servicehub-prod.azurewebsites.net/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The hosted version authenticate via Microsoft Entra ID (Azure AD). No user database. No personal data stored. Connection strings AES-GCM encrypted at rest in your session. &lt;/p&gt;

&lt;p&gt;What does your current DLQ investigation workflow look like? Drop it in the comments — I'm genuinely curious what the most painful part still is.&lt;/p&gt;

&lt;p&gt;⭐ GitHub: &lt;a href="https://github.com/debdevops/servicehub" rel="noopener noreferrer"&gt;https://github.com/debdevops/servicehub&lt;/a&gt; — MIT license, PRs welcome.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>azure</category>
      <category>dotnet</category>
      <category>devops</category>
    </item>
    <item>
      <title>I Built a Free Web Tool to Debug Azure Service Bus DLQs Without the 2 AM Headache</title>
      <dc:creator>Deb</dc:creator>
      <pubDate>Fri, 03 Apr 2026 15:58:26 +0000</pubDate>
      <link>https://dev.to/medghosh/i-built-a-free-web-tool-to-debug-azure-service-bus-dlqs-without-the-2-am-headache-1meb</link>
      <guid>https://dev.to/medghosh/i-built-a-free-web-tool-to-debug-azure-service-bus-dlqs-without-the-2-am-headache-1meb</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Free Web Tool to Debug Azure Service Bus DLQs Without the 2 AM Headache
&lt;/h1&gt;

&lt;p&gt;It’s 2 AM. Your pager goes off.&lt;/p&gt;

&lt;p&gt;Azure Service Bus is showing thousands of dead-letter messages, but the Azure Portal only tells you a number. No message content. No failure reasons. No easy way to understand what went wrong or how to safely fix it.&lt;/p&gt;

&lt;p&gt;After experiencing this pain too many times, I decided to build &lt;strong&gt;ServiceHub&lt;/strong&gt; — an open-source web-based forensic debugger for Azure Service Bus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try It Live — No Installation Needed
&lt;/h3&gt;

&lt;p&gt;I’ve deployed a &lt;strong&gt;free hosted demo&lt;/strong&gt; so you can test it instantly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://app-servicehub-prod.azurewebsites.net/connect" rel="noopener noreferrer"&gt;→ Try ServiceHub Live Demo&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just paste your Service Bus connection string and click Connect.&lt;br&gt;&lt;br&gt;
It works safely with just &lt;strong&gt;Listen&lt;/strong&gt; permission (read-only by default).&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.amazonaws.com%2Fuploads%2Farticles%2F0mw6jzcadbsnxm5agnks.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.amazonaws.com%2Fuploads%2Farticles%2F0mw6jzcadbsnxm5agnks.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What ServiceHub Gives You
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full Message Visibility&lt;/strong&gt; — Browse active queues, topics, and subscriptions with complete JSON body, system properties, custom headers, and syntax highlighting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Powerful DLQ Intelligence&lt;/strong&gt; — Persistent history, auto-categorization of failures (Transient, MaxDelivery, Expired, etc.), forensic timeline, trend charts, and export to JSON/CSV.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-Replay Rules&lt;/strong&gt; — Define smart rules once and automatically replay matching messages with live success statistics and rate limiting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe Batch Replay&lt;/strong&gt; — Replay multiple messages safely with strong confirmation guards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-Powered Insights&lt;/strong&gt; — Client-side pattern detection and anomaly flagging (no data leaves your browser).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing Tools&lt;/strong&gt; — Built-in Message Generator with realistic scenarios and anomaly injection.&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%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejtaj2x6vx97krcpmif9.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.amazonaws.com%2Fuploads%2Farticles%2Fejtaj2x6vx97krcpmif9.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F5lws8fuije7rvdkb8db1.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.amazonaws.com%2Fuploads%2Farticles%2F5lws8fuije7rvdkb8db1.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fbeh3h7kh03h1ulltiwz9.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.amazonaws.com%2Fuploads%2Farticles%2Fbeh3h7kh03h1ulltiwz9.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Femlpq4c3my4maydjudk2.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.amazonaws.com%2Fuploads%2Farticles%2Femlpq4c3my4maydjudk2.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of this in a clean, modern web interface built with React 18 and .NET 10.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run It Locally (Completely Free &amp;amp; Open Source)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
git clone https://github.com/debdevops/servicehub.git
cd servicehub
./run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>opensource</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
