3.3 The window
The GUI is GTK4 in C, built with meson, and it is optional in the same way the terminal workspace is optional: nothing in it can do something the modules cannot.
bash imsisting.sh --gui # or:
cd native && meson setup builddir && ninja -C builddir && ./builddir/imsisting-gui
Its own flags are few and mostly for scripted checks and screenshots:
native/builddir/imsisting-gui --module 07_gsm_attack # open on a module
native/builddir/imsisting-gui --tab 2 # open on notebook page 2
native/builddir/imsisting-gui --list-modules # id, category, name as TSV
native/builddir/imsisting-gui --selftest # every page's own arithmetic
native/builddir/imsisting-gui --version
Boot, and what is inside the binary
The window carries its palette, its icons, its fonts and a coarse world map in the binary as a GResource. It needs no network to draw itself, no icon theme to be installed and no fonts to be present on the box; a machine with GTK and nothing else still looks like the desk (§1.1).
Three stylesheets are loaded in a fixed order, each above the last:
style.css— the palette and the layout, the one the desk was drawn in;theme-<id>.css— a second palette, if the Theme setting is not Night. It may restate colours only, never layout, which is why a theme cannot fall behind the layout it decorates;corners.css— if the Corners setting is Square. Shape outranks palette, so this one loads above the theme.
The window has no console to print to, so it redirects its own standard error to
tmp/gui.stderr, tails that file, and turns anything that is not noise into an
event — which is how a GTK warning ends up in Notifications instead of vanishing.
What the window remembers between launches is a handful of rows in
state/ui.tsv: the catalogue's sort order, the density, the theme and the
corners. Deleting that file resets the window's appearance and nothing else.
The header
Two strips. The upper one carries the title and version, the global search field, and three buttons: Target (toggles the target panel), Events (with a count) and the window controls. The lower one is the instrument strip:
SDR TX FREQ CELLS GPS polled just now
none no — 0 absent
Each chip is a reading, not a wish. TX no means no transmitter is usable —
which is the state most readers should be in most of the time (§2.2). CELLS 0
means the survey store is empty, not that there are no towers. The strip
refreshes on a timer and says when it last did, so "0" always has a timestamp
next to it.
The drawer
The left edge holds a sliding drawer: the ten pages, then the module catalogue. It slides over the page rather than pushing it aside. The Help page's own words for this are the right ones — a drawer that took width would reflow every card each time it opened, and navigation would be felt as a layout change.
The drawer's order and the notebook's internal order are not the same list, and that matters when you are scripting screenshots or reading this manual against the source:
| Notebook index | Drawer row | Question it answers |
|---|---|---|
| 0 | Dashboard | what can this toolkit do, and where do I start |
| 1 | Live Traffic | what are the sensors hearing right now |
| 2 | Casebook | what am I collecting, and for which case |
| 3 | Stats | how much have I actually collected |
| 4 | Notifications | what happened while I was not looking |
| 5 | Help | how does this window work |
| 6 | Settings | what does this toolkit do by default |
| 7 | Workflows | I have a question, not a module — where do I start |
| 8 | Map | where are these cells and observations |
| 9 | Fleet | what machines are these sensors on |
Dashboard
The dashboard answers "what can this do" without a manual. It opens with four Start here steps, in order: Define the operation, Check the field kit, Survey the cell layer, Watch traffic. The order is the argument: set the case before collecting, verify the bench before trusting it, look at the cell layer before decoding it, and only then start watching. Under it sits the whole catalogue — sixty-one cards grouped by the eleven categories, each with its icon, its name and its one-line purpose, with search above and two controls beside it: Sort (Workflow / Name / Category) and Density (Spacious / Compact).
Workflow order is not alphabetical and not by category. It is the order a session actually moves in — dependency, radio, field kit, killchain, remote nodes — which is why it is the default.
Live Traffic
The page for as long as a survey is running. It filters packets by protocol, follows a stream, pauses, resets, saves the buffer to a pcap, and shows a packet inspector for the selected frame. Its own controls cover the things that decide whether a long capture survives: an autosave interval, a rotation threshold, a minimum signal level and a port. A packet below the minimum is not displayed; that is a display filter, and the capture file still contains it.
Casebook
Seven tabs, and the split is by object rather than by verb: Overview
(counters and the current case), Evidence (files with tags and a sensitivity
marker), Captures (indexed files), Timeline (events in order),
Alerts (raised by baselines and rules), Nodes (the sensor registry),
Reports (exported HTML, JSON, TSV). Each tab is a list over
python3 tools/casebook.py verbs, so anything you can do here you can do from a
shell — and the reverse, which is the point.
Map
An equirectangular projection over a basemap compiled into the binary, with
layers read from loot/map/layers/*.tsv. Pan and zoom are checked by the
window's own selftest, because a direction bug is the one thing a screenshot
cannot show. Cells, observations and geo-tagged casebook entries are drawn on the
same plane so that "the tower we heard" and "the place we heard it from" can be
compared without exporting anything.
Fleet
The registry of remote nodes: name, host, transport, class, role, capabilities,
status, detail and last-seen — read from the casebook's node store, under the same
columns as tools/casebook.py nodes. This is where a survey that spans more than
one box stops being a folder of TSVs and becomes a set of machines with roles
(§5.x).
Stats
Counts, deliberately unglamorous: modules installed, loot files, captures, runs recorded, empty shelves (categories with nothing collected), never pressed (modules that have never been run on this box), a breakdown by module, and the fleet. The last two counters are the useful ones. A toolkit is only as good as the parts of it you have exercised, and "never pressed" is the honest measure of that.
Notifications
The event log, one row per thing that happened, clearable. Both producers land here: a module that exits non-zero, and the window itself — including the "that theme is not in this build" toast described below.
Help
Eleven subjects, in the window: Overview, Getting around, Modules, Running a verb, Radio & transmit, Casebook & evidence, Files & state, Command line, Hardware, Troubleshooting, Legal & ethics. It is a page rather than a dialog because it is meant to be read with the window open beside it, and it is the same doctrine as this manual compressed to a screenful per subject.
Settings
Six views down the left: Interface, Paths, Radio, Live traffic,
Notifications, Diagnostics. Each row is one decision, its current value,
the built-in default and a note saying what the value means — the note is not
decoration, it is the only place the reason for a default is written down. Two of
them are appearance: Theme (Night / Ember) and Corners (Round / Square). Both are
persisted in state/ui.tsv and both are asserted by --selftest, because a
preference that offers a choice it cannot deliver is worse than no choice.
Paths shows each directory with the value in use, the source it came from (the
environment, the file, or the built-in default) and a default button. This is
the view to open first when a run wrote somewhere unexpected: it answers "where
did that go" before you go looking.
Diagnostics holds the things that are only interesting when something is wrong
— PIN state, crash reports, build facts, restart hints — and the log the window
writes about itself.
Workflows
Twelve questions, each opened into the steps that answer it. The questions are phrased the way an operator asks them, not the way a module is named: See the towers around me, Read my own SIM, Check whether my number is being ported, Find out whether a band is being jammed, Test my operator's API, Capture and read signalling, Watch for a fake tower, Read what a tower broadcasts, Recover an identity, Audit a cipher, Build a fake cell in a lab, Run an attack chain. Every step runs a module that is already installed; a route adds no capability, only an order and a worked example to type. Each route also states what it needs (a radio, a capture, nothing at all) and what you will be holding when it finishes.
A module page
Open any card and the page is built the same way, which is what makes sixty-one modules learnable once:
- a view list down the left, one row per view, in the order the module's author intended you to move through it — this is the same list the terminal workspace shows (§3.2);
- cards per view, each with a title in the imperative (Read the store, Build the frame, Send it to a radio) and a one-line explanation of what the button underneath will do;
- verb rows: a one-click button for a verb that needs no argument, a form with labelled fields for a verb that does, with an example already in the field so an empty form is never the first thing you see;
- chips stating what the verb needs:
NEEDS A RADIO,NEEDS A CAPTURE,TRANSMITS,NEEDS NO RADIO,READS,DOES READS. A transmitting verb is marked as such with the same weight as a warning, because it is one; - a standard result card: the argv that ran, the exit code, the output, and the store the run wrote to, if any.
The last of those is the most important. Every run in the window leaves behind the exact command line, so the answer to "what did that button do" is never "something in the GUI".
The target panel
Toggled from the header, it floats on the right and holds the current target and the case counters: the target set in the Casebook, its posture chips (what the network is doing to this subscriber right now, read from the newest row of each measurement), and the counts of evidence, captures, alerts and timeline entries in the open case. Like the drawer, it floats rather than taking width.
The posture chips are the one place the window interprets anything: an algorithm
the toolkit cannot place never comes out strong, and the newest row wins. That
rule is asserted in --selftest as well.
Themes and shape, and an honest failure
Two appearance settings, and one lesson behind each.
Theme (Night / Ember) works by loading a second sheet above the base one. The
Ember sheet is generated from the base sheet by tools/gen-theme.py — greys warm,
cool surfaces deepen toward black, the blue accent becomes an ember accent, and
green/red/amber keep their hue so a status colour still means what it means —
and it restates colours only. A theme therefore cannot fall behind the layout it
decorates. The one thing a generated palette cannot know is where the accent
should appear on purpose, so the generator appends a small hand-written tail for
the GTK widgets the base sheet never styled: entries, popovers, checkboxes,
switches, tooltips, scrollbars, selections.
Corners (Round / Square) loads a sheet whose whole content is a border-radius reset, with an exception list for the two shapes that must stay round — switches and radio knobs — because a square switch reads as a checkbox.
Both settings have the same failure mode and the window now refuses it. A stylesheet that is not compiled into the binary draws nothing, silently, while the control still says "Ember": GTK does not complain, so the user concludes the theme is broken and the app concludes it worked. The window therefore looks the sheet up first, and if it is missing it says
The 'ember' theme is not in this build (/com/imsisting/native/theme-ember.css): the base palette is what is being drawn. Re-run tools/gen-icons-xml.sh and rebuild.
puts the same sentence in Notifications, reverts the setting, and only then
carries on. --selftest asserts the same fact twice — once per theme, once per
corner style — so a build that offers a choice it cannot deliver fails a gate
rather than wasting an afternoon.