The device-info stream
At start the agent works out what it can about the board it runs on, with no RouterOS API
and no credential, and serves it on /capabilities. The collector hands that to every
sink as its own record. This page answers what the record holds, when it is sent, and
where each sink puts it.
What it carries
Section titled “What it carries”- Identity: the device tree’s model string (the board), the kernel, the core count, whether the container is privileged and whether it has cgroup2, the enabled sources, the port table’s provenance, and a hash of the kernel, the core count and the enabled sources.
- The device’s own ceilings, read once at agent start: each thermal zone’s critical
trip point and polling delay; each core’s cpufreq range, frequency ladder, governor and
cluster; the kernel’s conntrack ceiling; the container’s own
memory.max. - Cadences: the rate each level source is read and stored at, and the named reason it is not the sampler rate.
A ceiling the board does not publish is left out, never filled with a number from somewhere else — the 966 656 conntrack ceiling and the 64 MiB container cap are the board’s own.
The reasons a cadence gives
Section titled “The reasons a cadence gives”reason | What it means |
|---|---|
rate | read at the full sampler rate; nothing the device declares justifies less |
declared | the device publishes its own refresh cadence, and reading faster returns the same value with new dither |
policy | a setting says the value cannot move on its own: a userspace cpufreq governor |
budget | a measured parse cost |
change | read every tick, stored only when it moves |
override | FLOOR_HZ is set, and every level source is on its one cadence |
Scroll sideways to see every column
Counters are never floored; only level sources carry a cadence. The floors themselves and how they were measured are on each source at its own floor.
When it is sent
Section titled “When it is sent”When forward starts, whenever the agent’s capability hash changes — an agent restarted
with another source set, for example — and otherwise every five minutes. The collector
checks the hash on the health read it makes every minute to re-measure the clock skew, so
a change reaches the sinks within about a minute. Both the direct and the relay transport
can fetch /capabilities; when the fetch fails the collector logs it, sends nothing, and
tries again at the next health read. Nothing sent is absence, not a board with no facts.
Why it repeats. These facts are rows with the collector’s timestamp, so a store holds
them only at the instants they were sent, and a dashboard window that contains no
emission contains no facts at all. Sent once at start, the four device panels read “No
data” over every window after it: measured on the reference deployment on 2026-09-17,
where the last device row was 26 hours old and those panels had been empty for as long.
Five minutes puts the facts inside any window worth reading them over and costs twelve
rows an emission — one identity, one per thermal zone, one per core with cpufreq facts,
one per level source — against the 864 000 sample rows a day that --hz 10 produces.
The repeat is marked as one, and the sinks split on it: the stores write it like any
other row, which is the whole point, and the streams meant for a reader — Loki, stdout,
a recording — skip it, because a log is for change. So Loki still carries exactly one
device line per set of facts.
The hash covers only the kernel string, the core count and the enabled source names; the
board, privileged, cgroup, the port table’s provenance, every ceiling and every cadence
are left out of it. A restarted agent whose only change is a ceiling or a cadence — a new
--memory-max, or a FLOOR_HZ with the same source set — keeps its hash, so the change
is not what triggers the send; it reaches the sinks at the next five-minute repeat, and
the row that carries it is stamped then rather than when it happened. This is read from
the code (capsHash in internal/agent/source.go), not observed.
These are facts, not samples. They have no clock of their own, so the sinks that
timestamp records stamp them with the collector’s clock when they were sent; the file and
stdout json lines carry no timestamp, and Prometheus renders them at scrape. They are
never mixed into a sample row.
Where each sink puts it
Section titled “Where each sink puts it”| Sink | Form |
|---|---|
InfluxDB, Telegraf, stdout lp |
mikroscope_, mikroscope_, mikroscope_, mikroscope_ |
| SQL | the tables mikroscope_device, mikroscope_, mikroscope_, mikroscope_ |
file, stdout json |
a {"device":…} line holding the capabilities as fetched |
| Elasticsearch | a document with kind: device |
| Graphite | the numeric facts under device.*; board, kernel and governor have no Graphite form |
| OTLP | gauges: mikroscope.device.cores with board, kernel and hash as attributes, the thermal and cpufreq ceilings, mikroscope. |
| Loki | one source="device", level="info" line: board, kernel, cores, privileged, cgroup, sources, hash |
| Prometheus | the same device-info families the agent’s own /metrics carries |
Scroll sideways to see every column
InfluxDB and SQL
Section titled “InfluxDB and SQL”| Measurement or table | Identified by | Holds |
|---|---|---|
mikroscope_device |
board, kernel |
cores, privileged, cgroup, sources (comma-joined, sorted), hash, conntrack_max, cgroup_mem_max, ports_from |
mikroscope_ |
zone |
critical_celsius, polling_ms |
mikroscope_ |
cpu |
cluster (the lowest-numbered core that changes frequency with this one), min_khz, max_khz, governor, steps |
mikroscope_ |
source, reason |
hz |
Scroll sideways to see every column
In InfluxDB board and kernel are tags, unknown when empty; in SQL they are columns,
NULL when empty, and a ceiling that was not published is NULL. steps is the frequency
ladder as a space-joined list of kHz.
Prometheus
Section titled “Prometheus”| Family | Carries |
|---|---|
mikroscope_ |
always 1 |
mikroscope_ |
the zone’s lowest critical trip |
mikroscope_ |
the zone’s polling delay |
mikroscope_ |
the hardware clock range, min and max |
mikroscope_ |
every frequency the driver will use |
mikroscope_ |
always 1 |
mikroscope_ |
the cluster, named by its lowest-numbered core |
mikroscope_ |
the container’s own memory.max |
mikroscope_ |
each level source’s cadence |
Scroll sideways to see every column
On the reference RB5009, read from related_cpus and affected_cpus inside the container
on 2026-09-14 (RouterOS 7.24.2), the cpufreq clusters are {0,1} and {2,3}, so a
per-core frequency panel is really two series. That is this board’s topology and nothing
else’s: the agent reads the clusters per device, and no other board has been measured. The
conntrack ceiling reaches Prometheus as
mikroscope_ for the nf_conntrack cache rather than as a device-info
family.