Using the Aspire Dashboard
The .NET Aspire dashboard is your command center for developing and debugging CitadelMesh. This guide covers everything you need to know to maximize productivity.
Starting the Dashboardโ
Quick Startโ
cd /path/to/CitadelMesh/src/CitadelMesh.AppHost
dotnet run
Expected Output:
Building...
info: Aspire.Hosting.DistributedApplication[0]
Aspire version: 8.0.0
info: Aspire.Hosting.DistributedApplication[0]
Distributed application started. Press Ctrl+C to shut down.
info: Aspire.Hosting.DistributedApplication[0]
Dashboard URL: https://localhost:5000
Navigate to: https://localhost:5000
CLI Route (recommended when AppHost canโt find the dashboard)โ
If you see errors like โCliPath/DashboardPath not found,โ use the Aspire CLI. It brings its own dashboard runner and avoids path issues.
- Ensure .NET 9 SDK is installed (9.0.300+)
- Install the Aspire workload for that SDK
- Verify the CLI:
dotnet aspire --help - From
src/CitadelMesh.AppHost, start via CLI
Troubleshooting tips:
- If
dotnet aspireisnโt found, install/repair the Aspire workload for the SDK version youโre using - Make sure dev certs are trusted if using HTTPS locally (
dotnet dev-certs https --trust) - Prefer the CLI route on macOS when the AppHost path resolution fails
Advanced Startup Optionsโ
Custom Portโ
# Set custom port via environment variable
export ASPIRE_DASHBOARD_PORT=7000
dotnet run
Or update appsettings.Development.json:
{
"Dashboard": {
"Port": 7000
}
}
Selective Service Startupโ
Edit src/CitadelMesh.AppHost/Program.cs to comment out services:
var builder = DistributedApplication.CreateBuilder(args);
// Core infrastructure (always needed)
var redis = builder.AddRedis("redis", port: 6379);
var postgres = builder.AddPostgres("postgres", port: 5432)
.AddDatabase("citadel-db");
// Optional: Comment out if not needed
// var jaeger = builder.AddContainer("jaeger", "jaegertracing/all-in-one");
// var prometheus = builder.AddContainer("prometheus", "prom/prometheus");
var app = builder.Build();
app.Run();
Dashboard Featuresโ
1. Resources Tabโ
The Resources view shows all running services with real-time status.
Service Categoriesโ
| Resource Type | Purpose | Default Port |
|---|---|---|
| redis | Caching & pub/sub | 6379 |
| postgres | Persistent storage | 5432 |
| nats | Event bus | 4222 |
| opa | Policy engine | 8181 |
| spire-server | Identity provider | 8081 |
| spire-agent | Workload attestation | - |
| jaeger | Distributed tracing | 16686 |
| agent-runtime | Python agent container | - |
Resource Actionsโ
Each resource has quick actions:
- View Logs: Opens console output
- View Details: Shows configuration
- Restart: Graceful restart
- Stop/Start: Manual control
Example: Restarting OPA
- Find
opain Resources list - Click โฎ menu
- Select Restart
- Monitor logs for successful reload
2. Console Logs Tabโ
Real-time log streaming from all services.
Filtering Logsโ
By Service:
Filter: service:opa
Shows only OPA logs
By Level:
Filter: level:error
Shows only errors across all services
By Message Pattern:
Filter: message:policy
Shows logs containing "policy"
Combined Filters:
Filter: service:agent-runtime level:error
Shows errors from agent runtime
Log Levelsโ
TRACE- Verbose debug infoDEBUG- Development diagnosticsINFO- Normal operationsWARN- Potential issuesERROR- Failures requiring attentionFATAL- Critical system errors
Example: Debugging Policy Denialsโ
Filter: message:"Policy violation"
Result:
[12:34:56] ERROR [CitadelMesh.Safety] Policy violation: Door unlock denied
Policy: citadel.security.door_unlock
Input: {"action":"door_unlock","duration_seconds":600}
Reason: Exceeded maximum duration (300s)
3. Structured Logs Tabโ
Advanced log analysis with filtering, grouping, and export.
Query Examplesโ
Find all policy evaluations:
{
"policy.result": {"$exists": true}
}
Find slow operations:
{
"duration_ms": {"$gt": 1000}
}
Find agent errors:
{
"service": "agent-runtime",
"level": "error"
}
Grouping and Aggregationโ
- Select Group By โ
service - Select Aggregate โ
count - Result: Log count per service
Export Logsโ
- Apply filters
- Click Export โ JSON
- Use for offline analysis or bug reports
4. Traces Tabโ
Distributed tracing with OpenTelemetry/Jaeger integration.
Trace Viewโ
Each trace shows:
- Trace ID: Unique identifier
- Duration: Total time
- Spans: Individual operations
- Status: Success/Error/Timeout
Example: Agent Execution Traceโ
Trace: agent.security.process_scenario
โโ span: monitor_events (12ms)
โ โโ span: camera_action.get_incidents (8ms)
โโ span: analyze_threat (5ms)
โโ span: coordinate_response (3ms)
โโ span: execute_door_control (45ms)
โ โโ span: mcp.schneider.lock_door (40ms)
โโ span: audit_log_response (2ms)
Total: 67ms
Finding Performance Bottlenecksโ
- Click Traces tab
- Sort by Duration (descending)
- Click slow trace
- Examine span waterfall
- Identify bottleneck (longest span)
Trace Filteringโ
By Service:
service.name:agent.security
By Operation:
operation.name:execute_door_control
By Status:
status.code:ERROR
By Duration:
duration:>1000ms
5. Metrics Tabโ
Real-time metrics and dashboards.
Available Metricsโ
Infrastructure:
- Redis: Operations/sec, memory usage
- PostgreSQL: Query count, connection pool
- NATS: Messages/sec, queue depth
Application:
- Agent executions
- Policy evaluations
- MCP tool invocations
- Event processing latency
System:
- CPU usage per container
- Memory usage per container
- Network I/O
- Disk I/O
Creating Custom Dashboardsโ
- Click + New Dashboard
- Add metrics:
citadel_agent_executions_totalcitadel_policy_evaluations_totalcitadel_mcp_tool_duration_seconds
- Choose visualization (line chart, bar chart)
- Set refresh interval (5s, 30s, 1m)
Alertingโ
Set up alerts for critical metrics:
- Select metric (e.g.,
error_rate) - Click Create Alert
- Set threshold:
> 5% - Configure notification (email, webhook)
6. Environment Variables Tabโ
View and edit environment variables for all services.
Viewing Variablesโ
- Click Environment tab
- Select service (e.g.,
opa) - See all env vars:
OPA_LOG_LEVEL=debug
OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4317
Hot-Reloading Variablesโ
- Click Edit on variable
- Change value (e.g.,
OPA_LOG_LEVEL=info) - Click Save
- Service auto-restarts with new value
Note: Only supported for containerized services with restart policies.
Debugging Workflowsโ
Debugging OPA Policy Denialsโ
Scenario: Agent action is denied by policy
Steps:
-
Find the denial in logs:
- Console Logs โ Filter:
message:"Policy violation" - Note the
inputandreason
- Console Logs โ Filter:
-
Check policy evaluation:
- Structured Logs โ Query:
{"policy.result": false} - View full policy input/output
- Structured Logs โ Query:
-
Test policy locally:
# Create test input
cat > input.json <<EOF
{
"action": "door_unlock",
"duration_seconds": 600
}
EOF
# Evaluate policy
opa eval -i input.json -d policies/security.rego \
'data.citadel.security.allow' -
Fix policy or input:
- Edit
policies/security.rego - Policies auto-reload in OPA container
- Edit
-
Verify in dashboard:
- Watch Console Logs for policy reload
- Re-run agent
- Check for successful evaluation
Debugging Agent State Machineโ
Scenario: Agent gets stuck in a state
Steps:
-
Find agent execution trace:
- Traces โ Filter:
service.name:agent.security - Find stuck execution (long duration)
- Traces โ Filter:
-
Examine span timeline:
- Click trace
- Look for incomplete spans or timeouts
-
Check agent logs:
- Console Logs โ Filter:
service:agent-runtime level:debug - Look for state transitions:
DEBUG: State transition: monitor โ analyze
DEBUG: State transition: analyze โ coordinate_response
DEBUG: State transition: coordinate_response โ STUCK
- Console Logs โ Filter:
-
Inspect agent state:
- Structured Logs โ Query:
{"agent.state": {"$exists": true}} - View state object:
{
"status": "active",
"current_state": "coordinate_response",
"context": {...},
"error": "Timeout waiting for MCP response"
}
- Structured Logs โ Query:
-
Fix the issue:
- Add timeout handling in agent code
- Or fix MCP adapter issue
Debugging MCP Adapter Issuesโ
Scenario: MCP tool invocation fails
Steps:
-
Check MCP server logs:
- Resources โ Find MCP container
- View Logs
- Look for errors:
ERROR: Failed to execute tool: set_temperature
Error: Connection refused to EcoStruxure API
-
Verify MCP server is running:
docker ps | grep mcp
# Should show running container -
Test MCP server directly:
# List available tools
curl http://localhost:3001/tools
# Invoke tool
curl -X POST http://localhost:3001/tools/set_temperature \
-H 'Content-Type: application/json' \
-d '{"zone": "lobby", "temperature": 22.0}' -
Check OPA policy for tool:
- MCP tools may be blocked by policy
- Console Logs โ Filter:
message:set_temperature
-
Enable mock mode for development:
- Environment โ Select MCP server
- Edit
ENABLE_MOCK_MODE=true - Restart service
Hot Reload Workflowsโ
.NET Microservices Hot Reloadโ
Aspire supports .NET hot reload out of the box.
Steps:
- Edit C# file (e.g.,
src/microservices/CitadelMesh.Safety/Program.cs) - Save file
- Dashboard shows:
๐ Reloading: safety-engine - Changes applied (no restart needed)
Limitations:
- Method signature changes require restart
- New dependencies require
dotnet restore
OPA Policy Hot Reloadโ
Policies auto-reload when files change.
Steps:
- Edit policy:
vim policies/security.rego - Save file
- Dashboard Console Logs shows:
INFO: OPA bundle reloaded
INFO: Loaded policies: citadel.security - Test immediately (no restart)
Python Agent Developmentโ
Agents don't auto-reload, but you can use watchdog:
cd src/agents
# Install watchdog
pip install watchdog[watchmedo]
# Auto-restart on file change
watchmedo auto-restart \
--pattern="*.py" \
--recursive \
-- python security/security_agent.py
Advanced Featuresโ
Custom Resource Health Checksโ
Add health check to custom service:
// In Program.cs
var myService = builder.AddContainer("my-service", "my-image")
.WithHealthCheck("http://localhost:8080/health");
Dashboard will show health status with โ /โ indicator.
Resource Dependenciesโ
Ensure services start in order:
var postgres = builder.AddPostgres("postgres");
var orchestrator = builder.AddProject<Projects.Orchestrator>("orchestrator")
.WithReference(postgres) // Waits for postgres
.WaitFor(postgres); // Explicit wait
External Servicesโ
Connect to external systems:
// External Redis
builder.AddConnectionString("external-redis", "redis://prod-redis:6379");
// Use in services
var myService = builder.AddProject<Projects.MyService>("my-service")
.WithReference("external-redis");
Performance Tipsโ
1. Reduce Log Volumeโ
For better performance, reduce log verbosity in production-like testing:
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"CitadelMesh": "Information"
}
}
}
2. Disable Unused Featuresโ
// Disable tracing if not needed
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing.SetSampler(new AlwaysOffSampler()));
3. Use Persistent Volumesโ
Avoid recreating containers on each start:
var postgres = builder.AddPostgres("postgres")
.WithDataVolume("citadel-postgres-data"); // Persists across restarts
Troubleshooting Dashboard Issuesโ
Dashboard not accessibleโ
Error: Cannot connect to https://localhost:5000
Solution:
# Check if AppHost is running
ps aux | grep CitadelMesh.AppHost
# Restart AppHost
cd src/CitadelMesh.AppHost
dotnet run
Services show as unhealthyโ
Error: Red โ indicators for all services
Solution:
# Check Docker
docker ps
# Restart Docker Desktop if needed
# Then restart Aspire
dotnet run
Logs not appearingโ
Issue: Console Logs tab is empty
Solution:
- Check log level in
appsettings.Development.json - Ensure
"Aspire": "Debug"is set - Restart AppHost
Traces not showingโ
Issue: Traces tab shows "No traces"
Solution:
# Verify Jaeger is running
curl http://localhost:16686
# Check OTLP endpoint
curl http://localhost:4317
Next Stepsโ
- Docker Compose Alternative - Simpler deployment option
- Writing OPA Policies - Policy development
- Testing Agents - Agent debugging techniques
- Production Deployment - Deploy to K3s
Dashboard Keyboard Shortcutsโ
| Shortcut | Action |
|---|---|
Cmd/Ctrl + K | Open command palette |
Cmd/Ctrl + F | Focus search/filter |
Cmd/Ctrl + R | Refresh current view |
Esc | Clear filters |
? | Show help overlay |
Master the dashboard and you'll be 10x more productive! Continue to Docker Compose Setup.