From 99accbffec0c0619fde6d8450e0da2a657c42c56 Mon Sep 17 00:00:00 2001
From: Debasis Ghosh <65386308+debdevops@users.noreply.github.com>
Date: Thu, 26 Mar 2026 14:10:50 +0530
Subject: [PATCH] Add new screenshots and update documentation
- Added multiple new screenshots to the archive for various features and UI elements.
- Created package.json and package-lock.json for Playwright dependency.
- Removed outdated DOCUMENTATION_INDEX.md file.
- Updated README.md to reflect changes in documentation structure and removed references to the deleted index.
---
README.md | 776 ++++++------------
REMOTE_ACCESS.md | 174 ----
docs/PERMISSIONS.md | 86 --
docs/SCREENSHOTS.md | 331 --------
docs/screenshots/01-connect-page.png | Bin 0 -> 504732 bytes
docs/screenshots/02-messages-queue-active.png | Bin 0 -> 293348 bytes
.../03-message-detail-properties.png | Bin 0 -> 477582 bytes
docs/screenshots/04-message-detail-body.png | Bin 0 -> 326990 bytes
.../05-message-detail-ai-insights.png | Bin 0 -> 334905 bytes
.../06-message-detail-forensic.png | Bin 0 -> 341284 bytes
.../screenshots/07-message-detail-headers.png | Bin 0 -> 318103 bytes
.../08-messages-deadletter-queue.png | Bin 0 -> 275027 bytes
docs/screenshots/09-dlq-message-detail.png | Bin 0 -> 315727 bytes
.../10-dlq-message-ai-insights.png | Bin 0 -> 391656 bytes
.../11-messages-topic-subscription.png | Bin 0 -> 220101 bytes
.../screenshots/12-fab-quick-actions-open.png | Bin 0 -> 355164 bytes
docs/screenshots/13-send-message-dialog.png | Bin 0 -> 315376 bytes
.../14-generate-messages-dialog.png | Bin 0 -> 428661 bytes
docs/screenshots/15-test-dlq-dialog.png | Bin 0 -> 292931 bytes
docs/screenshots/16-dlq-history-overview.png | Bin 0 -> 362132 bytes
docs/screenshots/17-dlq-history-detail.png | Bin 0 -> 493556 bytes
docs/screenshots/18-dlq-history-full.png | Bin 0 -> 493557 bytes
docs/screenshots/19-rules-page.png | Bin 0 -> 336394 bytes
.../screenshots/20-rules-template-gallery.png | Bin 0 -> 395322 bytes
docs/screenshots/21-rules-create-dialog.png | Bin 0 -> 344546 bytes
docs/screenshots/22-health-page.png | Bin 0 -> 348321 bytes
docs/screenshots/23-help-page.png | Bin 0 -> 367914 bytes
docs/screenshots/24-help-page-full.png | Bin 0 -> 367914 bytes
docs/screenshots/25-sidebar-navigation.png | Bin 0 -> 71351 bytes
docs/screenshots/26-scalar-api-docs.png | Bin 0 -> 191520 bytes
docs/screenshots/27-full-app-overview.png | Bin 0 -> 326685 bytes
docs/screenshots/28-message-filter.png | Bin 0 -> 329842 bytes
.../{ => archive}/01-Start-The-App.png | Bin
.../archive/01-connect-page-full.png | Bin 0 -> 425273 bytes
docs/screenshots/archive/01-connect-page.png | Bin 0 -> 488655 bytes
...onnect-Service-Bus-With-Manage-ConnStr.png | Bin
.../02-connect-page-namespace-connected.png | Bin 0 -> 385763 bytes
.../archive/02-messages-overview.png | Bin 0 -> 149306 bytes
.../{ => archive}/03-Connected-ServiceBus.png | Bin
.../archive/03-messages-page-overview.png | Bin 0 -> 206264 bytes
.../archive/03-messages-queue-browser.png | Bin 0 -> 292598 bytes
.../04-feature-message-browser-empty.png | Bin
.../archive/04-message-detail-properties.png | Bin 0 -> 476268 bytes
.../archive/04-messages-queue-browser.png | Bin 0 -> 165227 bytes
.../05-main-message-display1.png | Bin
.../archive/05-message-detail-body.png | Bin 0 -> 325952 bytes
.../06-main-message-display2.png | Bin
.../archive/06-message-detail-ai-insights.png | Bin 0 -> 345566 bytes
...message-generator-basic-single-message.png | Bin
.../archive/07-message-detail-forensic.png | Bin 0 -> 339187 bytes
...08-feature-message-generator-scenarios.png | Bin
.../archive/08-message-detail-headers.png | Bin 0 -> 318213 bytes
.../archive/09-dead-letter-queue.png | Bin 0 -> 204973 bytes
...9-feature-message-generator-scenarios1.png | Bin
.../archive/10-dead-letter-queue.png | Bin 0 -> 163885 bytes
.../archive/10-dlq-message-detail-replay.png | Bin 0 -> 308029 bytes
.../{ => archive}/10-message-display.png | Bin
.../11-Generate-Single-Message-Topic.png | Bin
.../11-messages-topic-subscription.png | Bin 0 -> 160850 bytes
.../11-topic-subscription-messages.png | Bin 0 -> 197332 bytes
.../archive/12-fab-quick-actions-menu.png | Bin 0 -> 354072 bytes
.../archive/12-fab-quick-actions-open.png | Bin 0 -> 163896 bytes
.../12-showing-message-topic.png | Bin
.../archive/13-fab-send-message-dialog.png | Bin 0 -> 160257 bytes
...3-feature-message-details-custom-props.png | Bin
.../archive/13-send-message-dialog.png | Bin 0 -> 311197 bytes
.../14-fab-generate-messages-dialog.png | Bin 0 -> 160850 bytes
...-feature-message-details-custom-props1.png | Bin
.../archive/14-generate-messages-dialog.png | Bin 0 -> 422170 bytes
.../archive/15-dlq-history-page.png | Bin 0 -> 285113 bytes
.../archive/15-fab-test-dlq-dialog.png | Bin 0 -> 160850 bytes
.../15-feature-message-details-JSON-prop.png | Bin
.../archive/15-test-dlq-dialog.png | Bin 0 -> 421473 bytes
.../archive/16-dlq-history-detail.png | Bin 0 -> 286137 bytes
.../archive/16-dlq-history-page.png | Bin 0 -> 276721 bytes
.../16-feature-message-details-AI-Insight.png | Bin
.../archive/17-dlq-history-detail.png | Bin 0 -> 456895 bytes
...17-feature-message-details-AI-Insight1.png | Bin
docs/screenshots/archive/17-rules-page.png | Bin 0 -> 302034 bytes
...18-feature-message-details-AI-Insight2.png | Bin
docs/screenshots/archive/18-rules-page.png | Bin 0 -> 327698 bytes
.../archive/18-rules-template-gallery.png | Bin 0 -> 301087 bytes
.../19-feature-ai-findings-1.png | Bin
docs/screenshots/archive/19-health-page.png | Bin 0 -> 105686 bytes
.../archive/19-rules-template-gallery.png | Bin 0 -> 393986 bytes
.../20-feature-ai-findings-2.png | Bin
docs/screenshots/archive/20-health-page.png | Bin 0 -> 264646 bytes
docs/screenshots/archive/20-help-page.png | Bin 0 -> 307129 bytes
.../screenshots/archive/21-help-page-full.png | Bin 0 -> 368087 bytes
docs/screenshots/archive/21-help-page.png | Bin 0 -> 368087 bytes
.../21-workflow-dlq-investigation-step1.png | Bin
.../archive/22-scalar-api-docs.png | Bin 0 -> 190795 bytes
.../22-workflow-dlq-investigation-step2.png | Bin
.../23-workflow-dlq-AI-Insight.png | Bin
.../24-workflow-dlq-replay-step4.png | Bin
.../{ => archive}/25-feature-find-feature.png | Bin
.../archive/25-scalar-api-docs.png | Bin 0 -> 190795 bytes
.../{ => archive}/26-row-ui-new-feature.png | Bin
.../{ => archive}/27-dlq-enhancement.png | Bin
.../{ => archive}/28-dlq-intelligence.png | Bin
.../29-dlq-history-post-replay-message.png | Bin
.../{ => archive}/30-auto-replay-feature.png | Bin
.../31-auto-relay-test-feature.png | Bin
.../{ => archive}/32-replay-all-messages.png | Bin
.../{ => archive}/33-replay-all-process.png | Bin
.../34-post-replay-all-messages.png | Bin
...q-intelligence-history-post-replay-all.png | Bin
docs/screenshots/servicehub-demo.gif | Bin 0 -> 1483901 bytes
package-lock.json | 59 ++
package.json | 5 +
services/api/DOCUMENTATION_INDEX.md | 450 ----------
services/api/README.md | 7 +-
112 files changed, 330 insertions(+), 1558 deletions(-)
delete mode 100644 REMOTE_ACCESS.md
delete mode 100644 docs/PERMISSIONS.md
delete mode 100644 docs/SCREENSHOTS.md
create mode 100644 docs/screenshots/01-connect-page.png
create mode 100644 docs/screenshots/02-messages-queue-active.png
create mode 100644 docs/screenshots/03-message-detail-properties.png
create mode 100644 docs/screenshots/04-message-detail-body.png
create mode 100644 docs/screenshots/05-message-detail-ai-insights.png
create mode 100644 docs/screenshots/06-message-detail-forensic.png
create mode 100644 docs/screenshots/07-message-detail-headers.png
create mode 100644 docs/screenshots/08-messages-deadletter-queue.png
create mode 100644 docs/screenshots/09-dlq-message-detail.png
create mode 100644 docs/screenshots/10-dlq-message-ai-insights.png
create mode 100644 docs/screenshots/11-messages-topic-subscription.png
create mode 100644 docs/screenshots/12-fab-quick-actions-open.png
create mode 100644 docs/screenshots/13-send-message-dialog.png
create mode 100644 docs/screenshots/14-generate-messages-dialog.png
create mode 100644 docs/screenshots/15-test-dlq-dialog.png
create mode 100644 docs/screenshots/16-dlq-history-overview.png
create mode 100644 docs/screenshots/17-dlq-history-detail.png
create mode 100644 docs/screenshots/18-dlq-history-full.png
create mode 100644 docs/screenshots/19-rules-page.png
create mode 100644 docs/screenshots/20-rules-template-gallery.png
create mode 100644 docs/screenshots/21-rules-create-dialog.png
create mode 100644 docs/screenshots/22-health-page.png
create mode 100644 docs/screenshots/23-help-page.png
create mode 100644 docs/screenshots/24-help-page-full.png
create mode 100644 docs/screenshots/25-sidebar-navigation.png
create mode 100644 docs/screenshots/26-scalar-api-docs.png
create mode 100644 docs/screenshots/27-full-app-overview.png
create mode 100644 docs/screenshots/28-message-filter.png
rename docs/screenshots/{ => archive}/01-Start-The-App.png (100%)
create mode 100644 docs/screenshots/archive/01-connect-page-full.png
create mode 100644 docs/screenshots/archive/01-connect-page.png
rename docs/screenshots/{ => archive}/02-Connect-Service-Bus-With-Manage-ConnStr.png (100%)
create mode 100644 docs/screenshots/archive/02-connect-page-namespace-connected.png
create mode 100644 docs/screenshots/archive/02-messages-overview.png
rename docs/screenshots/{ => archive}/03-Connected-ServiceBus.png (100%)
create mode 100644 docs/screenshots/archive/03-messages-page-overview.png
create mode 100644 docs/screenshots/archive/03-messages-queue-browser.png
rename docs/screenshots/{ => archive}/04-feature-message-browser-empty.png (100%)
create mode 100644 docs/screenshots/archive/04-message-detail-properties.png
create mode 100644 docs/screenshots/archive/04-messages-queue-browser.png
rename docs/screenshots/{ => archive}/05-main-message-display1.png (100%)
create mode 100644 docs/screenshots/archive/05-message-detail-body.png
rename docs/screenshots/{ => archive}/06-main-message-display2.png (100%)
create mode 100644 docs/screenshots/archive/06-message-detail-ai-insights.png
rename docs/screenshots/{ => archive}/07-feature-message-generator-basic-single-message.png (100%)
create mode 100644 docs/screenshots/archive/07-message-detail-forensic.png
rename docs/screenshots/{ => archive}/08-feature-message-generator-scenarios.png (100%)
create mode 100644 docs/screenshots/archive/08-message-detail-headers.png
create mode 100644 docs/screenshots/archive/09-dead-letter-queue.png
rename docs/screenshots/{ => archive}/09-feature-message-generator-scenarios1.png (100%)
create mode 100644 docs/screenshots/archive/10-dead-letter-queue.png
create mode 100644 docs/screenshots/archive/10-dlq-message-detail-replay.png
rename docs/screenshots/{ => archive}/10-message-display.png (100%)
rename docs/screenshots/{ => archive}/11-Generate-Single-Message-Topic.png (100%)
create mode 100644 docs/screenshots/archive/11-messages-topic-subscription.png
create mode 100644 docs/screenshots/archive/11-topic-subscription-messages.png
create mode 100644 docs/screenshots/archive/12-fab-quick-actions-menu.png
create mode 100644 docs/screenshots/archive/12-fab-quick-actions-open.png
rename docs/screenshots/{ => archive}/12-showing-message-topic.png (100%)
create mode 100644 docs/screenshots/archive/13-fab-send-message-dialog.png
rename docs/screenshots/{ => archive}/13-feature-message-details-custom-props.png (100%)
create mode 100644 docs/screenshots/archive/13-send-message-dialog.png
create mode 100644 docs/screenshots/archive/14-fab-generate-messages-dialog.png
rename docs/screenshots/{ => archive}/14-feature-message-details-custom-props1.png (100%)
create mode 100644 docs/screenshots/archive/14-generate-messages-dialog.png
create mode 100644 docs/screenshots/archive/15-dlq-history-page.png
create mode 100644 docs/screenshots/archive/15-fab-test-dlq-dialog.png
rename docs/screenshots/{ => archive}/15-feature-message-details-JSON-prop.png (100%)
create mode 100644 docs/screenshots/archive/15-test-dlq-dialog.png
create mode 100644 docs/screenshots/archive/16-dlq-history-detail.png
create mode 100644 docs/screenshots/archive/16-dlq-history-page.png
rename docs/screenshots/{ => archive}/16-feature-message-details-AI-Insight.png (100%)
create mode 100644 docs/screenshots/archive/17-dlq-history-detail.png
rename docs/screenshots/{ => archive}/17-feature-message-details-AI-Insight1.png (100%)
create mode 100644 docs/screenshots/archive/17-rules-page.png
rename docs/screenshots/{ => archive}/18-feature-message-details-AI-Insight2.png (100%)
create mode 100644 docs/screenshots/archive/18-rules-page.png
create mode 100644 docs/screenshots/archive/18-rules-template-gallery.png
rename docs/screenshots/{ => archive}/19-feature-ai-findings-1.png (100%)
create mode 100644 docs/screenshots/archive/19-health-page.png
create mode 100644 docs/screenshots/archive/19-rules-template-gallery.png
rename docs/screenshots/{ => archive}/20-feature-ai-findings-2.png (100%)
create mode 100644 docs/screenshots/archive/20-health-page.png
create mode 100644 docs/screenshots/archive/20-help-page.png
create mode 100644 docs/screenshots/archive/21-help-page-full.png
create mode 100644 docs/screenshots/archive/21-help-page.png
rename docs/screenshots/{ => archive}/21-workflow-dlq-investigation-step1.png (100%)
create mode 100644 docs/screenshots/archive/22-scalar-api-docs.png
rename docs/screenshots/{ => archive}/22-workflow-dlq-investigation-step2.png (100%)
rename docs/screenshots/{ => archive}/23-workflow-dlq-AI-Insight.png (100%)
rename docs/screenshots/{ => archive}/24-workflow-dlq-replay-step4.png (100%)
rename docs/screenshots/{ => archive}/25-feature-find-feature.png (100%)
create mode 100644 docs/screenshots/archive/25-scalar-api-docs.png
rename docs/screenshots/{ => archive}/26-row-ui-new-feature.png (100%)
rename docs/screenshots/{ => archive}/27-dlq-enhancement.png (100%)
rename docs/screenshots/{ => archive}/28-dlq-intelligence.png (100%)
rename docs/screenshots/{ => archive}/29-dlq-history-post-replay-message.png (100%)
rename docs/screenshots/{ => archive}/30-auto-replay-feature.png (100%)
rename docs/screenshots/{ => archive}/31-auto-relay-test-feature.png (100%)
rename docs/screenshots/{ => archive}/32-replay-all-messages.png (100%)
rename docs/screenshots/{ => archive}/33-replay-all-process.png (100%)
rename docs/screenshots/{ => archive}/34-post-replay-all-messages.png (100%)
rename docs/screenshots/{ => archive}/35-rdlq-intelligence-history-post-replay-all.png (100%)
create mode 100644 docs/screenshots/servicehub-demo.gif
create mode 100644 package-lock.json
create mode 100644 package.json
delete mode 100644 services/api/DOCUMENTATION_INDEX.md
diff --git a/README.md b/README.md
index bf5bed1..e9fff17 100644
--- a/README.md
+++ b/README.md
@@ -1,645 +1,426 @@
+
+
# ServiceHub
-**The Forensic Debugger for Azure Service Bus Incidents**
+### The Forensic Debugger for Azure Service Bus
-When production breaks at 2 AM and you need to see **what's inside your queues** โ not just message counts.
+**See what's inside your queues โ not just message counts.**
-
+
[](LICENSE)
-[](https://dotnet.microsoft.com/download/dotnet/10.0)
-[](https://react.dev/)
-[](https://www.typescriptlang.org/)
-
----
-
-## โจ What's New in v2.0
+[](https://dotnet.microsoft.com/)
+[](https://react.dev/)
+[](https://www.typescriptlang.org/)
-๐ฏ **DLQ Intelligence System** โ Persistent tracking of dead-letter messages with categorization and timeline
-โก **Auto-Replay Rules** โ Define conditional replay rules with live statistics (Pending/Replayed/Success)
-๐ **Batch Replay All** โ Replay multiple DLQ messages with one click (optimized for O(N) performance)
-๐ **Instant Scanning** โ "Scan Now" button for immediate DLQ polling (bypasses background schedule)
-๐ **Export Capabilities** โ Download DLQ data as JSON/CSV for reporting
-๐จ **Enhanced UI** โ Improved message row display with better visual hierarchy
+[Get Started](#-quick-start) ยท [Features](#-features) ยท [Screenshots](#-screenshots) ยท [API Docs](#-api-documentation) ยท [Contributing](#-contributing)
-[See screenshots 26-35](#-dlq-intelligence--auto-replay-system) | [Read changelog](#roadmap)
+
---
-## The Problem: Blind Incident Response
+## Why ServiceHub?
-You're troubleshooting a Service Bus incident. Azure Portal shows **5,000 messages in Dead-Letter Queue**, but:
+Production breaks at 2 AM. Azure Portal shows **5,000 messages in Dead-Letter Queue** but you can't read them โ only counts and metadata. You manually sample messages one by one, spending hours on what should take minutes.
-โ **Can't read message content** โ Portal only shows counts and basic metadata
-โ **No search capability** โ Can't find messages by correlation ID or customer ID
-โ **No pattern analysis** โ Can't identify which error types are dominating
-โ **Manual investigation** โ Sample one message at a time with no bulk insight
-โ **Limited DLQ context** โ Can't see failure reasons across all dead-lettered messages
+**ServiceHub is a self-hosted web application that lets you browse, search, and analyze Azure Service Bus messages in real time** โ like email for your message queues.
-**Result:** 2-hour incidents become 6-hour marathons of blind debugging.
+| Capability | Azure Portal | ServiceHub |
+|---|---|---|
+| View message content | Count only | Full body + properties |
+| Search messages | Not available | Full-text search |
+| DLQ investigation | One at a time | Batch analysis + AI |
+| Pattern detection | Not available | AI-powered clustering |
+| Replay from DLQ | Not available | One-click replay |
+| Access | Cloud portal | Any browser |
---
-## ServiceHub vs. Azure Tools
-
-| **Capability** | **Azure Portal** | **Service Bus Explorer** | **ServiceHub** |
-|----------------|------------------|--------------------------|----------------|
-| **View Message Content** | โ Count only | โ
Desktop only | โ
Web-based |
-| **Search Messages** | โ | โ ๏ธ Basic filtering | โ
Full-text + properties |
-| **DLQ Investigation** | โ ๏ธ One at a time | โ ๏ธ Manual inspection | โ
Batch analysis + AI insights |
-| **Pattern Detection** | โ | โ | โ
AI-powered clustering |
-| **Correlation Tracking** | โ | โ ๏ธ Manual | โ
Search by any property |
-| **Read-Only Safety** | โ
| โ ๏ธ Can delete | โ
Peek-only mode |
-| **Multi-Message View** | โ | โ ๏ธ Limited | โ
Browse 100s at once |
-| **DLQ Replay** | โ | โ | โ
One-click replay |
-| **Deployment** | โ๏ธ Cloud | ๐ป Desktop app | ๐ Self-hosted web app |
-| **Access During Incidents** | โ
Always | โ ๏ธ Need .NET desktop | โ
Browser only |
-
-### Why ServiceHub?
-
-โ
**Built for incidents** โ Fast, web-based, read-only by design
-โ
**AI-powered insights** โ Automatically detect error patterns across thousands of messages
-โ
**Search at scale** โ Find needle-in-haystack messages by any property
-โ
**DLQ forensics** โ Understand failure patterns, not just individual messages
-โ
**Safe operations** โ Listen-only permissions, messages never leave the queue
-
----
+## ๐ธ Screenshots
-## Real-World Scenarios
+### Connect to Azure Service Bus
-### Scenario 1: Dead-Letter Queue Incident
-**Problem:** 5,000 orders stuck in DLQ. Customer escalation at 2 AM.
-**ServiceHub Solution:**
-1. Browse all 5,000 DLQ messages in seconds
-2. AI detects **3 error patterns**: "Payment Timeout" (40%), "Invalid Address" (35%), "Duplicate Order" (25%)
-3. Search for specific customer orders by correlation ID
-4. After fixing payment gateway, replay all "Payment Timeout" messages with one click
-
-**Time Saved:** 6 hours โ 45 minutes
-
-### Scenario 2: Message Correlation Tracking
-**Problem:** Customer reports "Order #12345 never processed." Find the message across 10,000 active messages.
-**ServiceHub Solution:**
-1. Search across message body, properties, and custom headers
-2. Find order in 3 seconds
-3. Review full message properties, delivery attempts, and processing timestamps
-
-**Time Saved:** 30 minutes โ 30 seconds
-
-### Scenario 3: Integration Testing
-**Problem:** Need to test error handling with 100 realistic failure scenarios.
-**ServiceHub Solution:**
-1. Use Message Generator with "Payment Gateway" scenario
-2. Generate 100 messages with 30% anomaly rate
-3. Verify error handling and DLQ behavior
-4. Clean up test messages with one filter (tagged `ServiceHub-Generated`)
+Enter your connection string and you're in. Supports Listen-only (read-only) or Manage (full access) policies.
-**Time Saved:** Manual testing โ Automated in 2 minutes
+
----
+### Message Browser
-## Feature Showcase
+Browse active messages across queues and topic subscriptions. See message previews, status badges, and metadata at a glance.
-### ๐จ Enhanced Message Browser UI
+
-Improved message list with better visual hierarchy and information density:
+### Message Detail โ Properties
-
+Click any message to inspect every detail. System properties show Message ID, enqueue time, TTL, sequence number, delivery count, and content type.
-**Improvements:**
-- Clearer message metadata display
-- Enhanced property visibility
-- Better spacing and readability
-- Optimized for long debugging sessions
+
----
+### Message Detail โ Body
-### ๐ Message Browser with Search
+Full JSON body with syntax highlighting and copy button. Supports JSON, XML, and plain text formats.
-Browse messages across queues and topic subscriptions with powerful filtering:
+
-
+### Message Detail โ AI Insights
-
+AI-powered analysis detects patterns, anomalies, and error clusters across your messages. All processing happens in your browser โ no data leaves your environment.
-**Features:**
-- View 100+ messages at once (Azure Portal: count only)
-- Real-time filtering by content, properties, message ID
-- Event type badges, timestamps, delivery counts
-- Active and Dead-Letter queue tabs in one view
+
----
+### Message Detail โ Forensic View
-### ๐ Complete Message Details
+Deep forensic analysis showing message lifecycle, delivery attempts, and processing timeline.
-Click any message to inspect every detail:
+
-**System Properties** โ Message ID, enqueue time, TTL, sequence number:
-
+### Message Detail โ Headers
-**Custom Properties** โ Application headers, correlation IDs, business metadata:
-
+View all custom application properties and correlation headers.
-**Message Body** โ Full JSON with syntax highlighting and copy button:
-
+
----
+### Dead-Letter Queue
-### ๐ค AI-Powered Pattern Detection
+Investigate failed messages with DLQ reason, error description, and AI-powered remediation guidance. Replay messages back to the original queue with one click.
-ServiceHub analyzes **all messages** and automatically identifies error clusters:
+
-
+### DLQ Message Detail with Replay
-**What it detects:**
-- Error message patterns (e.g., "Payment Timeout" across 500 messages)
-- Anomaly rates and confidence scores
-- Message groups by failure type
-- Suggested remediation actions
+See why messages failed and replay them after fixing the root cause.
-
+
-**Real-world example:** From 5,000 DLQ messages, AI instantly identifies:
-- 40% failed due to "Payment Gateway Timeout"
-- 35% failed due to "Invalid Shipping Address"
-- 25% failed due to "Duplicate Order ID"
+### DLQ AI Analysis
----
+AI automatically categorizes DLQ failures and suggests remediation steps.
-### โ ๏ธ Dead-Letter Queue Forensics
+
-Investigate DLQ failures with complete context:
+### Topic Subscriptions
-
+Browse messages from topic subscriptions with the same powerful inspection tools.
-**See instantly:**
-- Dead-letter reason (e.g., "MaxDeliveryCountExceeded")
-- Error description from Azure Service Bus
-- Delivery attempt history
-- Original message timestamp vs. DLQ timestamp
+
-
+### Quick Actions (FAB)
-**ServiceHub provides actionable guidance:**
-- Why the message failed
-- Recommended fix based on failure type
-- Whether to replay or discard
-- Related messages with same failure pattern
+Floating action button provides quick access to Send Message, Generate Messages, Test DLQ, and Refresh All.
----
+
-### ๐ DLQ Message Replay
+### Send Message
-After fixing the root cause, replay messages back to the original queue:
+Send single messages to queues or topics for ad-hoc testing. Supports custom properties, content types, and advanced options.
-
+
-
+### Generate Test Messages
-**Use cases:**
-- Payment gateway was down โ Now restored โ Replay all payment messages
-- Bug in message processor โ Bug fixed โ Replay all failed messages
-- Invalid configuration โ Config corrected โ Reprocess orders
+Generate realistic test messages with built-in scenarios (Order Processing, Payment Gateway, Notification Service, and more). Configure volume and anomaly rate.
----
+
-### ๐ Advanced Search
+### DLQ Intelligence Dashboard
-Find messages by any property โ something Azure Portal **cannot do**:
+Persistent tracking and monitoring of dead-letter queue messages with trend chart, status breakdown, and category classification.
-
+
-**Search across:**
-- Message body content (JSON, XML, plain text)
-- Custom properties (correlation ID, customer ID, order number)
-- System properties (message ID, session ID)
-- Event types and categories
+### DLQ History Detail
-**Example:** Find all messages for customer "C-12345" across 10,000 messages in 2 seconds.
+Drill into individual DLQ records with forensic timeline, replay history, and status tracking.
----
+
-## Testing & Development Tools
+### Auto-Replay Rules
-### Message Generator
+Define conditional replay rules with live statistics. Match messages by dead-letter reason, error description, entity name, content type, or body text.
-Generate realistic test messages for integration testing:
+
-
+### Rule Template Gallery
-
+Browse pre-built rule templates for common failure scenarios โ transient errors, max delivery exceeded, expired messages, and more.
-**6 Built-in Scenarios:**
-1. **Order Processing** โ E-commerce order flow
-2. **Payment Gateway** โ Payment transactions with retries
-3. **Notification Service** โ Email/SMS delivery events
-4. **Inventory Management** โ Stock updates and alerts
-5. **User Activity** โ Login, registration, profile updates
-6. **Error Events** โ Simulated failures for testing
+
-**Configuration:**
-- Volume: 30, 50, 100, 150, 200 messages
-- Anomaly rate: 0% to 50% (inject failures for testing)
-- Auto-tagged with `ServiceHub-Generated` for easy cleanup
+### Create Auto-Replay Rule
-### Send Custom Messages
+Build custom rules with field conditions, operators, actions, rate limiting, and target entity configuration.
-Send single messages to queues or topics for ad-hoc testing:
+
-
+### System Health
-
+Monitor API health, uptime, memory usage, thread count, GC collections, and server information.
----
+
-## Quick Start (Zero Configuration!)
+### Help & Quick Reference
-### ๐ Automated Setup (Recommended)
+Searchable help guide covering every feature, Azure Service Bus concepts, and a guided tour.
-**No manual prerequisites needed!** The setup script automatically installs everything:
+
-```bash
-git clone https://github.com/debdevops/servicehub.git
-cd servicehub
-./run.sh
-```
+### Message Search & Filter
-The script will:
-โ
Auto-install .NET 8 SDK (if not present)
-โ
Auto-install Node.js 18+ (if not present)
-โ
Install all dependencies
-โ
Start both API and UI servers
+Find messages by any property โ message ID, correlation ID, body content, custom headers.
-**First run:** 5-10 minutes (includes installations)
-**Subsequent runs:** 30-60 seconds
+
-Open **http://localhost:3000**
+### Sidebar Navigation
-> ๐ **Detailed Setup Guide:** See [SETUP.md](SETUP.md) for platform-specific details, troubleshooting, and manual installation options.
+Namespace browser with live message counts, queue/topic tree, and Quick Access panel.
-### Prerequisites (Auto-Installed)
-The following are **automatically installed** by `run.sh`:
-- โ
.NET 8.0 SDK
-- โ
Node.js 18+
-- โ
npm package manager
-- โ
Required utilities (curl, lsof)
+
-**Supports all major platforms:**
-- macOS (Intel & Apple Silicon), Ubuntu/Debian, RHEL/CentOS/Fedora, Arch Linux, openSUSE, Alpine, WSL
-- See [CROSS-PLATFORM-COMPATIBILITY.md](CROSS-PLATFORM-COMPATIBILITY.md) for complete list
+### API Documentation (Scalar)
-**You only need:**
-- โ ๏ธ Azure Service Bus connection string (Listen permission only)
+Interactive API documentation with Scalar โ test endpoints directly from the browser.
-
+
-### Connect to Your Namespace
+---
-1. Enter a display name for your connection
-2. Paste your Service Bus connection string
-3. Click "Connect"
+## ๐ Features
-
+### Message Browser
+- Browse **active** and **dead-letter** queue messages side by side
+- View full message body with JSON syntax highlighting
+- Inspect system properties, custom headers, and application properties
+- Real-time search across message content and properties
+- Auto-refresh with configurable polling intervals
-Your queues and topics appear instantly:
+### AI-Powered Analysis
+- **Pattern detection** โ Identify error clusters across thousands of messages
+- **Anomaly identification** โ Flag unusual messages automatically
+- **Remediation suggestions** โ Actionable guidance for each failure type
+- **Client-side processing** โ All analysis runs in your browser; no data leaves your environment
-
+### DLQ Intelligence System
+- **Persistent tracking** โ DLQ messages stored in local SQLite database
+- **Category classification** โ Auto-categorizes: Transient, MaxDelivery, Expired, DataQuality, Authorization
+- **Trend chart** โ 30-day DLQ trend visualization (New vs. Resolved)
+- **Instant scanning** โ "Scan Now" for immediate DLQ polling
+- **Export** โ Download DLQ data as JSON or CSV
+- **Status tracking** โ Active โ Replayed โ ReplayFailed โ Resolved
-**That's it!** Start browsing messages, investigating DLQs, and using AI insights.
+### Auto-Replay Rules Engine
+- **Conditional matching** โ Match messages by reason, error description, entity, delivery count, body text
+- **Operators** โ Contains, Equals, StartsWith, EndsWith, Regex, GreaterThan, LessThan, In
+- **Live statistics** โ Pending/Replayed/Success counts updated in real time
+- **Rate limiting** โ Max replays per hour to prevent overwhelming downstream services
+- **Batch replay** โ Replay all matching messages with one click
+- **Template gallery** โ Pre-built rules for common failure scenarios
+- **Circuit breaker** โ Auto-disables rules if success rate drops below threshold
+
+### Testing & Development Tools
+- **Send Message** โ Send single messages to queues or topics with custom properties
+- **Generate Messages** โ 6 built-in scenarios with configurable volume (30โ200) and anomaly rate (0โ50%)
+- **Test DLQ** โ Move test messages to dead-letter queue for testing DLQ workflows
+- **Tagged messages** โ All generated messages tagged with `ServiceHub-Generated` for easy cleanup
+
+### Security & Safety
+- **Read-only by default** โ Uses Azure SDK PeekMessagesAsync; messages are never removed
+- **Listen-only supported** โ Works with Listen permission for browse-only access
+- **Encrypted at rest** โ Connection strings encrypted with AES-GCM
+- **No external API calls** โ AI analysis runs entirely in the browser
+- **No data persistence** โ Messages displayed in-memory only
+- **Safe for production** โ Will not interfere with your consumers
---
-## Architecture
+## โก Quick Start
-```
-servicehub/
-โโโ apps/web/ # React + TypeScript frontend
-โ โโโ src/
-โ โโโ components/ # UI components
-โ โโโ hooks/ # React Query hooks
-โ โโโ lib/ # API client, utilities
-โ
-โโโ services/api/ # ASP.NET Core backend
-โ โโโ src/
-โ โโโ ServiceHub.Api/ # REST API controllers
-โ โโโ ServiceHub.Core/ # Domain entities & DTOs
-โ โโโ ServiceHub.Infrastructure/ # Azure SDK integration
-โ
-โโโ run.sh # One-command startup script
-```
+### Automated Setup (Recommended)
-**Tech Stack:**
-- **Frontend:** React 18, TypeScript, Tailwind CSS, TanStack Query (React Query)
-- **Backend:** ASP.NET Core 8, Azure.Messaging.ServiceBus SDK
-- **AI Analysis:** Client-side pattern detection (no external API calls)
-- **Database:** None (stateless, in-memory only)
-
----
+```bash
+git clone https://github.com/debdevops/servicehub.git
+cd servicehub
+./run.sh
+```
-## Security & Compliance
+The script automatically:
+- Installs .NET 10 SDK (if not present)
+- Installs Node.js 18+ (if not present)
+- Builds and starts both API and UI servers
-### Read-Only by Design
+Open **http://localhost:3000** and connect with your Azure Service Bus connection string.
-ServiceHub uses Azure SDK's **`PeekMessagesAsync`** โ messages are **never removed** from queues:
+### Prerequisites
-```csharp
-// ServiceHub only reads โ never receives or deletes
-await receiver.PeekMessagesAsync(maxMessages: 100);
-```
+Auto-installed by `run.sh`:
+- .NET 10.0 SDK
+- Node.js 18+
-โ
**Safe for production** โ Messages remain in queue for your actual consumers
-โ
**Audit-friendly** โ No message deletion or modification
-โ
**Incident-safe** โ Won't interfere with recovery processes
+You provide:
+- Azure Service Bus connection string (Listen permission minimum)
-### Minimal Permissions Required
-
-Create a dedicated SAS policy with **Listen permission only**:
+### Create a Service Bus Policy
+For read-only browsing:
```bash
-# Azure CLI
az servicebus namespace authorization-rule create \
--namespace-name \
- --name servicehub-readonly \
+ --resource-group \
+ --name servicehub \
--rights Listen
```
-โ ServiceHub does NOT require:
-- `Manage` permissions
-- `Send` permissions (except for testing tools, optional)
-- `Receive` permissions (never removes messages)
-
-### Data Privacy
-
-- **No external API calls** โ AI analysis runs in your browser
-- **No data persistence** โ Messages displayed in-memory only
-- **No logging of message content** โ API logs only metadata (message count, queue names)
-- **Self-hosted** โ Deploy in your VPC/VNET, no SaaS dependencies
-
----
-
-## Use Cases by Role
-
-### DevOps Engineers
-โ
Investigate DLQ spikes during incidents
-โ
Validate message processing after deployments
-โ
Search for specific customer transactions
-โ
Monitor message flow across queues and topics
-
-### QA Engineers
-โ
Generate realistic test messages for integration testing
-โ
Verify message routing to correct topic subscriptions
-โ
Test error handling with controlled anomaly injection
-โ
Validate message transformations and enrichments
-
-### Support Teams
-โ
Find customer orders by correlation ID during escalations
-โ
Verify if customer message was received or dead-lettered
-โ
Provide timestamps and failure reasons to customers
-โ
Replay messages after incident resolution
-
-### Platform Engineers
-โ
Debug namespace-level message routing issues
-โ
Analyze message patterns for capacity planning
-โ
Investigate intermittent failures across multiple queues
-โ
Document incident timelines with actual message evidence
-
----
-
-## Key Differentiators
-
-| Feature | Impact |
-|---------|--------|
-| **Web-Based** | No desktop installation needed โ accessible from any browser during incidents |
-| **Bulk Message View** | Browse 100s of messages at once vs. one-by-one in Azure Portal |
-| **AI Pattern Detection** | Automatically identify root causes across thousands of DLQ messages |
-| **Full-Text Search** | Find messages by correlation ID, customer ID, or any property |
-| **DLQ Replay** | One-click message reprocessing after fixing issues |
-| **Testing Tools** | Generate realistic scenarios for integration testing |
-| **Read-Only Safety** | Listen-only permissions โ never removes messages from production |
-| **Self-Hosted** | Deploy in your environment, no SaaS vendor dependency |
-
----
-
-## ๐ฏ DLQ Intelligence & Auto-Replay System
-
-ServiceHub includes a **production-ready DLQ management system** that goes far beyond simple message inspection.
-
-### ๐ DLQ Intelligence Dashboard
-
-Persistent tracking and monitoring of dead-letter queue messages:
-
-
-
-
-
-**Features:**
-- **Persistent history** โ All DLQ messages tracked in SQLite database
-- **Instant scanning** โ "Scan Now" button triggers immediate DLQ poll (bypasses 10-15s background schedule)
-- **Category classification** โ Auto-categorizes failures: Transient, MaxDelivery, Expired, DataQuality, Authorization, etc.
-- **Export capabilities** โ Download DLQ data as JSON or CSV for reporting
-- **Timeline view** โ Replay history with timestamps, outcomes, and error details
-- **Status tracking** โ Active โ Replayed โ ReplayFailed โ Resolved
+For full access (send, generate, replay):
+```bash
+az servicebus namespace authorization-rule create \
+ --namespace-name \
+ --resource-group \
+ --name servicehub \
+ --rights Listen Send Manage
+```
-
+### URLs
-**Background monitoring:** DLQ messages are automatically scanned every 10-15 seconds and stored for forensic analysis.
+| Service | URL |
+|---|---|
+| UI | http://localhost:3000 |
+| API | http://localhost:5153 |
+| API Docs | http://localhost:5153/scalar/v1 |
---
-### โก Auto-Replay Rules Engine
-
-Define **conditional replay rules** that match DLQ messages and enable batch operations:
-
-
-
-**Rule Components:**
-- **Conditions** โ Match messages by reason, error description, entity name, delivery count, content type, body text, or custom properties
-- **Operators** โ Contains, Equals, StartsWith, EndsWith, Regex, GreaterThan, LessThan, In
-- **Target Entity** โ Optionally replay to a different queue/topic (not just original)
-- **Rate Limiting** โ Max replays per hour to prevent overwhelming downstream services
+## ๐๏ธ Architecture
-**Rule Statistics (Live Evaluation):**
-- **Pending** โ How many Active DLQ messages currently match this rule (evaluated in real-time)
-- **Replayed** โ How many messages have been replayed using this rule
-- **Success** โ Replay success count and percentage
+```
+servicehub/
+โโโ apps/web/ # React 18 + TypeScript + Vite frontend
+โ โโโ src/
+โ โโโ components/ # UI components (messages, DLQ, rules, FAB)
+โ โโโ hooks/ # React Query hooks for API communication
+โ โโโ lib/ # API client, utilities, help content
+โ โโโ pages/ # Page components (Messages, Connect, Rules, Health, Help)
+โ
+โโโ services/api/ # ASP.NET Core 10 backend
+โ โโโ src/
+โ โโโ ServiceHub.Api/ # REST controllers, middleware, auth
+โ โโโ ServiceHub.Core/ # Domain entities, DTOs, interfaces
+โ โโโ ServiceHub.Infrastructure/ # Azure SDK integration, SQLite
+โ โโโ ServiceHub.Shared/ # Common types and utilities
+โ
+โโโ scripts/ # Setup and utility scripts
+โโโ run.sh # One-command startup
+```
-
+**Tech Stack:**
+| Layer | Technology |
+|---|---|
+| Frontend | React 18, TypeScript, Tailwind CSS, TanStack Query |
+| Backend | ASP.NET Core 10, Azure.Messaging.ServiceBus SDK |
+| AI Analysis | Client-side pattern detection (no external API) |
+| Database | SQLite (DLQ Intelligence), in-memory cache |
+| API Docs | Scalar (OpenAPI) |
-**Test Before Replay:** Click "Test" to preview how many Active DLQ messages match your conditions before executing any replay.
+For detailed backend architecture, see [services/api/ARCHITECTURE.md](services/api/ARCHITECTURE.md).
---
-### ๐ Batch Replay All System
-
-Replay **multiple matching messages** with a single click:
-
-
-
-**Safety Features:**
-- **Confirmation dialog with warnings** โ Red danger header, 3 safety warnings before executing
-- **Real-time statistics** โ Shows how many messages will be affected
-- **Cancel auto-focused** โ Safer default to prevent accidental clicks
-- **Test first workflow** โ Encourages testing rules before bulk replay
-
-
-
-**Performance:** Optimized batch replay using **single DLQ receiver per entity** (not per message). Tested with 7 messages across 2 subscriptions โ **9 seconds** (vs. 30s+ timeout before optimization).
-
-
-
-**Outcome Tracking:**
-- Messages matched: 7
-- Successfully replayed: 7
-- Failed: 0
-- Skipped: 0
-
-
+## ๐ก Real-World Scenarios
-**History Audit:** Every replay attempt is recorded in the DLQ Intelligence history with:
-- Timestamp
-- Rule name or "Manual Replay"
-- Outcome (Success/Failed)
-- Target entity
-- Error details (if failed)
-
----
+### Scenario 1: Dead-Letter Queue Incident
+**Problem:** 5,000 orders stuck in DLQ at 2 AM.
-### Real-World DLQ Scenario
+**With ServiceHub:**
+1. Browse all 5,000 DLQ messages instantly
+2. AI detects 3 error patterns: Payment Timeout (40%), Invalid Address (35%), Duplicate Order (25%)
+3. Search for specific customer orders by correlation ID
+4. Create auto-replay rule for Payment Timeout โ replay all 2,000 messages
-**Situation:** 1,500 order messages dead-lettered due to payment gateway outage.
+**Time saved:** 6 hours โ 45 minutes
-**ServiceHub Workflow:**
-1. Open **DLQ Intelligence** โ See 1,500 Active messages categorized as "Transient" failures
-2. Create **Auto-Replay Rule**:
- - Condition: `Reason contains "Payment Gateway Timeout"`
- - Target: Original subscription
- - Rate limit: 500/hour
-3. Click **Test** โ Confirms 1,500 messages match
-4. Click **Replay All** โ Review warnings โ Confirm
-5. Result: **All 1,500 messages replayed in ~3 minutes**
-6. Check **DLQ Intelligence History** โ See replay audit trail with success/failure counts
+### Scenario 2: Message Correlation
+**Problem:** Customer reports order never processed.
-**Why it's safe:**
-- โ
Messages removed from DLQ only after successful send to target
-- โ
Failed replays remain in DLQ with error description
-- โ
Rate limiting prevents overwhelming downstream services
-- โ
Full audit trail for compliance/debugging
+**With ServiceHub:**
+1. Search across message body and properties
+2. Find order in 3 seconds across 10,000 messages
+3. Review full message properties and delivery history
----
+**Time saved:** 30 minutes โ 30 seconds
-## Roadmap
-
-**Completed Features** โ
+### Scenario 3: Integration Testing
+**Problem:** Need to test error handling with 100 realistic failure scenarios.
-- โ
**Bulk DLQ Replay** โ Replay multiple messages with rule-based filters
-- โ
**DLQ Intelligence** โ Persistent tracking, categorization, and history
-- โ
**Auto-Replay Rules** โ Conditional replay with rate limiting
-- โ
**Batch Optimization** โ O(N) performance with single receiver per entity
+**With ServiceHub:**
+1. Select Payment Gateway scenario in Message Generator
+2. Generate 100 messages with 30% anomaly rate
+3. Verify error handling and DLQ behavior
+4. Clean up test messages filtered by ServiceHub-Generated tag
-**Community Requests:** [Open an issue](https://github.com/debdevops/servicehub/issues) to suggest features!
+**Time saved:** Manual testing โ Automated in 2 minutes
---
-## Comparison: ServiceHub vs. Alternatives
+## ๐ Permissions Guide
-### vs. Azure Portal
+| Permission Level | Capabilities |
+|---|---|
+| **Listen only** | Browse messages, inspect DLQ, search, AI insights, view health |
+| **Listen + Send** | All above + replay from DLQ + send test messages |
+| **Manage** | All above + generate messages, test DLQ, full management |
-**Azure Portal Strengths:**
-- Always available, no installation
-- Basic metrics and alerting
-- Namespace management
-
-**Azure Portal Limitations:**
-- โ Cannot view message **content** (only metadata)
-- โ No search or filtering capabilities
-- โ DLQ investigation requires manual sampling
-- โ No pattern detection or bulk analysis
-
-**When to use ServiceHub:** When you need to see **what's inside messages** during incidents.
+> **Tip:** Create a dedicated `servicehub` policy instead of using `RootManageSharedAccessKey`.
---
-### vs. Azure Service Bus Explorer (Desktop)
-
-**Service Bus Explorer Strengths:**
-- Rich feature set for power users
-- Message management (send, receive, delete)
-- Detailed property inspection
-
-**Service Bus Explorer Limitations:**
-- โ ๏ธ Desktop application (Windows .NET required)
-- โ ๏ธ Limited multi-message view
-- โ No AI pattern detection
-- โ No bulk DLQ analysis
-- โ ๏ธ Can accidentally delete production messages
+## ๐ API Documentation
-**When to use ServiceHub:**
-- Web-based access from any device
-- Read-only safety for production
-- AI-powered pattern detection
-- Bulk message analysis
+ServiceHub exposes a REST API documented with Scalar (OpenAPI). Access interactive docs at:
----
-
-### vs. Custom Scripts
+**http://localhost:5153/scalar/v1**
-**Custom Scripts (PowerShell/CLI) Strengths:**
-- Automation potential
-- Scriptable workflows
+
-**Custom Scripts Limitations:**
-- โ ๏ธ Requires scripting expertise
-- โ No UI for visual exploration
-- โ Limited to technical users
-- โ ๏ธ Time-consuming to build and maintain
-
-**When to use ServiceHub:**
-- Ready-to-use web UI
-- Accessible to non-developers (support teams, QA)
-- Zero maintenance required
+Key endpoints:
+- `GET /api/v1/namespaces` โ List connected namespaces
+- `GET /api/v1/namespaces/{id}/queues` โ List queues with message counts
+- `GET /api/v1/namespaces/{id}/topics` โ List topics with subscription counts
+- `GET /api/v1/namespaces/{id}/queues/{name}/messages` โ Browse messages
+- `POST /api/v1/namespaces/{id}/queues/{name}/messages` โ Send a message
+- `GET /api/v1/dlq-history` โ DLQ Intelligence records
+- `GET /api/v1/replay-rules` โ Auto-replay rules
---
-## FAQ
-
-**Q: Does ServiceHub remove messages from queues?**
-A: No. ServiceHub uses Azure SDK's **`PeekMessagesAsync`** which only reads messages without removing them. Your consumers continue processing normally.
+## โ FAQ
-**Q: Can I use ServiceHub in production?**
-A: Yes. ServiceHub only requires **Listen** permission and operates in read-only mode (except for optional testing tools). Messages remain in queues for your actual consumers.
+**Q: Does ServiceHub remove messages from queues?**
+No. ServiceHub uses PeekMessagesAsync which reads messages without removing them. Your consumers continue processing normally.
-**Q: How does AI pattern detection work?**
-A: ServiceHub analyzes message content, properties, and error descriptions using heuristic algorithms **in your browser**. No data leaves your environment โ no external AI API calls.
+**Q: Is it safe for production?**
+Yes. ServiceHub only requires Listen permission and operates in read-only mode. Messages remain in queues for your actual consumers.
-**Q: What about sensitive data in messages?**
-A: ServiceHub displays messages only in your browser session (not persisted). Deploy ServiceHub in your private network and restrict access via your identity provider. API logs do not capture message content.
+**Q: How does AI analysis work?**
+ServiceHub analyzes message content using heuristic algorithms entirely in your browser. No data leaves your environment.
-**Q: Can I deploy ServiceHub to Azure App Service / Kubernetes?**
-A: Yes. ServiceHub is a standard ASP.NET Core + React app. You can containerize it with Docker and deploy to any platform supporting .NET 8 and Node.js.
+**Q: What about sensitive data?**
+Messages are displayed only in your browser session โ not persisted. Deploy ServiceHub in your private network and restrict access.
-**Q: Does ServiceHub support topics with subscriptions?**
-A: Yes. ServiceHub browses messages from both queues and topic subscriptions. You can view messages for each subscription independently.
+**Q: Can I deploy to Azure App Service / Kubernetes?**
+Yes. ServiceHub is a standard ASP.NET Core + React SPA. Containerize with Docker and deploy anywhere supporting .NET 10.
-**Q: What about message sessions?**
-A: Session-aware browsing is on the roadmap. Currently, you can view session-enabled messages but not group them by session ID.
-
-**Q: Can I contribute?**
-A: Absolutely! ServiceHub is open source (MIT license). Fork the repository, make your changes, and submit a pull request.
+**Q: Does it support topics with subscriptions?**
+Yes. Browse messages from both queues and topic subscriptions independently.
---
-## Contributing
-
-We welcome contributions! Whether it's bug fixes, new features, or documentation improvements.
+## ๐ค Contributing
-**How to contribute:**
+We welcome contributions! Bug fixes, features, and documentation improvements.
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes with tests
-4. Commit your changes (`git commit -m 'Add amazing feature'`)
-5. Push to the branch (`git push origin feature/amazing-feature`)
-6. Open a Pull Request
+4. Commit and push
+5. Open a Pull Request
---
@@ -647,43 +428,14 @@ We welcome contributions! Whether it's bug fixes, new features, or documentation
MIT License โ see [LICENSE](LICENSE) for details.
-**TL;DR:** Use ServiceHub freely in your organization, modify as needed, no attribution required in your deployment.
-
----
-
-## Links & Resources
-
-- **๐ Repository:** https://github.com/debdevops/servicehub
-- **๐ Report Issues:** https://github.com/debdevops/servicehub/issues
-- **๐ Full Documentation:** [docs/COMPREHENSIVE-GUIDE.md](docs/COMPREHENSIVE-GUIDE.md)
-- **๏ฟฝ Screenshots Guide:** [docs/SCREENSHOTS.md](docs/SCREENSHOTS.md)
-- **๐ฌ Discussions:** https://github.com/debdevops/servicehub/discussions
-
----
-
-## Support
-
-**Need help?**
-- ๐ Check the [documentation](docs/)
-- ๐ฌ Start a [discussion](https://github.com/debdevops/servicehub/discussions)
-- ๐ Report a [bug](https://github.com/debdevops/servicehub/issues)
-
-**Enterprise support:** For production deployment assistance, custom features, or training โ open an issue with `[Enterprise]` tag.
-
----
-
-## Star History
-
-If ServiceHub saved you time during an incident, give it a โญ on GitHub!
-
---
-**ServiceHub** โ Because your Service Bus messages shouldn't be invisible during incidents.
+**ServiceHub** โ Because your Service Bus messages should not be invisible during incidents.
-Built with โค๏ธ for DevOps and Platform Engineers
+Built for DevOps, Platform, and SRE Engineers.
-[Get Started](#quick-start-5-minutes) โข [View Features](#feature-showcase) โข [Report Issue](https://github.com/debdevops/servicehub/issues)
+[Get Started](#-quick-start) ยท [View Features](#-features) ยท [Report Issue](https://github.com/debdevops/servicehub/issues)
diff --git a/REMOTE_ACCESS.md b/REMOTE_ACCESS.md
deleted file mode 100644
index 257a483..0000000
--- a/REMOTE_ACCESS.md
+++ /dev/null
@@ -1,174 +0,0 @@
-# ServiceHub โ Remote Server Access Guide
-
-This guide explains how to run ServiceHub on a Linux server and access it
-from a different machine (laptop, another server, etc.).
-
----
-
-## How It Works
-
-ServiceHub uses a Vite development proxy to route browser API calls:
-
-```
-Your Browser โ http://linuxhost:3000/api/v1/...
- โ (Vite proxy, same server)
- http://localhost:5153/api/v1/... (ASP.NET Core API)
-```
-
-By keeping the proxy on the server, the browser never needs to reach port 5153
-directly โ it just calls port 3000 (the UI port) for everything. This means:
-
-- **No CORS issues** โ the browser sees all requests as same-origin (port 3000).
-- **Firewall simplicity** โ you only need to open one port (3000) to the outside world.
-- **No hardcoded IP addresses** in client code.
-
----
-
-## Quick Start (Remote Server)
-
-### 1. Start ServiceHub
-
-```bash
-cd /path/to/servicehub
-bash run.sh
-```
-
-The startup banner will show all access URLs automatically:
-
-```
-๐ Web UI:
- โข http://localhost:3000 โ from this machine
- โข http://192.168.1.50:3000 โ from remote machines (by IP)
- โข http://linuxhost:3000 โ from remote machines (by hostname)
-
-๐ API Endpoints:
- โข HTTP: http://localhost:5153
- โข Remote: http://192.168.1.50:5153
- โข API Docs: http://localhost:5153/scalar/v1
-```
-
-### 2. Access from Your Laptop
-
-Open a browser on your laptop and navigate to:
-
-```
-http://linuxhost:3000
-```
-
-Replace `linuxhost` with the server's hostname or IP address.
-
----
-
-## Firewall Configuration
-
-If the connection is refused, open the UI port on the server's firewall.
-You only need port **3000** (API calls are proxied through it).
-
-**Ubuntu / Debian (ufw):**
-```bash
-sudo ufw allow 3000/tcp
-sudo ufw reload
-```
-
-**RHEL / CentOS / Fedora (firewalld):**
-```bash
-sudo firewall-cmd --add-port=3000/tcp --permanent
-sudo firewall-cmd --reload
-```
-
-**Quick test** (from your laptop):
-```bash
-curl http://linuxhost:3000/health
-```
-
----
-
-## Supporting Additional CORS Origins (Advanced)
-
-The Vite proxy eliminates CORS for browser calls. However, if you are making
-**direct API calls** from another backend service or tool (e.g. curl, Postman,
-another server-side app), you need to tell the .NET API which origins to allow.
-
-Use the `SERVICEHUB_ALLOWED_ORIGINS` environment variable โ no config-file edits needed:
-
-```bash
-export SERVICEHUB_ALLOWED_ORIGINS="http://192.168.1.50:3000,http://linuxhost:3000"
-bash run.sh
-```
-
-Multiple origins are comma-separated. This variable is read at startup by
-`CorsConfiguration.cs` and merged into the allowed origins list alongside any
-values already in `appsettings.json`.
-
----
-
-## Environment Variable Reference
-
-| Variable | Purpose | Example |
-|---|---|---|
-| `SERVICEHUB_ALLOWED_ORIGINS` | Extra CORS origins (comma-separated) | `http://192.168.1.50:3000` |
-| `VITE_API_BASE_URL` | Override API base URL in the browser client | `http://192.168.1.50:5153/api/v1` |
-
-> **Note:** `VITE_API_BASE_URL` is only needed if you want the browser to call the API
-> **directly** (bypassing the proxy). With the default proxy configuration you do
-> not need to set this.
-
----
-
-## Troubleshooting
-
-### "Connection Refused" on port 3000
-
-1. Confirm Vite started with `--host 0.0.0.0`:
- ```bash
- grep "host" /tmp/servicehub_ui_startup.log
- ```
-2. Check the firewall rules (see above).
-3. Verify the process is listening:
- ```bash
- ss -tlnp | grep 3000
- ```
-
-### "Network Error" / API Can't Be Reached
-
-The browser is talking to the Vite proxy on port 3000, which forwards to the API
-on port 5153 (loopback). If you see API errors:
-
-1. Check the API is running:
- ```bash
- curl http://localhost:5153/health
- ```
-2. Check API logs:
- ```bash
- tail -50 /tmp/servicehub_api_startup.log
- ```
-
-### CORS Errors in Browser Dev Tools
-
-If you see `Access-Control-Allow-Origin` errors, it means requests are bypassing
-the Vite proxy and hitting the API directly. Ensure:
-
-- `VITE_API_BASE_URL` is **not** set to an absolute URL pointing at a different
- host. Clear it or leave it unset to use the proxy.
-- The browser is loading the UI from the same origin it's making API calls to.
-
----
-
-## Production Deployment
-
-For a production Linux deployment (no Vite dev server), build the React app and
-serve it from the .NET API's wwwroot folder:
-
-```bash
-# Build the React app (outputs to services/api/src/ServiceHub.Api/wwwroot)
-cd apps/web
-npm run build
-
-# Run the .NET API (it serves both the SPA and the API)
-cd services/api
-dotnet run --project src/ServiceHub.Api/ServiceHub.Api.csproj \
- --urls "http://0.0.0.0:5153" \
- --environment Production
-```
-
-Then open `http://linuxhost:5153` โ only one port needed in production.
diff --git a/docs/PERMISSIONS.md b/docs/PERMISSIONS.md
deleted file mode 100644
index a99126f..0000000
--- a/docs/PERMISSIONS.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# ServiceHub โ Azure Permissions Guide
-
-ServiceHub requires appropriate permissions to access your Azure Service Bus resources. This guide explains the permissions needed for different features.
-
----
-
-## Recommended Setup: Shared Access Policy
-
-**For full ServiceHub functionality, create a dedicated Shared Access Policy with:**
-- โ
**Manage** permission
-- โ
**Send** permission
-- โ
**Listen** permission
-
-**How to create:**
-1. Go to Azure Portal โ Your Service Bus Namespace
-2. Navigate to **Shared Access Policies**
-3. Click **+ Add**
-4. Name: `ServiceHub-FullAccess` (or your preferred name)
-5. Check: โ
**Manage**, โ
**Send**, โ
**Listen**
-6. Click **Create**
-7. Copy the **Primary Connection String**
-
-โ ๏ธ **Do NOT use RootManageSharedAccessKey** โ Always create a dedicated policy for ServiceHub.
-
----
-
-## Alternative: Limited Permissions (Read-Only)
-
-If you only need to browse messages without replay or testing capabilities:
-
-**Required Role (using Azure RBAC):**
-- `Azure Service Bus Data Receiver`
-
-**Or create a Shared Access Policy with:**
-- โ
**Listen** permission only
-
-**Permissions Granted:**
-- โ
Peek/browse messages from queues and subscriptions
-- โ
View message metadata, properties, and bodies
-- โ
View queue and topic metrics
-- โ Cannot replay messages from DLQ
-- โ Cannot create test DLQ messages
-
----
-
-## Feature-Specific Requirements
-
-### ๐ Read-Only Investigation
-
-**Permissions Required:**
-- Listen (peek messages)
-
-**What You Can Do:**
-- Browse active and dead-letter queue messages
-- View message details and properties
-- Search and filter messages
-- View queue/topic metrics
-
-### ๐ Replay Messages from DLQ
-
-**Permissions Required:**
-- Listen (read from DLQ)
-- Send (write to active queue)
-
-**What You Can Do:**
-- All read-only features
-- Move messages from DLQ back to main queue
-
-### ๐งช Create Test DLQ Messages
-
-**Permissions Required:**
-- Listen (read from queue)
-- Send (move messages to DLQ)
-
-**What You Can Do:**
-- All read-only features
-- Manually dead-letter messages for testing
-
-### ๐ ๏ธ Full Management
-
-**Permissions Required:**
-- Manage (full control)
-
-**What You Can Do:**
-- All features above
-- Future management operations
\ No newline at end of file
diff --git a/docs/SCREENSHOTS.md b/docs/SCREENSHOTS.md
deleted file mode 100644
index 2695438..0000000
--- a/docs/SCREENSHOTS.md
+++ /dev/null
@@ -1,331 +0,0 @@
-# ServiceHub Screenshots Guide
-
-This guide explains the 35 screenshots used in the README and how they demonstrate ServiceHub capabilities.
-
----
-
-## ๐ธ Screenshot Organization
-
-All screenshots are located in `docs/screenshots/` with descriptive filenames that indicate their purpose and placement in the documentation.
-
-### Screenshot Mapping
-
-| # | Filename | Shows | Used In Section |
-|---|----------|-------|-----------------|
-| 1 | 01-Start-The-App.png | Application startup | Quick Start |
-| 2 | 02-Connect-Service-Bus-With-Manage-ConnStr.png | Connection form with connection string | Quick Start |
-| 3 | 03-Connected-ServiceBus.png | Connected namespace view | Quick Start |
-| 4 | 04-feature-message-browser-empty.png | Message browser (empty) | Features |
-| 5 | 05-main-message-display1.png | Message browser with loaded messages | Features - Message Browser |
-| 6 | 06-feature-message-generator.png | Message generator basic form | Testing Tools |
-| 7 | 07-feature-message-generator-basic-single-message.png | Single message generator | Testing Tools |
-| 8 | 08-feature-message-generator-scenarios.png | Generator scenarios selection | Testing Tools |
-| 9 | 09-feature-send-message.png | Send message form | Testing Tools |
-| 10 | 10-message-display.png | Message details view | Features - Message Details |
-| 11 | 11-Generate-Single-Message-Topic.png | Generate message to topic | Testing Tools |
-| 12 | 12-showing-message-topic.png | Topic messages display | Features |
-| 13 | 13-feature-message-details-custom-props.png | Custom properties tab | Features - Message Details |
-| 14 | 14-feature-ai-findings.png | AI findings indicator | Features - AI Insights |
-| 15 | 15-feature-message-details-JSON-prop.png | Message body JSON | Features - Message Details |
-| 16 | 16-feature-message-details-AI-Insight.png | AI insights in message details | Features - AI Insights |
-| 17 | 17-feature-ai-patterns-popup.png | AI patterns summary popup | Features - AI Insights |
-| 18 | 18-feature-dlq-tab-with-ai.png | DLQ tab with AI indicators | Features - DLQ |
-| 19 | 19-feature-ai-findings-1.png | AI findings detection | Features - AI Insights |
-| 20 | 20-workflow-dlq-investigation-step1.png | DLQ investigation step 1 | Workflows - DLQ |
-| 21 | 21-workflow-dlq-investigation-step1.png | DLQ investigation detailed | Workflows - DLQ |
-| 22 | 22-workflow-dlq-investigation-step2.png | DLQ AI insights view | Workflows - DLQ |
-| 23 | 23-workflow-dlq-AI-Insight.png | DLQ AI guidance | Workflows - DLQ |
-| 24 | 24-workflow-dlq-replay-step4.png | Replay confirmation dialog | Workflows - DLQ |
-| 25 | 25-feature-find-feature.png | Advanced search functionality | Features - Search |
-| 26 | 26-row-ui-new-feature.png | Enhanced message row UI | Features - Message Browser |
-| 27 | 27-dlq-enhancement.png | DLQ enhancements | Features - DLQ Intelligence |
-| 28 | 28-dlq-intelligence.png | DLQ Intelligence dashboard | Features - DLQ Intelligence |
-| 29 | 29-dlq-history-post-replay-message.png | DLQ history after single replay | Features - DLQ Intelligence |
-| 30 | 30-auto-replay-feature.png | Auto-Replay Rules page with rule cards | Features - Auto-Replay System |
-| 31 | 31-auto-relay-test-feature.png | Rule test dialog with matched messages | Features - Auto-Replay System |
-| 32 | 32-replay-all-messages.png | Replay All confirmation with warnings | Features - Batch Replay |
-| 33 | 33-replay-all-process.png | Batch replay in progress | Features - Batch Replay |
-| 34 | 34-post-replay-all-messages.png | Results after batch replay | Features - Batch Replay |
-| 35 | 35-rdlq-intelligence-history-post-replay-all.png | DLQ history audit trail after bulk replay | Features - DLQ Intelligence |
-
-**Total screenshots: 35 files**
-
----
-
-## ๐ฏ Key Screenshots Explained
-
-### DLQ Intelligence System (Screenshots 26-35)
-
-**New Feature Area:** DLQ Intelligence & Auto-Replay System
-
-#### Enhanced Message UI (Screenshot 26)
-**File:** `26-row-ui-new-feature.png`
-- Improved message list visual hierarchy
-- Better property visibility
-- Enhanced spacing and readability
-
-#### DLQ Intelligence Dashboard (Screenshots 27-28)
-**Files:** `27-dlq-enhancement.png`, `28-dlq-intelligence.png`
-- Persistent DLQ message tracking in SQLite database
-- Category classification (Transient, MaxDelivery, Expired, DataQuality, etc.)
-- "Scan Now" button for instant DLQ polling
-- Export capabilities (JSON/CSV)
-- Timeline view with replay history
-
-#### Replay History Tracking (Screenshot 29)
-**File:** `29-dlq-history-post-replay-message.png`
-- Shows DLQ message status after replay
-- Timestamps and outcome tracking
-- Audit trail for compliance
-
-#### Auto-Replay Rules System (Screenshots 30-31)
-**Files:** `30-auto-replay-feature.png`, `31-auto-relay-test-feature.png`
-- Rule cards with live statistics (Pending/Replayed/Success)
-- Conditions builder (field, operator, value)
-- "Test" button to preview matched messages
-- Real-time evaluation against Active DLQ messages
-
-#### Batch Replay All (Screenshots 32-35)
-**Files:** `32-replay-all-messages.png`, `33-replay-all-process.png`, `34-post-replay-all-messages.png`, `35-rdlq-intelligence-history-post-replay-all.png`
-- Confirmation dialog with red danger header and 3 safety warnings
-- Shows matched message count before execution
-- Real-time progress indicator
-- Post-replay statistics (matched/replayed/failed/skipped)
-- Complete audit trail in DLQ Intelligence history
-
----
-
-### Hero Image (Most Important)
-**File:** `05-main-message-display1.png`
-
-**Shows:**
-- ServiceHub message browser with loaded messages
-- Active (50) and Dead-Letter (0) tabs
-- AI Findings: 2 indicator
-- Auto-refresh toggle (ON, 7s ago)
-- Filter, Search, and Refresh buttons
-- Message list with preview text and timestamps
-
-**Why it's the hero:** This single screenshot demonstrates ServiceHub's complete value proposition - browsing real messages with AI insights during an incident.
-
-**Used in 7+ sections:**
-- Hero (top of README)
-- Problem section (after state)
-- Quick Start Step 4
-- Features - Message Browser
-- Roadmap current capabilities
-- Get Started CTA
-- Comparison sections
-
----
-
-### Critical Workflow Screenshots
-
-#### DLQ Investigation (4-step workflow)
-1. **Step 1:** `18-feature-dlq-tab-with-ai.png` - Shows 3 messages in DLQ with AI badges
-2. **Step 2:** `19-workflow-dlq-investigation-step1.png` - DLQ message Properties showing TestingDLQ reason
-3. **Step 3:** `20-workflow-dlq-investigation-step2.png` - AI Insights showing 88% confidence pattern
-4. **Step 4:** `21-workflow-dlq-replay-step3.png` - Replay confirmation dialog
-5. **Step 5:** `22-workflow-dlq-replay-step4.png` - After replay (message counts increased)
-
-#### Message Inspection (3-tab view)
-1. **Properties:** `11-feature-message-details-properties.png` - Message ID, timestamps, delivery count
-2. **Custom Props:** `12-feature-message-details-custom-props.png` - Application headers
-3. **Body:** `13-feature-message-details-body.png` - JSON message content
-
----
-
-## ๐ README Structure & Screenshot Usage
-
-### Hero Section (Line 1-15)
-- **1 screenshot:** `08-hero-message-browser-loaded.png`
-- **Purpose:** Immediate visual impact showing full capabilities
-
-### The Problem (Line 32-75)
-- **2 screenshots:**
- - `01-problem-empty-state.png` (Azure Portal limitations)
- - `08-hero-message-browser-loaded.png` (ServiceHub solution)
-- **Purpose:** Visual before/after comparison
-
-### Quick Start (Line 77-125)
-- **5 screenshots:**
- - `02-quickstart-connection-form.png` (Step 3)
- - `03-quickstart-connected-namespace.png` (Step 3)
- - `08-hero-message-browser-loaded.png` (Step 4)
- - `07-feature-message-generator-scenarios.png` (Step 4)
-- **Purpose:** Visual step-by-step setup guide
-
-### Key Features (Line 127-550)
-- **15+ screenshots** demonstrating:
- - Message browser (`08-hero...`)
- - Message details (3 tabs: `11`, `12`, `13`)
- - Search (`23-feature-search...`)
- - DLQ forensics (`18`, `24`)
- - AI insights (4 screenshots: `14`, `15`, `16`, `17`)
- - Message generator (`06`, `07`)
- - Send message (`09`)
- - Replay (`21`, `22`)
-
-### Investigation Workflows (Line 552-650)
-- **8 screenshots** showing:
- - DLQ investigation (5 screenshots)
- - Topic delivery (`10-workflow...`)
- - Search functionality (`23-feature...`)
-
-### Comparison (Line 652-750)
-- **2 screenshots:**
- - `01-problem-empty-state.png` (Portal)
- - `08-hero-message-browser-loaded.png` (ServiceHub)
-
-### Security & Trust (Line 752-850)
-- **1 screenshot:**
- - `02-quickstart-connection-form.png` (Security guidance)
-
-### Roadmap (Line 900-950)
-- **1 screenshot:**
- - `08-hero-message-browser-loaded.png` (Current capabilities)
-
-### Get Started (Line 1000-1100)
-- **1 screenshot:**
- - `08-hero-message-browser-loaded.png` (CTA)
-
----
-
-## โ
Verification Checklist
-
-Use this to verify screenshots display correctly on GitHub:
-
-- [ ] **Hero image** loads immediately at top
-- [ ] **Problem section** shows before/after comparison
-- [ ] **Quick Start** shows connection flow (4 steps)
-- [ ] **Features** demonstrates each capability with real screenshot
-- [ ] **DLQ workflow** shows clear 4-step progression
-- [ ] **AI insights** displays pattern detection screenshots
-- [ ] **Message details** shows all 3 tabs (Properties, Custom, Body)
-- [ ] **Comparison** shows Portal vs ServiceHub side-by-side
-- [ ] All images load in <2 seconds
-- [ ] No broken image icons (๐ผ๏ธโ)
-
----
-
-## ๐ง Maintenance
-
-### Adding New Screenshots
-
-1. Take screenshot of new feature
-2. Save to `docs/screenshots/` with naming convention:
- - Format: `XX-category-description.png`
- - Categories: `feature`, `workflow`, `comparison`, `security`
- - Example: `25-feature-bulk-replay.png`
-
-3. Update README.md with new image reference:
- ```markdown
- 
- *Caption explaining what screenshot shows*
- ```
-
-4. Commit and push:
- ```bash
- git add docs/screenshots/25-feature-bulk-replay.png README.md
- git commit -m "docs: Add bulk replay feature screenshot"
- git push origin main
- ```
-
-### Updating Existing Screenshots
-
-1. Replace file in `docs/screenshots/` (keep same filename)
-2. Commit and push:
- ```bash
- git add docs/screenshots/08-hero-message-browser-loaded.png
- git commit -m "docs: Update hero image with latest UI"
- git push origin main
- ```
-
-3. GitHub will automatically display updated image
-
----
-
-## ๐ Screenshot Statistics
-
-| Metric | Value |
-|--------|-------|
-| **Total screenshots** | 35 files |
-| **Most used screenshot** | `05-main-message-display1.png` (message browser) |
-| **Total size** | ~6.8 MB (estimated) |
-| **Average file size** | ~195 KB |
-| **Largest file** | ~350 KB |
-| **Smallest file** | ~85 KB |
-| **Recommended max size** | 500 KB per file |
-
----
-
-## ๐จ Screenshot Best Practices
-
-### When Taking Screenshots
-
-1. **Full window** (not partial crops)
-2. **Realistic data** (show 10-50 messages, not 1000s)
-3. **No sensitive data** (use test namespaces, generated messages)
-4. **Consistent resolution** (1200-1600px width)
-5. **Clean UI** (close unnecessary panels)
-
-### File Format
-
-- **Format:** PNG (for crisp UI text)
-- **Compression:** Use pngquant (65-80% quality)
-- **Max size:** 500 KB per file
-- **Naming:** Descriptive (`feature-name.png`, not `Screenshot 1.png`)
-
-### Alt Text
-
-Write descriptive alt text for accessibility:
-
-```markdown
-
-
-
-
-
-
-```
-
----
-
-## ๐ Quick Commands
-
-```bash
-# View all screenshots
-ls -lh docs/screenshots/
-
-# Count screenshots
-ls -1 docs/screenshots/ | wc -l
-
-# Check file sizes
-ls -lh docs/screenshots/*.png | sort -k5 -hr
-
-# Find large files (>500KB)
-find docs/screenshots -name "*.png" -size +500k
-
-# Optimize all screenshots
-cd docs/screenshots
-for img in *.png; do
- pngquant --quality=65-80 --ext .png --force "$img"
-done
-```
-
----
-
-## ๐ View on GitHub
-
-**Repository:** https://github.com/debdevops/servicehub
-
-**Branch:** dglocal-110126
-
-**README:** https://github.com/debdevops/servicehub/blob/dglocal-110126/README.md
-
-All 24 screenshots should display inline without broken image icons.
-
----
-
-**Last updated:** February 15, 2026
-**Screenshots version:** v2.0 (DLQ Intelligence & Auto-Replay System added)
diff --git a/docs/screenshots/01-connect-page.png b/docs/screenshots/01-connect-page.png
new file mode 100644
index 0000000000000000000000000000000000000000..2be589c0588f1df1f72fb9cab888f1d81e4f1b43
GIT binary patch
literal 504732
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