Core Flows: Operational Monitoring & User Experience#
Core Flows: Operational Monitoring & User Experience#
This specification documents the core user flows for CipherSwarm V2 Operational Excellence, focusing on improving visibility, feedback, and troubleshooting capabilities for a solo developer managing a production system.
Flow 1: Agent Fleet Monitoring#
Description: Monitor the health and activity of all agents in the system at a glance, with ability to drill down into individual agent details for troubleshooting.
Trigger: User clicks "Agents" in sidebar navigation
Flow Steps:
-
Agent List Page Loads
- Skeleton screen appears while data loads
- Table populates with agent rows showing: State, Name, Projects, Client Signature, Enabled, OS, Last Seen
- New columns added: Status Badge (🟢 Online / 🟡 Idle / 🔴 Offline), Hash Rate, Error Indicator (red badge with count if errors exist)
- Turbo Stream updates status badges, hash rates, and error indicators in real-time (scoped to data cells only, not entire table)
-
User Scans Agent List
- Quickly identifies problematic agents by red error badges
- Sees which agents are actively working (hash rate > 0)
- Identifies offline agents by status badge
-
User Clicks Agent Name to Investigate
- Navigates to agent detail page
- Skeleton screen shows while loading
-
Agent Detail Page Loads (Tabbed Interface)
- Overview Tab (default):
- Current task section (if working): Shows task name, campaign, attack, with link to task detail page
- Performance metrics: Current hash rate, temperature, device utilization
- Quick stats: Total tasks completed, success rate, last seen timestamp
- Errors Tab:
- Error log table: Timestamp, Severity (color-coded), Message, Task (if applicable)
- Sorted by most recent first
- Shows last 50 errors
- Configuration Tab:
- Agent settings: Projects assigned, enabled/disabled status
- Device information: GPU/CPU details
- Capabilities Tab:
- Supported hash types (from benchmarks)
- Benchmark data table: Hash type, speed, date
- Overview Tab (default):
-
User Clicks Current Task Link
- Navigates to task detail page (Flow 3)
-
Real-time Updates
- Turbo Streams update current task, performance metrics, and error log as changes occur
- Updates scoped to specific sections (not entire page or form inputs)
Exit Points:
- Navigate to task detail page
- Return to agent list
- Navigate to related campaign
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui; margin: 20px; background: #f5f5f5; }
.page { background: white; padding: 20px; border-radius: 8px; }
h2 { margin: 0 0 20px 0; font-size: 24px; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
th { background: #f8f9fa; font-weight: 600; }
.badge { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: 600; }
.badge-success { background: #d4edda; color: #155724; }
.badge-warning { background: #fff3cd; color: #856404; }
.badge-danger { background: #f8d7da; color: #721c24; }
.status-online { color: #28a745; }
.status-idle { color: #ffc107; }
.status-offline { color: #dc3545; }
.error-badge { background: #dc3545; color: white; padding: 2px 6px; border-radius: 10px; font-size: 11px; margin-left: 8px; }
</style>
</head>
<body>
<div class="page">
<h2>Agents</h2>
<button data-element-id="new-agent-btn" style="padding: 8px 16px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;">+ New Agent</button>
<table>
<thead>
<tr>
<th>Status</th>
<th>Name</th>
<th>Hash Rate</th>
<th>Errors</th>
<th>Projects</th>
<th>OS</th>
<th>Last Seen</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="status-online">🟢 Online</span></td>
<td><a href="#" data-element-id="agent-link-1">cracker-01</a></td>
<td>2.5 GH/s</td>
<td>—</td>
<td>Project Alpha</td>
<td>Linux</td>
<td>2 minutes ago</td>
<td><a href="#" data-element-id="edit-agent-1">Edit</a></td>
</tr>
<tr>
<td><span class="status-idle">🟡 Idle</span></td>
<td><a href="#" data-element-id="agent-link-2">cracker-02</a> <span class="error-badge">3</span></td>
<td>0 H/s</td>
<td><span class="badge-danger">3 errors</span></td>
<td>Project Alpha, Beta</td>
<td>Windows</td>
<td>5 minutes ago</td>
<td><a href="#" data-element-id="edit-agent-2">Edit</a></td>
</tr>
<tr>
<td><span class="status-offline">🔴 Offline</span></td>
<td><a href="#" data-element-id="agent-link-3">cracker-03</a></td>
<td>—</td>
<td>—</td>
<td>All Projects</td>
<td>Linux</td>
<td>2 hours ago</td>
<td><a href="#" data-element-id="edit-agent-3">Edit</a></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui; margin: 20px; background: #f5f5f5; }
.page { background: white; padding: 20px; border-radius: 8px; max-width: 1200px; }
h2 { margin: 0 0 20px 0; font-size: 24px; }
.breadcrumb { margin-bottom: 20px; color: #6c757d; font-size: 14px; }
.tabs { display: flex; border-bottom: 2px solid #dee2e6; margin-bottom: 20px; }
.tab { padding: 12px 24px; cursor: pointer; border: none; background: none; font-size: 16px; }
.tab.active { border-bottom: 3px solid #007bff; color: #007bff; font-weight: 600; margin-bottom: -2px; }
.section { margin-bottom: 30px; }
.section h3 { font-size: 18px; margin-bottom: 12px; color: #495057; }
.card { background: #f8f9fa; padding: 16px; border-radius: 6px; margin-bottom: 12px; }
.metric { display: inline-block; margin-right: 30px; }
.metric-label { font-size: 12px; color: #6c757d; text-transform: uppercase; }
.metric-value { font-size: 24px; font-weight: 600; color: #212529; }
.task-link { color: #007bff; text-decoration: none; font-weight: 500; }
.error-table { width: 100%; border-collapse: collapse; }
.error-table th, .error-table td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
.error-table th { background: #f8f9fa; font-weight: 600; font-size: 14px; }
.severity-error { color: #dc3545; font-weight: 600; }
.severity-warning { color: #ffc107; font-weight: 600; }
.severity-info { color: #17a2b8; font-weight: 600; }
</style>
</head>
<body>
<div class="page">
<div class="breadcrumb">Dashboard > Agents > cracker-02</div>
<h2>Agent: cracker-02</h2>
<div class="tabs">
<button class="tab active" data-element-id="tab-overview">Overview</button>
<button class="tab" data-element-id="tab-errors">Errors (3)</button>
<button class="tab" data-element-id="tab-config">Configuration</button>
<button class="tab" data-element-id="tab-capabilities">Capabilities</button>
</div>
<div class="section">
<h3>Current Activity</h3>
<div class="card">
<strong>Task #1247:</strong> <a href="#" class="task-link" data-element-id="current-task-link">Dictionary Attack - Campaign: Password Audit</a><br>
<span style="color: #6c757d; font-size: 14px;">Started 45 minutes ago • 23% complete • ETA: 2h 15m</span>
</div>
</div>
<div class="section">
<h3>Performance Metrics</h3>
<div class="metric">
<div class="metric-label">Hash Rate</div>
<div class="metric-value">2.5 GH/s</div>
</div>
<div class="metric">
<div class="metric-label">Temperature</div>
<div class="metric-value">72°C</div>
</div>
<div class="metric">
<div class="metric-label">Utilization</div>
<div class="metric-value">85%</div>
</div>
</div>
<div class="section">
<h3>Quick Stats</h3>
<div class="card">
<strong>Total Tasks Completed:</strong> 156<br>
<strong>Success Rate:</strong> 94.2%<br>
<strong>Last Seen:</strong> 2 minutes ago
</div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui; margin: 20px; background: #f5f5f5; }
.page { background: white; padding: 20px; border-radius: 8px; max-width: 1200px; }
h2 { margin: 0 0 20px 0; font-size: 24px; }
.tabs { display: flex; border-bottom: 2px solid #dee2e6; margin-bottom: 20px; }
.tab { padding: 12px 24px; cursor: pointer; border: none; background: none; font-size: 16px; }
.tab.active { border-bottom: 3px solid #007bff; color: #007bff; font-weight: 600; margin-bottom: -2px; }
.error-table { width: 100%; border-collapse: collapse; margin-top: 20px; }
.error-table th, .error-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
.error-table th { background: #f8f9fa; font-weight: 600; }
.severity-fatal { color: #dc3545; font-weight: 600; }
.severity-error { color: #fd7e14; font-weight: 600; }
.severity-warning { color: #ffc107; font-weight: 600; }
.severity-info { color: #17a2b8; font-weight: 600; }
</style>
</head>
<body>
<div class="page">
<h2>Agent: cracker-02</h2>
<div class="tabs">
<button class="tab" data-element-id="tab-overview">Overview</button>
<button class="tab active" data-element-id="tab-errors">Errors (3)</button>
<button class="tab" data-element-id="tab-config">Configuration</button>
<button class="tab" data-element-id="tab-capabilities">Capabilities</button>
</div>
<table class="error-table">
<thead>
<tr>
<th>Timestamp</th>
<th>Severity</th>
<th>Message</th>
<th>Task</th>
</tr>
</thead>
<tbody>
<tr>
<td>2024-01-06 14:32:15</td>
<td><span class="severity-error">ERROR</span></td>
<td>GPU temperature exceeded threshold (85°C), throttling enabled</td>
<td><a href="#" data-element-id="error-task-link-1">Task #1247</a></td>
</tr>
<tr>
<td>2024-01-06 13:15:42</td>
<td><span class="severity-warning">WARNING</span></td>
<td>Task assignment delayed due to benchmark age</td>
<td>—</td>
</tr>
<tr>
<td>2024-01-06 12:08:33</td>
<td><span class="severity-info">INFO</span></td>
<td>Task skipped: does not meet performance threshold for hash type 1000</td>
<td>—</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
Flow 2: Campaign Progress Monitoring#
Description: Monitor campaign execution, track attack progress, identify failures, and view cracked hashes.
Trigger: User clicks "Activity" in sidebar or clicks a campaign name from the list
Flow Steps:
-
Campaign Detail Page Loads
- Skeleton screen appears while data loads
- Page header shows campaign name, priority, description
- Attack stepper component displays with progress bars
-
User Views Attack Progress
- Each attack shows:
- Attack name and type (Dictionary, Mask, Hybrid)
- Progress bar (0-100%) with percentage text
- ETA text below progress bar: "ETA: 2h 30m" or "Completed" or "Failed"
- Error indicator (⚠️ red icon) if attack failed
- Turbo Streams update progress bars and ETAs in real-time
- Each attack shows:
-
User Identifies Failed Attack
- Red error indicator (⚠️) visible on failed attack
- User clicks error indicator or attack name
-
Error Details Revealed
- Modal dialog opens showing error details:
- Error message: "Attack failed: Invalid mask pattern in mask list"
- Timestamp when error occurred
- Affected task ID (clickable link)
- Suggested actions: "Check mask list configuration" or "Retry attack"
- User can close modal or click task link for more details
- Modal dialog opens showing error details:
-
User Views Recent Cracks
- Button at bottom of page: "View Recent Cracks (12 in last 24h)"
- User clicks button
- Section expands showing table of cracked hashes (max 100):
- Hash value
- Plaintext (always visible, not masked)
- Cracked timestamp
- Attack that cracked it
- Turbo Streams add new cracks to top of list as they occur
-
Error Log Section (Bottom of Page)
- "Campaign Errors" section shows recent errors across all attacks
- Table: Timestamp, Attack, Error Message
- Helps identify patterns or systemic issues
Exit Points:
- Navigate to attack detail (if needed)
- Navigate to hash list to see all results
- Return to campaign list
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui; margin: 20px; background: #f5f5f5; }
.page { background: white; padding: 20px; border-radius: 8px; max-width: 1000px; }
h2 { margin: 0 0 10px 0; font-size: 24px; }
.meta { color: #6c757d; font-size: 14px; margin-bottom: 20px; }
.attack-list { margin-top: 30px; }
.attack-item { background: #f8f9fa; padding: 20px; border-radius: 6px; margin-bottom: 16px; border-left: 4px solid #007bff; }
.attack-item.failed { border-left-color: #dc3545; }
.attack-item.completed { border-left-color: #28a745; }
.attack-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.attack-name { font-size: 16px; font-weight: 600; }
.error-icon { color: #dc3545; font-size: 20px; }
.progress-bar { width: 100%; height: 24px; background: #e9ecef; border-radius: 4px; overflow: hidden; margin-bottom: 8px; }
.progress-fill { height: 100%; background: #007bff; transition: width 0.3s; }
.progress-text { font-size: 14px; color: #495057; }
.eta-text { font-size: 13px; color: #6c757d; }
.cracks-section { margin-top: 40px; padding-top: 20px; border-top: 2px solid #dee2e6; }
.expand-btn { padding: 10px 20px; background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; cursor: pointer; }
.crack-table { width: 100%; border-collapse: collapse; margin-top: 16px; }
.crack-table th, .crack-table td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; font-size: 13px; }
.crack-table th { background: #f8f9fa; font-weight: 600; }
</style>
</head>
<body>
<div class="page">
<h2>Campaign: Password Audit 2024</h2>
<div class="meta">Priority: Urgent • Created 3 days ago • Hash List: corporate_ntlm.txt (1,000 hashes)</div>
<div class="attack-list">
<div class="attack-item completed">
<div class="attack-header">
<span class="attack-name">Attack 1: Common Passwords (Dictionary)</span>
<span style="color: #28a745; font-weight: 600;">✓ Completed</span>
</div>
<div class="progress-bar"><div class="progress-fill" style="width: 100%; background: #28a745;"></div></div>
<div class="progress-text">100% complete</div>
<div class="eta-text">Completed 2 hours ago • 234 hashes cracked</div>
</div>
<div class="attack-item">
<div class="attack-header">
<span class="attack-name">Attack 2: Rules + Wordlist (Dictionary)</span>
<span style="color: #007bff; font-weight: 600;">⚡ Running</span>
</div>
<div class="progress-bar"><div class="progress-fill" style="width: 45%;"></div></div>
<div class="progress-text">45% complete</div>
<div class="eta-text">ETA: 2h 30m • 3 agents working</div>
</div>
<div class="attack-item failed">
<div class="attack-header">
<span class="attack-name">Attack 3: Mask Attack</span>
<span class="error-icon" data-element-id="error-indicator">⚠️</span>
<span style="color: #dc3545; font-weight: 600;">Failed</span>
</div>
<div class="progress-bar"><div class="progress-fill" style="width: 12%; background: #dc3545;"></div></div>
<div class="progress-text">12% complete (failed)</div>
<div class="eta-text" style="color: #dc3545;">Error: Invalid mask pattern - check mask list configuration</div>
</div>
</div>
<div class="cracks-section">
<button class="expand-btn" data-element-id="expand-cracks-btn">📊 View Recent Cracks (12 in last 24h)</button>
<table class="crack-table" style="display: none;" data-element-id="cracks-table">
<thead>
<tr>
<th>Hash</th>
<th>Plaintext</th>
<th>Cracked</th>
<th>Attack</th>
</tr>
</thead>
<tbody>
<tr>
<td>5f4dcc3b5aa765d61d8327deb882cf99</td>
<td>password</td>
<td>2 hours ago</td>
<td>Attack 1</td>
</tr>
<tr>
<td>e10adc3949ba59abbe56e057f20f883e</td>
<td>123456</td>
<td>2 hours ago</td>
<td>Attack 1</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
Flow 3: Task Detail Investigation#
Description: View detailed information about a specific task, including progress, status history, and errors.
Trigger: User clicks on a task link from agent detail page or campaign page
Flow Steps:
-
Task Detail Page Loads
- Skeleton screen appears while data loads
- Breadcrumb shows: Dashboard > Agents > [Agent Name] > Task #[ID] (or via Campaign path)
-
Page Displays Comprehensive Task Information
-
Header Section:
- Task ID and state badge (Pending/Running/Completed/Failed)
- Associated campaign and attack (both clickable links)
- Assigned agent (clickable link)
- Start time, end time (if complete)
-
Progress Section:
- Progress bar with percentage
- Keyspace processed / total keyspace
- Current hash rate
- Estimated finish time (if running)
-
Status History Section:
- Table of recent status updates from hashcat
- Columns: Timestamp, Progress %, Hash Rate, Status
- Shows last 20 updates
-
Errors Section (if any):
- Error messages with timestamps
- Severity indicators
-
Crack Results Section (if any):
- Hashes cracked during this task
- Table: Hash, Plaintext, Timestamp
-
-
Real-time Updates
- Turbo Streams update progress bar, hash rate, and status history as task executes
- New crack results appear in real-time
-
User Actions Available:
- Cancel Task button (if running or pending) - stops task execution
- Retry Task button (if failed) - creates new task with same parameters
- Reassign Task button (if stuck) - allows selecting different agent
- View Logs button - shows full hashcat output in modal
- Download Results button (if completed) - downloads cracked hashes for this task
- Navigate to campaign (to see broader context)
- Navigate to agent (to see agent status)
- Navigate to attack (to see attack configuration)
Exit Points:
- Navigate to campaign detail
- Navigate to agent detail
- Return to previous page
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui; margin: 20px; background: #f5f5f5; }
.page { background: white; padding: 20px; border-radius: 8px; max-width: 1000px; }
h2 { margin: 0 0 10px 0; font-size: 24px; }
.breadcrumb { margin-bottom: 20px; color: #6c757d; font-size: 14px; }
.badge { display: inline-block; padding: 6px 12px; border-radius: 4px; font-size: 14px; font-weight: 600; margin-left: 12px; }
.badge-running { background: #cfe2ff; color: #084298; }
.badge-pending { background: #e2e3e5; color: #41464b; }
.actions { margin: 20px 0; display: flex; gap: 12px; }
.btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 600; }
.btn-danger { background: #dc3545; color: white; }
.btn-primary { background: #007bff; color: white; }
.btn-secondary { background: #6c757d; color: white; }
.section { margin-top: 30px; }
.section h3 { font-size: 18px; margin-bottom: 12px; color: #495057; border-bottom: 2px solid #dee2e6; padding-bottom: 8px; }
.info-grid { display: grid; grid-template-columns: 200px 1fr; gap: 12px; margin-bottom: 20px; }
.info-label { font-weight: 600; color: #495057; }
.info-value { color: #212529; }
.progress-bar { width: 100%; height: 32px; background: #e9ecef; border-radius: 6px; overflow: hidden; margin: 16px 0; }
.progress-fill { height: 100%; background: #007bff; display: flex; align-items: center; padding-left: 12px; color: white; font-weight: 600; }
.metrics { display: flex; gap: 30px; margin: 16px 0; }
.metric { text-align: center; }
.metric-value { font-size: 28px; font-weight: 600; color: #212529; }
.metric-label { font-size: 12px; color: #6c757d; text-transform: uppercase; margin-top: 4px; }
table { width: 100%; border-collapse: collapse; margin-top: 12px; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; font-size: 14px; }
th { background: #f8f9fa; font-weight: 600; }
</style>
</head>
<body>
<div class="page">
<div class="breadcrumb">Dashboard > Agents > cracker-02 > Task #1247</div>
<h2>Task #1247 <span class="badge badge-running">Running</span></h2>
<div class="actions">
<button class="btn btn-danger" data-element-id="cancel-task-btn">Cancel Task</button>
<button class="btn btn-secondary" data-element-id="reassign-task-btn">Reassign to Agent</button>
<button class="btn btn-secondary" data-element-id="view-logs-btn">View Logs</button>
<button class="btn btn-primary" data-element-id="download-results-btn">Download Results</button>
</div>
<div class="info-grid">
<div class="info-label">Campaign:</div>
<div class="info-value"><a href="#" data-element-id="campaign-link">Password Audit 2024</a></div>
<div class="info-label">Attack:</div>
<div class="info-value"><a href="#" data-element-id="attack-link">Attack 2: Rules + Wordlist (Dictionary)</a></div>
<div class="info-label">Agent:</div>
<div class="info-value"><a href="#" data-element-id="agent-link">cracker-02</a></div>
<div class="info-label">Started:</div>
<div class="info-value">45 minutes ago (2024-01-06 13:47:00)</div>
</div>
<div class="section">
<h3>Progress</h3>
<div class="progress-bar">
<div class="progress-fill" style="width: 45%;">45%</div>
</div>
<div class="metrics">
<div class="metric">
<div class="metric-value">2.5 GH/s</div>
<div class="metric-label">Hash Rate</div>
</div>
<div class="metric">
<div class="metric-value">2h 30m</div>
<div class="metric-label">ETA</div>
</div>
<div class="metric">
<div class="metric-value">450M / 1B</div>
<div class="metric-label">Keyspace</div>
</div>
</div>
</div>
<div class="section">
<h3>Recent Status Updates</h3>
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>Progress</th>
<th>Hash Rate</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>14:32:15</td>
<td>45%</td>
<td>2.5 GH/s</td>
<td>Running</td>
</tr>
<tr>
<td>14:31:15</td>
<td>44%</td>
<td>2.6 GH/s</td>
<td>Running</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
Flow 4: System Health Monitoring#
Description: Check the health status of all system services (PostgreSQL, Redis, MinIO) and access diagnostic tools.
Trigger: User clicks "System Health" in sidebar navigation
Flow Steps:
-
System Health Page Loads
- Skeleton screen appears while checking service status
- Page displays service status cards
-
Service Status Cards Display
-
PostgreSQL Card:
- Status indicator: 🟢 Healthy / 🔴 Down
- Metrics: Connection count, query latency, database size
- Link: "View slow queries" (if admin)
-
Redis Card:
- Status indicator: 🟢 Healthy / 🔴 Down
- Metrics: Memory usage, connected clients, cache hit rate
- Link: "View Sidekiq Dashboard"
-
MinIO Card:
- Status indicator: 🟢 Healthy / 🔴 Down
- Metrics: Storage used, bucket count, API latency
- Link: "Open MinIO Console" (external)
-
Application Card:
- Rails version, uptime, worker status
- Link: "View Rails Logs"
-
-
User Identifies Issue
- Red status indicator on a service
- Error message displayed: "Redis connection failed: Connection refused"
- Last successful check timestamp shown
-
User Accesses Diagnostic Tools
- Clicks diagnostic link (e.g., "View Sidekiq Dashboard")
- Opens in new tab or navigates to tool
- Returns to health page to verify fix
-
Real-time Updates
- Turbo Streams update service status indicators and metrics every 30 seconds
- Critical errors trigger immediate updates
Exit Points:
- Navigate to diagnostic tools (Sidekiq, logs)
- Return to dashboard or other pages
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui; margin: 20px; background: #f5f5f5; }
.page { background: white; padding: 20px; border-radius: 8px; max-width: 1200px; }
h2 { margin: 0 0 20px 0; font-size: 24px; }
.services-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-top: 20px; }
.service-card { background: #f8f9fa; padding: 20px; border-radius: 8px; border: 2px solid #dee2e6; }
.service-card.healthy { border-color: #28a745; }
.service-card.error { border-color: #dc3545; }
.service-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.service-name { font-size: 18px; font-weight: 600; }
.status-indicator { font-size: 24px; }
.metrics { margin: 16px 0; }
.metric-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #dee2e6; }
.metric-label { color: #6c757d; font-size: 14px; }
.metric-value { font-weight: 600; color: #212529; }
.diagnostic-links { margin-top: 16px; padding-top: 16px; border-top: 1px solid #dee2e6; }
.diagnostic-link { color: #007bff; text-decoration: none; font-size: 14px; margin-right: 16px; }
.error-message { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 4px; margin-top: 12px; font-size: 14px; }
</style>
</head>
<body>
<div class="page">
<h2>System Health</h2>
<div style="color: #6c757d; font-size: 14px; margin-bottom: 20px;">Last updated: 2 minutes ago</div>
<div class="services-grid">
<div class="service-card healthy">
<div class="service-header">
<span class="service-name">PostgreSQL</span>
<span class="status-indicator">🟢</span>
</div>
<div class="metrics">
<div class="metric-row">
<span class="metric-label">Status</span>
<span class="metric-value">Healthy</span>
</div>
<div class="metric-row">
<span class="metric-label">Connections</span>
<span class="metric-value">12 / 100</span>
</div>
<div class="metric-row">
<span class="metric-label">Query Latency</span>
<span class="metric-value">2.3ms</span>
</div>
<div class="metric-row">
<span class="metric-label">Database Size</span>
<span class="metric-value">1.2 GB</span>
</div>
</div>
<div class="diagnostic-links">
<a href="#" class="diagnostic-link" data-element-id="pg-slow-queries">View Slow Queries</a>
</div>
</div>
<div class="service-card healthy">
<div class="service-header">
<span class="service-name">Redis</span>
<span class="status-indicator">🟢</span>
</div>
<div class="metrics">
<div class="metric-row">
<span class="metric-label">Status</span>
<span class="metric-value">Healthy</span>
</div>
<div class="metric-row">
<span class="metric-label">Memory Usage</span>
<span class="metric-value">245 MB / 2 GB</span>
</div>
<div class="metric-row">
<span class="metric-label">Connected Clients</span>
<span class="metric-value">8</span>
</div>
<div class="metric-row">
<span class="metric-label">Cache Hit Rate</span>
<span class="metric-value">87.3%</span>
</div>
</div>
<div class="diagnostic-links">
<a href="#" class="diagnostic-link" data-element-id="sidekiq-link">Sidekiq Dashboard</a>
</div>
</div>
<div class="service-card error">
<div class="service-header">
<span class="service-name">MinIO</span>
<span class="status-indicator">🔴</span>
</div>
<div class="metrics">
<div class="metric-row">
<span class="metric-label">Status</span>
<span class="metric-value" style="color: #dc3545;">Connection Failed</span>
</div>
<div class="metric-row">
<span class="metric-label">Last Successful Check</span>
<span class="metric-value">15 minutes ago</span>
</div>
</div>
<div class="error-message">
⚠️ Connection refused: Unable to connect to MinIO at http://minio:9000
</div>
<div class="diagnostic-links">
<a href="#" class="diagnostic-link" data-element-id="minio-console">MinIO Console</a>
<a href="#" class="diagnostic-link" data-element-id="minio-logs">View Logs</a>
</div>
</div>
<div class="service-card healthy">
<div class="service-header">
<span class="service-name">Application</span>
<span class="status-indicator">🟢</span>
</div>
<div class="metrics">
<div class="metric-row">
<span class="metric-label">Rails Version</span>
<span class="metric-value">8.0.1</span>
</div>
<div class="metric-row">
<span class="metric-label">Uptime</span>
<span class="metric-value">3 days 14h</span>
</div>
<div class="metric-row">
<span class="metric-label">Sidekiq Workers</span>
<span class="metric-value">5 active</span>
</div>
<div class="metric-row">
<span class="metric-label">Job Queue</span>
<span class="metric-value">12 pending</span>
</div>
</div>
<div class="diagnostic-links">
<a href="#" class="diagnostic-link" data-element-id="rails-logs">View Rails Logs</a>
</div>
</div>
</div>
</div>
</body>
</html>
Flow 5: Campaign Creation Workflow#
Description: Create a new campaign and immediately add the first attack to begin cracking operations.
Trigger: User clicks "+ New Campaign" button on Activity page
Flow Steps:
-
Campaign Creation Form Loads
- Form displays with fields:
- Campaign name (required)
- Description (optional)
- Priority level (dropdown: Routine, Priority, Urgent, etc.)
- Hash list selection (dropdown of existing hash lists)
- Project selection (dropdown of user's projects)
- Skeleton screen while hash lists load
- Form displays with fields:
-
User Fills Form
- Enters campaign name: "Password Audit 2024"
- Selects hash list from dropdown
- Selects priority: "Urgent"
- Clicks "Create Campaign" button
-
Form Submits
- Loading spinner appears on submit button
- Button text changes to "Creating..."
- Form inputs disabled during submission
-
Campaign Created Successfully
- Toast notification appears: "✓ Campaign created successfully"
- Immediately redirects to "Add Attack" form (not campaign detail page)
- Attack form pre-populated with campaign context
-
Add First Attack Form
- Attack type selector: Dictionary / Mask / Hybrid Dictionary / Hybrid Mask
- User selects attack type
- Type-specific form appears (e.g., wordlist + rules for dictionary)
- User configures attack parameters
- Clicks "Create Attack"
-
Attack Created
- Toast notification: "✓ Attack added to campaign"
- Redirects to campaign detail page
- Campaign page shows newly created attack in stepper
Exit Points:
- Campaign detail page (after adding first attack)
- Can add more attacks from campaign page
- Can navigate away during creation (changes lost)
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui; margin: 20px; background: #f5f5f5; }
.page { background: white; padding: 20px; border-radius: 8px; max-width: 800px; }
h2 { margin: 0 0 20px 0; font-size: 24px; }
.form-group { margin-bottom: 20px; }
label { display: block; font-weight: 600; margin-bottom: 6px; color: #495057; }
input, select, textarea { width: 100%; padding: 10px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px; }
textarea { min-height: 80px; resize: vertical; }
.required { color: #dc3545; }
.help-text { font-size: 13px; color: #6c757d; margin-top: 4px; }
.button-group { display: flex; gap: 12px; margin-top: 30px; }
button { padding: 10px 24px; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; font-weight: 600; }
.btn-primary { background: #007bff; color: white; }
.btn-secondary { background: #6c757d; color: white; }
</style>
</head>
<body>
<div class="page">
<h2>Create New Campaign</h2>
<form data-element-id="campaign-form">
<div class="form-group">
<label>Campaign Name <span class="required">*</span></label>
<input type="text" placeholder="e.g., Password Audit 2024" data-element-id="campaign-name" required>
</div>
<div class="form-group">
<label>Description</label>
<textarea placeholder="Optional description of this campaign" data-element-id="campaign-description"></textarea>
</div>
<div class="form-group">
<label>Priority <span class="required">*</span></label>
<select data-element-id="campaign-priority" required>
<option value="">Select priority...</option>
<option value="deferred">Deferred (Best effort)</option>
<option value="routine" selected>Routine (Default)</option>
<option value="priority">Priority (Important)</option>
<option value="urgent">Urgent (Important & Urgent)</option>
<option value="immediate">Immediate (ASAP)</option>
<option value="flash">Flash (Critical, small hashes)</option>
</select>
<div class="help-text">Higher priority campaigns pause lower priority ones</div>
</div>
<div class="form-group">
<label>Hash List <span class="required">*</span></label>
<select data-element-id="hash-list-select" required>
<option value="">Select hash list...</option>
<option value="1">corporate_ntlm.txt (1,000 hashes - NTLM)</option>
<option value="2">linux_shadow.txt (500 hashes - SHA512)</option>
<option value="3">wifi_handshakes.hccapx (25 hashes - WPA2)</option>
</select>
</div>
<div class="form-group">
<label>Project <span class="required">*</span></label>
<select data-element-id="project-select" required>
<option value="">Select project...</option>
<option value="1">Project Alpha</option>
<option value="2">Project Beta</option>
</select>
</div>
<div class="button-group">
<button type="submit" class="btn-primary" data-element-id="create-campaign-btn">Create Campaign</button>
<button type="button" class="btn-secondary" data-element-id="cancel-btn">Cancel</button>
</div>
</form>
</div>
</body>
</html>
Flow 6: Error Investigation & Troubleshooting#
Description: Identify, investigate, and resolve errors across agents, campaigns, and tasks.
Trigger: User notices error indicator (red badge, warning icon, or failed status)
Flow Steps:
-
Error Discovery
- From Agent List: Red error badge shows "3" next to agent name
- From Campaign Page: Red ⚠️ icon on failed attack
- From System Health: Red 🔴 status on service card
-
User Clicks Error Indicator
- Agent Error: Navigates to agent detail page, Errors tab
- Campaign Error: Modal dialog opens with error details
- System Error: Error message visible on service card
-
Error Details Displayed
- Error Log Table Shows:
- Timestamp (when error occurred)
- Severity (Fatal/Error/Warning/Info) - color-coded
- Error message (descriptive, actionable)
- Related task (if applicable) - clickable link
- Sorted by most recent first
- Shows context needed for diagnosis
- Error Log Table Shows:
-
User Investigates Root Cause
- Clicks task link to see task details
- Reviews error message for clues
- Checks related resources (hash list, wordlist, etc.)
-
User Takes Corrective Action
- Fixes configuration issue
- Restarts agent (if needed)
- Retries failed task/attack
- Updates resources
-
Confirmation
- Toast notification: "✓ Changes saved"
- Error clears from list (if resolved)
- Status updates via Turbo Streams
Exit Points:
- Return to monitoring (agent list, campaign page)
- Navigate to related resources for fixes
- Check system health to verify resolution
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui; margin: 0; padding: 0; }
.modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000; }
.modal { background: white; border-radius: 8px; max-width: 600px; width: 90%; box-shadow: 0 4px 20px rgba(0,0,0,0.3); }
.modal-header { padding: 20px; border-bottom: 1px solid #dee2e6; display: flex; justify-content: space-between; align-items: center; }
.modal-title { font-size: 20px; font-weight: 600; color: #dc3545; display: flex; align-items: center; gap: 10px; }
.modal-close { background: none; border: none; font-size: 24px; color: #6c757d; cursor: pointer; padding: 0; }
.modal-body { padding: 20px; }
.error-detail { margin-bottom: 16px; }
.error-label { font-weight: 600; color: #495057; font-size: 14px; margin-bottom: 4px; }
.error-value { color: #212529; font-size: 14px; }
.error-message { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 4px; margin: 16px 0; }
.suggestion { background: #d1ecf1; color: #0c5460; padding: 12px; border-radius: 4px; margin: 16px 0; }
.modal-footer { padding: 20px; border-top: 1px solid #dee2e6; display: flex; justify-content: flex-end; gap: 12px; }
.btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 600; }
.btn-primary { background: #007bff; color: white; }
.btn-secondary { background: #6c757d; color: white; }
</style>
</head>
<body>
<div class="modal-overlay" data-element-id="error-modal-overlay">
<div class="modal">
<div class="modal-header">
<div class="modal-title">
<span style="font-size: 24px;">⚠️</span>
Attack Failed
</div>
<button class="modal-close" data-element-id="modal-close-btn">×</button>
</div>
<div class="modal-body">
<div class="error-detail">
<div class="error-label">Attack:</div>
<div class="error-value">Attack 3: Mask Attack</div>
</div>
<div class="error-detail">
<div class="error-label">Timestamp:</div>
<div class="error-value">2024-01-06 14:15:32</div>
</div>
<div class="error-detail">
<div class="error-label">Affected Task:</div>
<div class="error-value"><a href="#" data-element-id="task-link" style="color: #007bff;">Task #1248</a></div>
</div>
<div class="error-message">
<strong>Error:</strong> Invalid mask pattern in mask list - hashcat returned error code 1
</div>
<div class="suggestion">
<strong>💡 Suggested Action:</strong> Check mask list configuration and verify mask patterns are valid hashcat syntax
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-element-id="view-task-btn">View Task Details</button>
<button class="btn btn-primary" data-element-id="retry-attack-btn">Retry Attack</button>
</div>
</div>
</div>
</body>
</html>
Flow 7: Loading & Feedback Patterns (Cross-cutting)#
Description: Consistent feedback patterns used across all flows to communicate system state.
Loading States#
Initial Page Load:
- Skeleton screen displays showing page structure
- Placeholder boxes for content areas
- Maintains layout to prevent content shift
Data Refresh:
- Turbo Streams update specific sections
- No full-page reload
- Smooth transitions without flicker
Form Submission:
- Submit button shows spinner
- Button text changes: "Create Campaign" → "Creating..."
- Form inputs disabled
- Prevents double-submission
Success Feedback#
Action Completion:
- Toast notification appears (top-right corner)
- Green background with checkmark: "✓ Campaign created successfully"
- Auto-dismisses after 5 seconds
- Can be manually dismissed with X button
State Changes:
- Status badges update via Turbo Streams
- Progress bars animate smoothly
- Color changes reflect new state
Error Feedback#
Validation Errors:
- Red border on invalid input fields
- Error message below field: "Campaign name is required"
- Form cannot submit until fixed
Operation Errors:
- Toast notification with red background: "✗ Failed to create campaign: [reason]"
- Error persists until dismissed
- Actionable error messages when possible
System Errors:
- Error banner on affected page
- Clear description of problem
- Link to diagnostic tools or support
Real-time Updates (Turbo Streams)#
Scoped Updates:
- Agent list: Status badges, hash rates, error indicators update
- Campaign page: Progress bars, ETAs, crack counts update
- Agent detail: Current task, performance metrics, error log update
- System health: Service status indicators update
Non-updated Elements:
- Form inputs (never update while user is typing)
- Navigation elements (sidebar, navbar)
- Static content (headers, labels, help text)
- Action buttons (to prevent accidental clicks)
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui; margin: 0; padding: 0; }
.toast-container { position: fixed; top: 20px; right: 20px; z-index: 1000; }
.toast { background: white; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); padding: 16px 20px; margin-bottom: 12px; min-width: 300px; display: flex; align-items: center; justify-content: space-between; }
.toast-success { border-left: 4px solid #28a745; }
.toast-error { border-left: 4px solid #dc3545; }
.toast-content { display: flex; align-items: center; gap: 12px; }
.toast-icon { font-size: 20px; }
.toast-message { font-size: 14px; color: #212529; }
.toast-close { background: none; border: none; font-size: 18px; color: #6c757d; cursor: pointer; padding: 0; }
.skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: loading 1.5s infinite; border-radius: 4px; }
.skeleton-text { height: 16px; margin-bottom: 8px; }
.skeleton-header { height: 32px; width: 60%; margin-bottom: 20px; }
.skeleton-card { height: 120px; margin-bottom: 16px; }
@keyframes loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
.loading-spinner { border: 3px solid #f3f3f3; border-top: 3px solid #007bff; border-radius: 50%; width: 20px; height: 20px; animation: spin 1s linear infinite; display: inline-block; margin-right: 8px; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
</style>
</head>
<body>
<!-- Toast Notifications -->
<div class="toast-container">
<div class="toast toast-success" data-element-id="success-toast">
<div class="toast-content">
<span class="toast-icon">✓</span>
<span class="toast-message">Campaign created successfully</span>
</div>
<button class="toast-close" data-element-id="toast-close">×</button>
</div>
<div class="toast toast-error" data-element-id="error-toast">
<div class="toast-content">
<span class="toast-icon">✗</span>
<span class="toast-message">Failed to update agent: Connection timeout</span>
</div>
<button class="toast-close">×</button>
</div>
</div>
<!-- Skeleton Loading State -->
<div style="background: white; padding: 20px; margin: 20px; border-radius: 8px; max-width: 800px;">
<div class="skeleton skeleton-header"></div>
<div class="skeleton skeleton-card"></div>
<div class="skeleton skeleton-card"></div>
<div class="skeleton skeleton-text" style="width: 80%;"></div>
<div class="skeleton skeleton-text" style="width: 60%;"></div>
</div>
<!-- Loading Button State -->
<div style="background: white; padding: 20px; margin: 20px; border-radius: 8px;">
<button style="padding: 10px 24px; background: #007bff; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: not-allowed; opacity: 0.7;" disabled data-element-id="loading-button">
<span class="loading-spinner"></span>
Creating...
</button>
</div>
</body>
</html>
User Journey Map#
Information Hierarchy Summary#
Critical (Always Visible)#
- Agent status (online/offline/idle)
- Campaign progress (percentage, ETA)
- Error indicators (badges, icons)
- Current activity (what's running now)
Secondary (One Click Away)#
- Error details and logs
- Performance metrics and trends
- Task history and details
- Configuration and capabilities
Tertiary (Progressive Disclosure)#
- Crack results (expandable feed)
- Detailed status history
- Diagnostic tools and logs
- Advanced configuration options
Key UX Principles#
- Visibility Without Overwhelm: Critical information in lists, full details on dedicated pages
- Real-time Awareness: Turbo Streams for live updates, scoped to avoid disrupting user input
- Clear Feedback: Toast notifications for actions, skeleton screens for loading, inline errors for validation
- Efficient Navigation: Direct links between related entities (agent → task → campaign)
- Progressive Disclosure: Tabs and expandable sections for detailed information
- Error Prominence: Red indicators make problems immediately visible, detailed logs for diagnosis
- Desktop-Optimized: No mobile considerations, focus on efficient desktop workflows
Edge Cases & Default Behaviors#
Empty States#
All Agents Offline:
- Agent list shows all agents with 🔴 Offline status
- Warning banner at top: "⚠️ No agents currently online. Tasks cannot be assigned."
- Suggestion: "Check agent connectivity or start new agents"
No Recent Cracks:
- Crack feed button shows: "View Recent Cracks (0 in last 24h)"
- When expanded: "No hashes cracked in the last 24 hours"
- Suggestion: "Check campaign progress and agent status"
No Errors:
- Error tab shows: "No errors recorded"
- Agent list: No error badge displayed
- Campaign: No error indicators
Empty Campaign:
- Already handled with blank slate component
- Message: "Add your first attack to begin cracking"
Large Data Sets#
Campaign with 50+ Attacks:
- Stepper component scrolls vertically
- No pagination (keep all attacks visible for context)
- Consider grouping by state (Running → Pending → Completed → Failed)
Error Log with 1000+ Errors:
- Show most recent 50 errors by default
- "Load More" button at bottom to fetch next 50
- Filter options: By severity, by date range, by task
Crack Feed with 10,000+ Cracks:
- Truncate at 100 most recent cracks (last 24h)
- Message: "Showing 100 most recent cracks. View hash list for complete results."
- Link to hash list detail page for full crack history
Missing or Unavailable Data#
ETA Unavailable:
- Show "Calculating..." if task just started (< 1 minute)
- Show "—" if no benchmark data available
- Show "Unknown" if calculation fails
Hash Rate Display:
- "—" = Agent idle (no task assigned)
- "0 H/s" = Agent working but no progress (possible issue)
- "2.5 GH/s" = Normal operation
Temperature/Utilization Not Reported:
- Show "N/A" if agent doesn't report these metrics
- Don't show error or warning (some agents may not support)
Service Health Check Fails:
- Show last successful check timestamp
- Display error message from connection attempt
- Provide diagnostic links to investigate
Default Pagination & Limits#
- Lists: 50 items per page (agents, campaigns, tasks)
- Error Logs: 50 most recent, load more on demand
- Status History: 20 most recent updates
- Crack Feed: 100 most recent in last 24h
- Search Results: 50 items per page