Skip to content

Dashboards

A dashboard is a board of small summaries of the case: a row count, the top remote hosts, failed logons over time, how many watchlist hits have landed. Every number is a way in. Click it and the table opens filtered to the rows that number counted, so a board is where a triage pass starts rather than a report you read at the end. A case can hold as many named boards as you like, and a board you build once can be saved to reuse on the next case of the same kind, either on its own (the Library) or together with the plugins it needs (a profile). Two profiles ship ready to use: KAPE triage and ESXi / UAC triage.

Boards live in the case file, so they travel with it when you hand the case to someone else. Their last results are kept in the case file too, so a board opens without re-running every query; see Cached numbers and Refresh.

Where boards live

Boards are listed in the sidebar under Dashboards, below your tables and pages. Each row shows the board's name and how many widgets it has.

To Do this
Open a board Click its name
Rename it Double-click its name in the sidebar, or the title at the top of the board
Delete it ✕ on its row. The widgets go with it; the case data is untouched
Pin it as a page tab ⇱ on its row, or drag the row onto the sidebar's Pages header. It then appears as a tab in the page strip
Unpin it ⇲ on its row, or close its page tab. Closing a pinned board's tab unpins it; the board stays in the case
Link to it from Notes The Notes page's link menu lists every board as Dashboard: name (see Tagging and notes)

Under the boards, a Library heading lists boards kept on this machine and boards a plugin offers. See The Library.

Creating a board

Click + New dashboard at the bottom of the sidebar's Dashboards section. Give it a name and pick where it starts from:

Start from What you get
Blank An empty board. Add widgets with + Add widget
Starter for table A first board read from the table you have open (offered only when a table that isn't a merge is open, and picked by default then)
Shipped: KAPE triage (10 widgets), Shipped: ESXi / UAC triage (17 widgets) A copy of a shipped profile's board, on its own. It does not turn on the profile's plugins or seed its watchlist; apply the profile for that
Library: name A copy of a board you saved to the Library

The starter is built from the table's shape alone: a row count; if the table has a datetime column, its activity window (first and last) and an events over time chart, bucketed by hour when the whole table spans two days or less and by day otherwise; and a top-values bar chart for up to four columns that have between 2 and 12 distinct values. Every widget it makes opens its rows when clicked, and can be edited or removed like any other.

Working with a board

The bar across the top of a board carries:

  • the board's name (double-click to rename);
  • As of time · age, the age of the oldest result on the board;
  • how many widgets run every time it opens;
  • ↻ Refresh all, + Add widget, Save to library… and Save as profile….

Each card has a ⠿ grip (drag it to reorder), a small age mark, ⤴ to open the rows behind it, and ✎ to edit it. Everything else about a widget, including removing it, is in the editor ✎ opens. Cards are laid out on a four-column grid; a widget's Width is 1 to 4 columns.

Empty cards fold into one line. A card that has nothing to show is hidden, and a strip at the top of the board lists it instead with the reason. The two you will see most:

  • no "header set" table in this case yet: the widget reads a kind of export the case doesn't have (no RECmd batch, say). Import one ▸ opens the import dialog.
  • the table is in the case; the query matched nothing: the widget looked and found nothing, which is itself a finding.

Others say that the table the card read is no longer in the case, or that the case has no watchlist entries or no tags yet.

Show them puts the folded cards back on the board so you can edit or remove them; Fold them back collapses them again. A Number card is never folded, because 0 is an answer.

Cached numbers and Refresh

A board paints what each widget returned last time, with its age on the card, and runs only the widgets marked Run every time the dashboard opens. Everything else is re-run when you ask:

  • ↻ Refresh all re-runs every widget on the board. If some can't run, they keep their previous numbers and a toast says how many failed.
  • Run now in a widget's editor re-runs just that one.

When rows have been imported or tagged, or the watchlist has changed, since a number was worked out, its age mark turns to a warning and the bar adds the case has changed since. The number is still shown; ↻ Refresh re-runs it. The check is deliberately coarse: a widget that reads something else, such as a derived column's values or case notes, can go out of date without being marked.

Tick Run every time the dashboard opens only for cheap widgets whose number has to be current the moment the board appears, such as a watchlist or tag count. The editor shows how long the widget took on its last run, and the preview's ran in how long the draft takes, to help you decide.

Adding widgets

The widget editor

+ Add widget (in the bar, or the card of that name at the end of the board) opens the editor; ✎ on a card opens it for that widget. It has two sides. On the left, The question: what to show and which table it comes from, and the editor writes the query. On the right, How it looks, above a preview card that answers the draft as you change it. In a narrow window the two sides stack, the question first.

The question

Field What it does
Data from A query (against the case), Watchlist hits, Tag counts, or Signals card (below)
What to show A template, below. Blank — write my own SQL opens the SQL for you to write
Table One of this case's tables, by name, or a portable entry such as Event logs (EvtxECmd) — any case (portable); see Portable tables
Column, Match, Value, Bucket Shown when the template needs them
Advanced — the SQL The query the template wrote. You can change it. While the box is empty, the example in it names the table picked under Table, both as the SQL has to write it (src_3, say) and by its name
Title The card's heading. Filled in from the template until you type your own

How it looks

Field What it does
Render as How the answer is drawn, one button per kind; see Render kinds
Sub-label (optional) A line under a Number's number, or under a Signals grid
Width 1 column to 4 columns of the board
Run every time the dashboard opens See Cached numbers and Refresh

The preview runs the draft by itself about half a second after you stop typing or picking, and says how long it took (ran in time). There is no button to press, and nothing is saved:

  • Changing only how the card looks (Render as, Title, Sub-label, Width) redraws the answer that is already there, without running the query again.
  • While a changed draft is running, the previous answer stays on the card, dimmed. An answer that comes back for a draft you have changed since is thrown away, so a slow query can't replace the answer to the one you are looking at. It is not stopped on the server, though: a query that takes minutes still runs to the end.
  • A draft that can't run yet says what's missing in one line: no query yet, or no column to pick on the chosen table.
  • An empty answer says what the board will say about it, because a card with nothing to show folds into the board's empty-cards line (see Working with a board).
  • The preview never touches the board's cached numbers. Until you save, the card on the board keeps its own.

Save widget saves it. Run now and Remove widget… appear when you are editing an existing widget. Run now re-runs the saved widget and keeps its result; the preview shows that result too, unless you have changed the question since.

Editing the SQL by hand makes the widget yours: the template stops applying, and so does the click-through to rows. A widget with its own SQL and no click-through opens its query in the SQL pane instead (see From a widget to its rows).

Widget SQL is read-only, with the same rules as the SQL pane, and the editor's column lists leave out derived columns.

Templates

What to show Renders as Asks for Clicking it opens
Row count Number — The whole table
Count of rows matching a value Number Column, Match (equals / contains), Value. An empty value counts rows where the column is empty The matching rows
Distinct count of a column Number Column (empty values are not counted) Rows where the column is not empty
Top values of a column (bar chart) Bar chart, 2 columns Column. The ten commonest non-empty values Click a bar: rows with that value
Rarest values of a column (list) List, 2 columns Column. The ten rarest non-empty values Click a row: rows with that value
Events over time (histogram) Histogram, 2 columns Column (only datetime columns are listed when the table has any), Bucket (per day / per hour). Rows whose timestamp can't be read are left out Click a bar: that day or hour
Activity window (first / last) Key / value, 2 columns Column (as for the histogram) The whole table

From what you're looking at

The grid can add a ready-made widget without the editor. These entries are hidden until you turn on Case ▾ → Case settings… → Dashboards → Dashboard creator mode. It is off by default, so menus stay short for people who only read boards, and it is stored in the case file, so it is set per case.

Where Entry Adds
Right-click a column header Under the Add to dashboard heading: Top values of column, Distinct count of column, and for a datetime column Events over time by column The matching template, on that column
Right-click a cell Add to dashboard → Count of column = value A count of rows with that value
Filters ▾ Add to dashboard: count of this view One number: the rows the grid shows right now. Clicking it reopens the table with exactly these header filters, search, filter-builder conditions, timeframe and tag filter

If the case has one board, the widget goes straight onto it. With none, you are asked to name the first one (Overview is suggested). With several, you pick which. On a merged view the header and Filters ▾ entries are not shown and the cell entry is greyed out. A derived column gets no header entries, and the cell entry is greyed out on one.

Widgets added this way read this case's table, so they won't find it in another case. To build a board you mean to reuse, use the portable tables in the editor instead.

From the SQL pane

To dashboard… in the SQL pane's toolbar turns the query in the editor into a widget: pick a board (or + New dashboard…), and the widget editor opens on it with your query, titled after the query tab and rendered as Key / value. The preview shows the answer straight away; pick how it renders and save.

Widget reference

The editor shows each source and render kind by name. A board, a Library entry or a profile file stores the value in the Stored as column, which is what to write when you edit a profile's JSON by hand.

Data sources

Data from Stored as Answers with
A query sql Your query's result, up to its first 200 rows
Watchlist hits watchlist One row per indicator with its hit count; as a Number, the total of all hits
Tag counts tags One row per tag with how many rows carry it; as a Number, the total
Signals card cells Several small questions, each with its own answer and click-through

Render kinds

Render as Stored as Draws Expects
Number stat One big number The last column of the first row (or the watchlist/tags total)
Key / value kv Label and value pairs Two columns: label, value
Yes / no chips chips Yes/no pills: ✓ when the value is above 0 or true, ✗ otherwise Two columns: label, value
List list A ranked list, first 12 rows Label first, count last
Bar chart bar A horizontal bar chart Label first, count last
Histogram histogram Bars over time Bucket label first, count last
Signals signals A grid of labelled numbers, some optionally as yes/no pills, each opening its own rows Data from Signals card

A signals card is how the shipped KAPE board fits ten numbers in one card without losing any of their click-throughs. A cell whose table isn't in the case shows — (or ? for a pill) in its own place and leaves the others alone. The editor shows a signals card's cells read-only: you can change its title, width and run-every-time setting, but a new signals card comes from a board or profile that already has one.

From a widget to its rows

Clicking a number opens its table filtered to exactly the rows it counted:

  • Number, Key / value and Yes / no chips: click the card body, or ⤴.
  • Bar chart and List: click a bar or a row to open the rows with that value; ⤴ opens the table with no value picked.
  • Histogram: click a bar to open that day or hour. On a datetime column this sets the timeframe filter; on a text column that merely holds dates, it filters on the bucket's prefix instead.
  • Signals: each cell and pill opens its own rows.

The table opens fresh: the header filters, search, tag filter and timeframe you had on it are cleared first, because they would hide rows the widget counted. If the timeframe filter was on, a toast says it has been turned off. The conditions arrive in the filter builder, where you can change them like any other filter (see Filtering and searching).

A widget with its own SQL and no click-through opens its query in the SQL pane instead, reusing the same query tab on later clicks. A widget that counts across several tables (an {{all:…}} widget, below) asks This widget counts across several tables — open which? and opens the one you pick, so you see that table's share of the number.

Portable tables: placeholders

A widget built on one of this case's tables names it by its internal id, which is different in every case. A placeholder names a kind of export instead, and resolves to whichever table in the open case matches it, so a board saved to the Library or a profile works on the next case. The editor's Table list offers the common ones as — any case (portable); in your own SQL you can write any of these:

Placeholder Resolves to
{{evtx}} Event logs (EvtxECmd)
{{mft}} NTFS $MFT (MFTECmd)
{{usn}} USN journal $J (MFTECmd)
{{registry}} Registry (RECmd batch)
{{amcache}} Amcache — program entries
{{prefetch}} Prefetch (PECmd)
{{header_set:Full name}} Any shipped header set by its full name
{{all:…}} Every matching table at once, e.g. {{all:header_set:ESXi / Linux host logs}} or {{all:evtx}}

A table matches when it has every column of that header set (extra columns are fine). A plain placeholder uses the first matching table. {{all:…}} counts across all of them, which is what a support bundle needs: it arrives as dozens of log files sharing one layout, and rotated logs imported later are picked up with no rewiring. The shipped ESXi / UAC board is written this way.

Merged tables, tables that failed to import, and tables saved with Save this view as a table… are left out of an {{all:…}} union: a saved view is a copy of rows another table already contributes, and counting it too would count those rows twice. For the same reason they are not offered when a union widget asks which table to open.

The Library

The Library is boards kept on this machine, outside any case, so they can be added to any case one at a time.

  • Save to library… on a board's bar saves a copy under the name you give (the board's name by default). Saving under a name the Library already has replaces that entry.
  • The Library is listed in the sidebar under Dashboards → Library. Click a name, or + beside it, to copy that board into the open case.
  • ✕ removes a board from the Library. Cases that already have a copy keep it.

Adding a Library board replaces a board of the same name in the case, without asking. Rename the case's board first if you want to keep both.

Some plugins offer a board of their own, listed in the same place with only + (to remove one, turn the plugin off). Turning the plugin on never puts its board in a case; it is copied in only when you add it. If the case already has a board of that name that you built or edited, you are asked first (Replace it / Keep mine); adding it again over its own untouched copy just refreshes it. The bundled esxi_logs plugin offers ESXi / Linux host overview this way (see Plugins).

Profiles

A profile is "how I work this kind of case": the plugins it needs, the boards it opens with, a starter watchlist, and the values the case must carry (case variables). Profiles are kept on this machine and applied to a case.

Saving a board as a profile

Save as profile… on a board's bar saves, under the name you give:

  • the plugins switched on for this case;
  • this board, which is created under the profile's name when the profile is applied;
  • the case's variable definitions (names, descriptions, whether required), never their values.

It is a snapshot: later edits to the board don't change the profile. Saving under the name of one of your existing profiles updates that profile.

The profile manager

Open it with p (the default key), or Settings → Profiles → Open profiles…. Profiles are listed on the left, tagged shipped, applied (to this case) or update; the right side shows the whole of the selected one: every plugin, every board with each widget's render kind and whether it runs on every open, the watchlist and the variables.

Button Does
+ New profile… Build one from scratch: pick plugins, boards (from the Library or the open case) and variables (see Building a profile)
Import… / Export… Read or write one profile as a single .json file. An imported profile whose name is taken is saved with (imported) added
Save current plugins… Save just the plugins switched on now, under a name
Copy to edit Shipped profiles are read-only; this starts an editable copy that remembers where it came from
Edit…, ✕ Edit or delete one of your own. Deleting a profile doesn't change cases it was applied to
Apply to this case… Opens the apply sheet (needs an open case)

When a shipped profile you copied has since been revised, your copy shows one line saying so and See what changed. Take the shipped version replaces your copy with it, and your edits to the copy are lost; Keep mine leaves it alone. Nothing is updated automatically.

Building a profile

+ New profile…, Edit… and Copy to edit open the profile builder, titled New profile, Edit profile — name or New profile from “name”. A shipped profile is read-only, so copying one starts a profile of your own called name (copy).

The left side, What the profile turns on, has the Profile name and Description, then two lists to tick:

  • Plugins: every installed plugin, the bundled examples marked example. Applying the profile turns the ticked ones on for the case and every other plugin off. A plugin the profile names that this machine doesn't have stays in the list, ticked and marked not installed, so saving the profile here doesn't drop it.
  • Dashboards: boards from the dashboard Library (a board's Save to library… puts it there), boards in the open case, and the profile's own, marked library, open case or in this profile. Each is created in the case under its own name. A board saved with Save as profile… is the profile's own board and is created under the profile's name.

The right side, What a case gets, opens with one line saying what applying the profile does, such as Turns on 3 plugins · opens 2 dashboards · asks for 2 values (1 required), kept current as you tick and type. Under it, notes name any ticked plugin that isn't installed here, and the profile's watchlist, which the builder carries over unchanged (it has no watchlist editor).

Variables are the values a case of this type carries, such as the engagement name, a backend URL or a link to the scoping document. + Add variable adds one as a card, and ✕ removes it:

Box Means
Name How plugins read the value (req.variables). A letter, then up to 63 letters, digits, _, . or -, and not used by another variable in the profile
Label What the analyst is shown. Left empty, the name is shown
What it’s for A line shown under the label
Default Already typed into the box when the analyst is asked
Required at case creation The case can't be created until it has a value

A name that breaks the rule, or repeats another, is flagged under its box, and Create profile (Save changes when editing) refuses it. Variables are case data and travel with the case file, so never a token or password: those go in Settings → Environment (see Settings and shortcuts).

Asked when a case is created shows the variables the way the analyst will be asked for them, required ones marked *: in the New case dialog, or after applying the profile to a case that has no value for a required one. It is a preview, so you can't type into it.

The Profile name is checked as you type as well. Profiles are saved by name, so a name you already use says Saving replaces your profile “…”, and renaming a profile while editing it says the original stays as it is and the new name is saved as a second profile.

Applying a profile

Apply to this case… shows what will change, counted against the open case, as four parts you can tick separately. Nothing happens until you press Apply.

Part What applying it does
Plugins Sets every installed plugin on or off for this case: the profile's plugins on, every other plugin off. It also pins them, so turning a plugin on or off "everywhere" later, while another case is open, doesn't change this case. Plugins the profile names that aren't installed here are listed
Boards Creates the profile's board, or replaces a board with the same name. If that board isn't the profile's own untouched copy (you've edited it, or built one with that name), the sheet warns you first
Watchlist Adds the profile's indicators that the case doesn't already have, without auto-tagging, and starts a scan of the case's tables
Variables Adds the variable definitions. Values already set are kept; required ones still empty are asked for after applying

You can also pick a profile in the New case dialog's Profile list. Every part is then applied as soon as the case is created, and required variables are asked for in the dialog itself.

Applying a profile again replaces its board with the profile's current version, which is how a case picks up a revised profile. Cases are never changed by a profile on their own.

The shipped profiles

KAPE triage

Windows host triage from KAPE output: EvtxECmd event logs, plus a RECmd batch if you have one. Applying it turns on lateral_movement (and every other plugin off for this case), seeds five filename indicators (rclone, psexec, mimikatz, anydesk, cobaltstrike) and declares one optional variable, Engagement name. Its board, KAPE triage, has ten widgets:

Widget Shows
Host Computer name, hostname, IP addresses, domain, Windows version and build, and whether it's a workstation, server or domain controller, from the RECmd batch
Logging posture Pills for Sysmon service, Sysmon driver, and PowerShell script block, module and transcription logging (from the registry); counts of Logs cleared (1102) and Audit policy changed (4719)
Coverage The Security log's oldest and newest events, span in days and event count, and the window across all channels
Triage signals Computers seen, distinct accounts, remote logon peers, failed logons (4625), explicit credentials (4648), RDP sessions (21/24/25), PowerShell blocks (4104), process creates (4688), Defender alerts, Run / service entries
Findings Watchlist hits and tagged findings. The one widget that runs every time the board opens
Logon volume over time (4624/4625/4648) Histogram
Top remote hosts (4624) Bar chart
Rarest logon accounts (long tail) List
Registry entries by category Bar chart, from the RECmd batch
Most recent Defender alerts The latest eight

Without a RECmd batch, Host and Registry entries by category fold into the empty-cards line with Import one ▸, and the registry pills read ?.

ESXi / UAC triage

VMware ESXi support bundles and UAC Linux collections. Applying it turns on esxi_logs (and every other plugin off for this case) and declares the same optional Engagement name; it seeds no watchlist. Turn it on, then import the bundle: Winnow expands the archive and the plugin parses every log into one layout (see Plugins). Its board, ESXi / UAC triage, has 17 widgets, all counting across every parsed log at once with {{all:header_set:ESXi / Linux host logs}}:

Log lines · Log types · Activity window · SSH logins accepted · Failed SSH logins · Top source IPs · sudo / su use · Shell commands · Top shell commands · VM power operations · Snapshot operations · vCenter-initiated actions · Account / lockdown / firewall changes · VIB install / remove · Web / API access (rhttpproxy) · Severity mix · Log volume over time.

When the bundle produced more than one log table, clicking a number asks which one to open (see From a widget to its rows).

Limits worth knowing

  • A merged table can't be a widget's table. It isn't offered in the editor, the Add to dashboard entries don't work on a merged view, and a placeholder never resolves to one. SQL you write yourself can read a merge by the name the SQL pane gives it (merge_N), but clicking that widget opens its query in the SQL pane, not rows.
  • Tables made with Save as table are left out of {{all:…}} unions and of the table picker a union widget shows: their rows are already counted in the table they were saved from.
  • A widget built on this case's table doesn't travel. Use the portable tables for boards you'll save to the Library or a profile.
  • Derived columns aren't offered in the widget editor or the Add to dashboard menus, though a widget's own SQL can still name one.
  • Hand-editing a widget's SQL removes its click-through to rows.
  • Adding a Library board replaces a same-named board in the case without asking.
  • Applying a profile turns off every plugin it doesn't list, for that case.
  • Cached numbers can be out of date. Check the age on the card, and use ↻ Refresh all before quoting a number.
  • A widget whose data is A query reads at most the first 200 rows of it, and a List shows the first 12.