lnstatus(1)
NAME
lnstatus -- Reticulum network stack status
SYNOPSIS
lnstatus [options] [filter]
DESCRIPTION
lnstatus displays the status of the interfaces on a running Reticulum daemon. It is compatible with Python's rnstatus and produces the same per-interface layout. It connects to a running daemon (lnsd or rnsd) via shared instance IPC, querying interface_stats (and link_count for -l), so lnstatus | diff rnstatus against the same daemon passes.
Without a filter, all up interfaces are shown. Give a filter string to only display interfaces whose name contains it.
With -R it queries a remote transport instance over a link, the way rnstatus -R does, and feeds the result to the same renderer, so remote and local output match. With -d/-D it reads the local discovered-interface registry over the RPC and renders the rnstatus discovered layout.
OPTIONS
filter : Only display interfaces whose name contains this string.
--config dir : Path to alternative Reticulum configuration directory.
--instance-name name
: Shared-instance name to query. Defaults to the value from the configuration file, otherwise default.
-a, --all : Show all interfaces, including those that are down.
-A, --announce-stats : Show announce statistics.
-P, --pr-stats : Show path request statistics.
-B, --burst : Only show interfaces with active bursts.
-l, --link-stats
: Show link statistics: the number of entries in the daemon's transport link table, i.e. the links it relays (queries link_count from the daemon, the same value rnstatus -l reads).
-t, --totals : Display traffic totals.
-s, --sort key
: Sort interfaces by key: rate, traffic, rx, tx, rxs, txs, announces, arx, atx, prx, ptx, or held.
-r, --reverse : Reverse the sort order.
-j, --json : Output in JSON format.
--tables
: Add the size of every table the transport maintains, and the entry count
of every collection its storage holds, to the JSON output as a
transport_tables object. Sizes only; the rows are asked for with
--table-rows. Requires -j; not available with -R or
-d/-D. Leviculum extension — rnstatus has no counterpart, and a
daemon that does not implement it (a Python rnsd, or an older lnsd)
causes the key to be omitted, with a note on stderr and exit status 0.
See TRANSPORT TABLES below.
--table-rows TABLE[,TABLE...]
: Also include the ROWS of the named tables: path_table, reverse_table,
link_table, announce_table, announce_cache, tunnels,
local_links, or all for every one. Repeatable, and accepts a
comma-separated list. Requires --tables. A table not named here is
absent from the response rather than present and empty; its size is in
table_sizes either way. Rows are the expensive half of this query — see
WHAT THE QUERY COSTS below.
-N, --identities
: List every identity the daemon has learned from announces, one row per
announced destination, plus a derived: line with the lxmf.delivery
and rnstransport.probe destinations computed from the identity hash.
Leviculum extension — rnstatus has no counterpart, and a daemon that
does not implement the query (a Python rnsd, or an older lnsd)
causes an error and exit status 2. With -j the raw response is
printed instead. Not available with -R, -d/-D, --tables
or -m. See IDENTITY LISTING below.
-m, --monitor : Continuously monitor status, clearing and redrawing on each interval.
-I, --monitor-interval seconds : Refresh interval for monitor mode (default: 1).
-v, --verbose : Increase verbosity. Repeat for more detail.
--version : Print version and exit.
-R hash : Transport identity hash of a remote instance to query instead of the local one.
-i file : Identity used for remote management.
-w seconds : Timeout before giving up on remote queries.
-d, --discovered : List interfaces discovered on the network.
-D : Show details and config entries for discovered interfaces.
EXIT STATUS
0 : Success.
1 : No shared RNS instance available to get status from (could not derive the RPC authkey).
2 : The status query failed.
20 : Remote management (-R) was requested but the management identity is missing or unusable.
EXAMPLES
Show all interfaces:
lnstatus
Show announce and path request statistics for interfaces named like eth:
lnstatus -A -P eth
Sort interfaces by traffic, most first:
lnstatus -s traffic -r
Continuously monitor, refreshing every two seconds:
lnstatus -m -I 2
Emit machine-readable JSON:
lnstatus -j
Emit JSON with the size of every transport table and storage collection:
lnstatus -j --tables
Emit JSON with the path table's rows as well:
lnstatus -j --tables --table-rows path_table
List the identities heard from announces, with derived destinations ready to paste into lnprobe:
lnstatus -N
IDENTITY LISTING
rnpath -t shows destination hashes only, but probing a remote transport node
needs its rnstransport.probe destination, which is derived from its identity
hash — and the daemon knows that identity, because the announce carried the
public key. -N exposes it: one row per announced destination the daemon
still holds, with the identity hash, the announced destination hash, the name
(only when it matches an aspect the daemon registered itself; ? otherwise —
never guessed), and the live path toward it (hops, via as
interface/next-hop, last seen). Columns without a live path show -.
Under each row a derived: line prints the destinations computed from the
identity hash as sha256(sha256(name)[:10] + identity_hash)[:16] for the two
names that matter in practice: lxmf.delivery and rnstransport.probe.
Hashes are printed in full so they can be pasted into lnprobe or rnprobe.
The inventory is the daemon's announce cache (Python's equivalent store is
Identity.known_destinations), which is bounded by the announce-cache
cleaning the daemon already performs; an identity whose cached announce has
been evicted no longer appears.
TRANSPORT TABLES
With --tables, the -j object gains one additional key,
transport_tables. Nothing else about the output changes, so anything that
parses lnstatus -j today keeps working.
The object always holds table_sizes, rows_for and collections, plus one
list of rows per table named in --table-rows:
table_sizes
: How many rows each of the seven tables below has, as one {name, entries}
row per table, whether or not this response carries that table's rows.
Every entry is a len().
rows_for
: The tables whose rows this response carries — what --table-rows asked
for. Empty by default. A table not listed here has no key in the object;
a table listed here with an empty list is empty.
collections
: How large every collection the daemon's storage holds currently is — not
only the tables dumped below. One row per collection: name (the field
name in the storage, so a row can be read against the source), entries
(live count) and capacity (the ceiling the daemon enforces, or null
where it enforces none). The packet dedup cache appears as its two
generations, packet_cache and packet_cache_prev, never as a sum: a
rotation frees one generation whole, and a sum is flat across exactly
that event. A null capacity is an answer, not a gap — that collection
is bounded by expiry alone.
path_table
: Destinations this node knows a route to. Keys hash, timestamp, via,
hops, expires, interface are the same keys, with the same units, that
Python's own path_table RPC returns (Reticulum.get_path_table). Added:
announce_emitted.
reverse_table
: Where to send the reply to a packet this node forwarded: hash,
receiving_interface, outbound_interface, timestamp.
link_table
: Links this node relays: link_id, timestamp,
next_hop_interface, remaining_hops, receiving_interface, hops,
destination_hash, validated, proof_timeout.
announce_table
: Announces held for deferred rebroadcast: hash, timestamp,
retransmit_timeout, retries, receiving_interface, hops,
packet_length, local_rebroadcasts, block_rebroadcasts,
attached_interface.
announce_cache
: Known destinations whose last announce is still held: hash,
packet_length, retained, last_used.
tunnels
: Reconnectable peers and the paths held against them: tunnel_id,
interface, expires, and paths, each path carrying hash, hops,
via, expires, timestamp, announce_emitted.
local_links
: Links this node is an endpoint of — not the same table as link_table
above: link_id, state, destination_hash, age, interface.
Which clock a timestamp is
Two questions that are easy to confuse, and are answered by two different keys:
timestamp
: Our clock. When this node learned or last refreshed the row, in Unix
seconds. In path_table it is recovered from expires minus the lifetime
that path was granted, so it is exact for a path still on the interface it
was learned on.
announce_emitted
: The announcing node's clock. The whole-second emission stamp that node
wrote into its announce, as this node received it. Peers order competing
announces for one destination by this value, so it is a claim about a
remote machine's time, never about ours. 0 means no announce blob is
stored for the row.
What a count is for
entries without capacity does not say whether a node is near its limit,
which is the question an operator has, so the two travel together. Both are
reported, never enforced here: reading this changes no ceiling and adds none.
Use it to attribute memory. Multiply a count by what one entry of that collection costs and the products either account for the daemon's resident set or they do not — the difference is what separates a design that costs too much from a leak. Before this existed, seven tables of the twenty collections were visible, and the largest structure in the daemon, the dedup cache, was not among them.
What the query costs
A size is a len(). A row is not: the daemon builds one dictionary per row,
with a string key per field, before it can serialise anything, and the whole
structure is live at once. Measured on a node with 11 000 paths and 43 000
reverse entries, asking for those two tables' rows peaks at 83 MB of
daemon memory for one call; asking for sizes alone peaks at 35 KB, and
that figure does not move with the size of the tables.
This is why the rows are named rather than included. An operator polling a
node for how full its tables are — the common case, and the one that gets
polled in a loop — was moving the daemon's resident set by tens of megabytes
per call. If you want rows, ask for the table you want and not for all.
Absent is not empty
A daemon that implements the query answers with transport_tables present and
its tables possibly empty. A daemon that does not implement it causes the key
to be omitted entirely. Test the key's presence to tell the two apart; do
not read an absent key as an empty table.
SEE ALSO
lnsd(1), lnstest(1), lncp(1)