There is no separate HTTP/REST API for the UI; see
Reactive Loop for how the pieces fit. Every
server-side operation is one of these named actions. All of them are TAB scoped,
and the URL is <basePath>_action/<name>, the same in every tab: the via_ctx
field of the JSON body tells the server which tab a request belongs to. Templates
always resolve the URL with {{ actionName.url() }} so the base path is right
(the Twig name is the camelCase of the action name, set-range becomes
setRange). A script scrapes via_ctx and the signal ids from the page (see
Environment Notes).
Inputs are either signals, posted as the JSON body the way Datastar sends them, or
query parameters on the action URL (?id=), read with $c->input().
Every action POST needs an Origin header naming the host of the request, as a
browser sends it. Outside dev mode (NFSEN_DEV_MODE) php-via answers a POST
without one with 403 Forbidden: missing Origin, and one whose Origin names
another host than the request’s Host header with 403 Forbidden: untrusted origin, so a reverse proxy has to pass the original Host on.
Renders the page the client switched to; an unknown page is reset to the default page
dismiss-notification
ShellActions.php
?page=<page id>&id=<notice id>
Removes a notice from that page’s state; without a known page, from every page
kill-nfdump
UtilityActions.php
none
Sends SIGTERM to every nfdump this tab’s query runs (a split query and a filtered graph run several), by query handle (see NfdumpSlots), and names their PIDs; the notice goes to the page that owns query_kind
ip-info
UtilityActions.php
?ip=
Renders the IP info dialog into the modal root: reverse DNS, then GeoIP or the web service (public) or Netbox (private)
Moves the global window. Presets, durations and now make it live; back, pin and an absolute window that ends in the past pin it. back is refused at the start of the stored data. Reads no capture file
Checks the target’s filter with nfdump -Z and writes the answer into _flt_<target>; the newest request wins, and a check that cannot run answers Filter could not be checked
conv_group (ip|net24|net16|port), conv_direction (both|forward), sankey_metric, sankey_topN, filter and byte limits
Runs the aggregation (query kind conversations), split into parallel time slices when the read is large. A Kill before nfdump starts or before the result is stored keeps the previous result and its notices
conversations-check
ConversationActions.php
the same signals
Recomputes _conv_stale only and reads no capture file; an effect on the query card’s signals posts it, so a filter applied from the drawer counts too
Validates the text into _flt_drawer; the render adds the editor and the saved list
drawer-close
FilterDrawerActions.php
signal drawer_open
Re-renders the closed drawer
filter-save
FilterDrawerActions.php
signals drawer_filter, drawer_name
Saves the editor’s text, named after itself when unnamed; a duplicate answers with a warning
filter-update
FilterDrawerActions.php
?id=, optional &rename=1
Updates name and expression, or with rename=1 only the name
filter-delete
FilterDrawerActions.php
?id=
Deletes a saved filter
filter-star
FilterDrawerActions.php
?id=&on=0|1
Stars or unstars it
filter-use
FilterDrawerActions.php
?id=
Loads the expression into the editor and marks the filter used
filter-migrate-local
FilterDrawerActions.php
signal drawer_import
Imports a browser’s old saved list
The drawer opens on the window event nfsen-open-drawer with
{target: overview|talkers|flows|conversations|alert, tab: builder|raw|saved};
the filter fields’ Builder and Saved buttons dispatch it.
filter-migrate-local reads drawer_import and always clears it. When the import
ran, or the list held nothing new, it sets the server-owned signal
_drawer_imported to a fresh random id; a post with an empty drawer_import is
not acknowledged, and a failure answers with an error-level _drawer_notice. The
browser sets localStoragenfsen-filters-migrated only when _drawer_imported
changes while its import is pending, so a failed, lost or unanswered post is
retried on the next load.
Evaluates the rule against the newest complete interval, records a test event, sends the notifications if it would fire (waiting up to 10 s for the webhook), and opens the result dialog with each channel’s delivery
?scope=general|rdns, the settings_* signals and displayTz
Saves the General tab (general), the reverse DNS switch (rdns), or both without a scope; merges into preferences.json, keeping the alert rules and templates
Every query that reads capture files runs through QueryRunner with a kind, which
the progress signals (query_running, query_permille, query_status,
query_eta, query_kind) report and which decides the page a Kill notice goes
to: graph and overview-topn (Overview), stats and talkers-panel (Top
Talkers), flows, flows-summary and flowsgraph (Flows), conversations
(Conversations). The estimator records every finished run of a kind in
query_runs, with the nfdump processes it read its files with and whether it
needed a second pass. A split run’s query_status counts files (Read 120 of 288
files in 4 nfdump processes), and its final status names the processes.
An MCP client does not use these actions, which are bound to a browser tab’s
signals. The optional MCP server exposes ten read-only tools
over stdio or HTTP and calls the query layer directly.
The fastest way to see what signals an action reads and writes is the action
closure itself: they’re short, and each starts by pulling its inputs with
$c->getSignal('name') or $c->input('name'). There is no separate schema to
keep in sync with them.