starling

Inspector

Local web UI for browsing, diffing, and replaying recorded runs.

starling-inspect opens a SQLite event log read-only and serves a self-contained UI on loopback. No CDN, no JS build, never writes back to the log.

terminal
go run github.com/jerkeyray/starling/cmd/starling-inspect runs.db

--help lists every flag. For non-local access, see Operations → Inspector auth and TLS.

Runs list

Inspector runs dashboard

Per-run totals up top, status tabs and preset chips for narrowing, substring search on run id (/ focuses). The runs list is paged on the server: 50 rows by default, up to 200 with ?per_page=200. Filters and search terms are preserved when paging. Totals reflect the currently visible page, while the pager shows the full matching count.

Run detail

Inspector run detail with timeline and JSON pane

Timeline on the left (color-coded by event family, inline cost/token chips). Detail pane on the right with a sticky meta header - hash, prev hash, call id are click-to-copy - and a syntax-highlighted JSON body that wraps long strings by default. Press ? in the timeline header for the keyboard shortcuts.

Diff

Inspector diff page comparing two runs side by side

/diff aligns two runs by sequence. Pick A and B from the latest 100 runs in the dropdowns, hit Compare. Older runs can still be compared by opening /diff?a={runIDA}&b={runIDB} directly. Diverging rows get a red left rail, a summary strip up top counts matches/diffs and points at the first divergence.

Replaying a run

A Replay button shows up on the run page when the inspector is built into a dual-mode binary that wires starling.InspectCommand(factory). It opens a side-by-side recorded-vs-reproduced timeline; divergence surfaces as a toast plus a click-through dialog. The source log is never written to.

cmd/myagent/main.go
if len(os.Args) > 1 && os.Args[1] == "inspect" {
    return starling.InspectCommand(myFactory).Run(os.Args[2:])
}

Embedding the inspector

If you want to mount the inspector in a larger HTTP server, build it directly:

import "github.com/jerkeyray/starling/inspect"

srv, err := inspect.New(log,
    inspect.WithAuth(inspect.BearerAuth(os.Getenv("STARLING_INSPECT_TOKEN"))),
    inspect.WithReplayer(myFactory),         // optional: enables Replay button
    inspect.WithDBPath("/var/lib/runs.db"),  // optional: shows the DB basename in the topbar context chip
)
if err != nil { return err }
http.Handle("/runs/", http.StripPrefix("/runs", srv))

WithDBPath populates a small chip in the inspector topbar with the database's basename (full path on hover) so operators can see which log the UI is pointing at when several inspectors run side by side. The standalone CLI passes it automatically.

On this page