Skip to main content

Debug Page

The Debug Page is SSG's window into its own decision-making. Every time SSG checks a tool call (Bash, Read, Edit, Write, …), it runs a small pipeline:

parse → match → decide → audit

This page lets you watch that pipeline live so you can answer questions about SSG's behavior — most often: "Why did SSG block (or allow) this?" and "Is my new rule actually firing?".

The same data is also exposed via the ssg debug CLI and the ssg_debug_* MCP tools, so Claude Code, Cursor, and other clients see exactly what you see in the dashboard.

Who this is for

You don't need to be developing on SSG itself to get value from this page. Common audiences:

You are……and you'd use this page to…
A rule authorConfirm your new .rules file is loaded and that your rule actually matched the tool call you wrote it for
A team adminUnderstand why your AI agent got blocked from running a command in front of a teammate
An operatorTroubleshoot the daemon when evals feel slow, drop events, or behave unexpectedly
An SSG developerTrace per-stage timings, inspect native FFI calls, and replay frames for a specific trace

Opening the page

ssg serve # starts daemon + dashboard on http://localhost:5599

Then navigate to http://localhost:5599/debug.

The SigmaShake Debug page — plain-English questions about why a call was blocked or whether a rule fired, above the RCA / Timeline / Watch / State / Metrics tabs

Enabling frame capture

Most tabs work out of the box. The Timeline tab — which renders a flame-chart of every eval frame — is opt-in because collecting every frame adds overhead on the hot path. To turn it on, restart the daemon with the env var set:

SSG_DEBUG_ENABLED=1 ssg daemon

You can confirm capture is on at a glance: the top-right of the page shows a green pulsing LIVE chip when frames are being captured, or an amber DEBUG OFF chip when they aren't. When debug is off, an explicit banner above the tabs tells you the Timeline will stay empty until you restart with the env var.

SSG_DEBUG_ENABLED is read once at daemon startup, so changes only take effect on restart. There is no runtime toggle by design — disabled means a single return null on the hot path, with zero allocations.

The five tabs

The page has five tabs. They all read from the same unified ssg:// URI resolver inside the daemon, so they're always consistent with the CLI and MCP equivalents.

RCA — Root Cause Analysis

Use when: Something just broke and you want a one-line diagnosis before grepping logs.

When an eval errors, an ssqa finding fires, or a daemon-level error occurs, SSG auto-composes an RCA pack with:

  • A short summary of what went wrong
  • Severity (critical / high / medium / low)
  • Ranked hypothesis list with confidence percentages and evidence bullets
  • Suggested next actions
  • Links to related ssg:// resources for drill-down

The CLI equivalent is ssg debug rca --current, and the MCP equivalent is ssg_debug_rca({auto_current: true}). Claude Code's /rca slash command and the /prelude skill pull from this same pack.

Timeline — Eval Frame Visualization

Use when: You want to see exactly which stages a rule went through and how long each took.

A flame-chart of the last 1024 eval frames across every rule and tool call:

  • Rows group frames by trace_id — one tool call = one trace
  • Bar colors identify the pipeline stage (hook-ingress, parse, match, ffi-call, decide, audit, respond)
  • Bar width is the stage's duration in nanoseconds
  • A red outline means that stage errored

Click any bar to open the frame inspector sidebar with full detail: stage, tool, duration, rule_id, error type/message, sanitized input preview, full trace and frame IDs.

Requires SSG_DEBUG_ENABLED=1 on the daemon. Frames are also persisted to SQLite for 24 hours so you can investigate evals that happened earlier in the day.

Watch — Live URI Subscription

Use when: You know which internal resource you want to inspect and want to watch it update in real time.

Subscribe to any ssg:// URI and the pane re-fetches whenever the daemon emits a relevant SSE event. Quick-select buttons cover the common cases; the text input accepts any URI the resolver supports.

Available URIs:

URIReturns
ssg://daemon/stateLive vitals (uptime, rules loaded, native FFI availability, frame ring, eval quotas)
ssg://daemon/metricsLatency distributions and event counters
ssg://audit/recent?limit=20Recent audit rows
ssg://audit/<id>A single audit row
ssg://frames/recent?limit=64Newest debug frames
ssg://frames/<trace_id>All frames for one eval (supplemented from SQLite if the ring buffer is short)
ssg://frames/<trace_id>/<frame_id>A single frame
ssg://rules/listAll loaded rules
ssg://rules/<rule_id>A single rule
ssg://rca/currentThe latest RCA pack
ssg://rca/trace/<id>RCA pack for a specific trace
ssg://native/statsNative FFI evaluator stats

State — Daemon Snapshot

Use when: The daemon feels off and you want to confirm it's healthy.

Renders ssg://daemon/state. Key fields:

  • uptime_ms — how long the daemon has been running
  • rules_loaded — number of rules currently loaded (use this to confirm a new .rules file was picked up)
  • native_available — whether the native FFI evaluator was loaded
  • audit_worker_ready — whether the audit worker thread is accepting writes
  • frame_ring_size, frame_ring_dropped — frame buffer state
  • daily_eval_count, eval_limit — current quota usage
  • debug_enabled — whether SSG_DEBUG_ENABLED=1 is set on the running daemon

Metrics — Performance Counters

Use when: Evals feel slow and you want to find the hot stage before optimizing.

Renders ssg://daemon/metrics — latency distributions and counters for the eval pipeline. Look at per-stage timings to identify whether parse, match, the native FFI call, decide, or audit is the bottleneck.

CLI equivalents

Every dashboard view has a CLI mirror so you can script against the same data:

CLI commandPurpose
ssg debug snapshot <uri>Fetch a single URI envelope as JSON
ssg debug rca --currentFetch the latest RCA pack (add --markdown for human-readable)
ssg debug rca --trace=<id>Fetch the RCA pack for a specific trace
ssg debug watch <uri>Long-poll a URI, print one envelope per change
ssg debug followStream new frames as JSONL (--tool, --trace, --rule filters)
ssg debug analyze-sessionsCost analysis of Claude Code conversation history (last 30 days)

See ssg debug in the CLI reference for full flag documentation.

MCP equivalents

When the SSG MCP server is connected to your AI client, the same data is available as MCP tools:

MCP toolMaps to
ssg_debug_snapshotssg debug snapshot
ssg_debug_rcassg debug rca
ssg_debug_watchssg debug watch
ssg_debug_replayreplay frames for a specific trace

This is how Claude Code's /rca and /prelude skills inject pre-computed root-cause analysis into a conversation without you having to fetch it manually.

Cost model

When SSG_DEBUG_ENABLED is unset, the entire frame-capture subsystem is a no-op. The check is a single environment-variable read at module init, so calls to emitFrame() short-circuit to return null with zero allocations on the hot path.

When enabled, frames are:

  1. Pushed to an in-memory ring buffer (1024 frames max)
  2. Queued for SQLite persistence in batches (24-hour retention, auto-pruned)
  3. Broadcast to dashboard subscribers via the debug-frame SSE event

Input previews are sanitized for secrets before storage. The overhead is small but non-zero, which is why the flag exists at all.

Troubleshooting

"Live" chip is green but Timeline is empty. Confirm SSG_DEBUG_ENABLED=1 was set in the daemon's environment, not just your shell. Restart with SSG_DEBUG_ENABLED=1 ssg daemon and verify debug_enabled: true on the State tab.

Timeline shows frames but my rule_id isn't on any bar. Your rule may not be loaded — check rules_loaded on the State tab, or open Watch and resolve ssg://rules/list to confirm. If the rule is loaded but didn't match, the bars will still appear without a rule_id field on the matched-stage frame.

/debug page returns 404. You're hitting the daemon's Unix socket port or a different service. The dashboard runs on port 5599 by default (ssg serve).

RCA tab says "no current RCA pack". That's the green case — nothing has errored badly enough to compose a pack. Packs auto-fire on eval errors, ssqa findings, and daemon-level errors.