Where the project stands
This page answers what a reader deciding whether to try mikroscope needs first: which parts work end to end, on what hardware that was shown, what the observer costs today, which defects are known and still open, and whether there is anything to download. It is checked against the code as of 2026-09-16.
What works end to end
Section titled “What works end to end”Everything below except the test suites and the seven sinks marked as such has run against the reference RB5009 (RouterOS 7.24.2), not only against fakes:
- The agent reads the shared kernel’s
/proc,/sys,/dev/kmsgandperf_event_opencounters on a fixed ticker at 1 to 100 Hz (10 Hz by default; 10, 50 and 100 Hz measured), keeps the samples in a ring, and serves them:/healthz,/capabilities,/snapshot,/stream,/metrics, and the triggered-capture endpoints/capturesand/capture. - The deployment CLI installs, upgrades and removes it —
doctor,plan,install,status,upgrade,uninstall— with every write listed before it happens and every removal verified by ownership counts. On 2026-09-12,doctor→install→status→upgrade→uninstallleft the router’s/exportbyte-identical, compared by hash in memory and never written to disk. The agent answered 3 s after install, at a 5–7 ms round trip. On 2026-09-17 all four install routes were run against the same device, one after another and each removed before the next: the checkout build, the published agent tar, the router’s own pull from Docker Hub, and theplan --rscscript imported on the router with no CLI in the install itself. The agent answered in every one, and the/exportafter the four was byte-identical to the one before them. record,markandplotwork end to end. A 60 s recording at 10 Hz on 2026-09-12 yielded exactly 600 samples, 0 gaps and −7 ms of clock skew.forward, the collector, merges the kernel tier with the RouterOS API tier and writes to ten sinks. File, Prometheus and InfluxDB 3 have run from the RB5009; Loki, OTLP, Graphite, Elasticsearch, SQL, Telegraf and stdout have not yet had router samples pushed through them, but every one of them now writes into the real product — InfluxDB 3, PostgreSQL, Elasticsearch, Graphite, Loki, an OpenTelemetry Collector, Telegraf and Prometheus in containers — and the suite reads each one back through its own API (2026-09-16). Its derive stage adds derived values and detection events beside the raw rows, and it emits a device-info stream once per capability hash.- Five Grafana dashboards, one per store — InfluxDB 3, Prometheus,
PostgreSQL, Graphite and Elasticsearch — generated from one panel list, with
alert rules for the three whose query language the rules are written in. The
two SQL stores are asked the same question in two dialects: the PostgreSQL
panels are the InfluxDB ones rewritten, and every one of its 209 queries is
planned by a real PostgreSQL in the container suite. Graphite and
Elasticsearch carry fewer panels on purpose (41 and 30 against 171): Graphite
has no labels and Elasticsearch no nested documents, so what they cannot
express is absent rather than wrong. All five are imported into a real
Grafana over the stores the suite filled, and every panel is asked: on
2026-09-17, 57, 98, 136, 35 and 28 panels returned data and none failed.
On 2026-09-16, in Grafana 13.2.1, against the RB5009 with
the default triggers and a 30-minute
forwardinto both stores,dashboards checkpassed 171 InfluxDB panels (0 failing, 10 known-empty tolerated) and 133 Prometheus panels (0 failing, 9 known-empty tolerated). The headless row-by-row render walk of both dashboards — 0 error badges, 0 “No data” — is from 2026-09-15 and covers 168 and 130 panels; it has not been repeated for the two port-event panels and the interface-inventory table it does not cover. A walk on 2026-09-14, against a 10.5 h capture from the same device, rendered 140 InfluxDB panels with the same result. - An end-to-end suite builds both binaries and drives them against a
captured
/proctree of the reference device and against a fake agent, with one receiver per sink protocol asserting the bytes. It needs no router, no Grafana and no network, and runs in CI. - A second suite against the stores themselves (
make test-e2e-docker) starts nine of them with docker compose, runs the same collector against the same fake agent with every sink pointed at them, and then asks each store its own question with its own API: SQL against InfluxDB 3,psqlagainst the script the SQL sink wrote, a search against Elasticsearch,query_rangeagainst Loki,renderagainst Graphite, the decoded OTLP the collector received, the line protocol Telegraf parsed, and a scrape of the exporter stored in Prometheus. It then imports both dashboards into Grafana and runs every panel’s query through Grafana’s own API. It needs Docker and no router: the samples are the same canned ones, so the run is reproducible anywhere. The first full run, on 2026-09-16, found a panel that named two columns the store only has when the API tier ran.
What the agent costs today
Section titled “What the agent costs today”At the install default — 10 Hz, default per-source floors, a 300 s ring — the agent costs 2.85 % of one core and 31.3 MiB RSS, read from its own cgroup at steady state with the ring full:
Measured on RB5009UG+S+ · 4 × 1.4 GHz Cortex-A72 · RouterOS 7.24.2 · · 60 s windows at steady state (ring full), full source set, collector forwarding to a file, a Prometheus exposition and InfluxDB 3 at once
| rate | floors | CPU of one core | µs/ | RSS | slipped ticks | gaps / |
|---|---|---|---|---|---|---|
| 10 Hz (default) | default | 2.85 % | 2 856 | 31.3 MiB | 0 | 0 / 0 |
Scroll sideways to see every column
That is above the budget of 2 % of one core and 16 MiB RSS. The budget is guidance rather than a contract: cost scales with the device, the source set and the ring size. The image budget is 8 MiB; the one image size on record is 6.1 MiB, and it carries no date.
Two rules keep the cost figure honest: wait out the ring (BUFFER_S) before
quoting a steady-state figure — on the RB5009, at a 14 MiB soft memory limit, a
reading taken in the first minute after install came back at 1.47 % of one core
against a 9.38 % steady state — and read cost from /metrics rather than from a
large /snapshot, whose ~1.5 MB response the agent must serialise.
Cost at 10 Hz, by configuration
Section titled “Cost at 10 Hz, by configuration”Every row was measured on the same RB5009 at 10 Hz, and each is one window with no spread recorded. They are the settings a reader can choose, so the table says what each one costs — not what the number was at some earlier point.
| Configuration | CPU of one core | RSS | Measured | Note |
|---|---|---|---|---|
| Every source read every tick | 2.43 % | not recorded | 2026-09-12 | above the budget |
Ring full, MEM_LIMIT_MB 14 |
9.38 % (9 374 µs/sample) | not recorded | 2026-09-12 | a 300 s ring of lines of about 2.4 kB holds ~7.3 MB; the Go GC runs without pause |
Ring full, --mem-limit-mb 40, --memory-max 64M |
1.39 % (1 388 µs/sample) | 25.13 MiB | 2026-09-12 | 0 slipped ticks |
PMU counters on, a live forward writing to InfluxDB |
1.72 % | not recorded | 2026-09-12 | 0 slipped ticks; 2 400 samples forwarded, 0 gaps, 0 drops |
| The install default | 2.85 % | 31.3 MiB | 2026-09-15 | the figure above |
Scroll sideways to see every column
One device, one RouterOS version
Section titled “One device, one RouterOS version”Every on-device figure in this documentation comes from one RB5009UG+S+ on RouterOS 7.24.2, the owner’s production router. There is no lab device.
Found and not fixed
Section titled “Found and not fixed”Checked against the code as of this page:
forward’s end-of-run summary goes to stdout, the same stream the--stdoutsink writes records to, soforward --stdout=lp | telegrafends every run with lines the consumer cannot parse.- A wrong
--tokendoes not fail the run. Each pull is logged as401 Unauthorized, and the run then ends at its--fordeadline with exit status 0 andforwarded 0 kernel samples. The health checkforwardmakes before it starts pulling reads/healthz, which needs no token, so it passes. The message is right; the exit status tells a unit file nothing. - Duplicate sequence numbers in the 2026-09-13 overnight run. The 50 Hz run
wrote repeated
seqvalues into InfluxDB; the dashboards name those rows “duplicate sample (seq repeated)”. The leading explanation, which is unverified, is Go’s HTTP client replaying a POST on a dead pooled connection after the server had committed it. The InfluxDB, Loki, OTLP, Elasticsearch and Telegraf sinks refuse that replay, so a dead connection is an error the sink retries and counts. No run since that night shows whether the duplicates are gone. - Graphite names thermal zones by index. The index is deliberate — a
zone’s
typestring is not unique across zones — but the path listing at the top ofinternal/readssinks/ graphite. go thermal.<zone>.
Found on the device and not collected
Section titled “Found on the device and not collected”Two readable files on the reference device, read there on 2026-09-15, are not
collected: /proc/cmdline, which
carries board=5009 ver=7.24.1 — a second device identity without the API —
and the hardware watchdog at /.
What the release publishes
Section titled “What the release publishes”The current release is v1.0.0 — the version in VERSION, compiled into both
binaries and reported by mikroscope version. A v* tag runs the GoReleaser
configuration, which publishes:
- CLI archives for linux, darwin, windows and freebsd on amd64, arm64 and
arm:
.tar.gz, and.zipon Windows, each carryingLICENSEandREADME.md. - Agent archives for linux on the same three architectures, for a host that wants the bare binary rather than an image.
- Side-loadable agent image tars, one per architecture —
mikroscope-agent-arm64.,tar mikroscope-agent-arm.tar,mikroscope-agent-amd64.— whichtar install --agent-taruploads to the router. - The agent image in two registries,
jmrplens/on Docker Hub andmikroscope-agent:1. 0. 0 ghcr.on GHCR, each one manifest overio/ jmrplens/ mikroscope-agent:1. 0. 0 linux/amd64,linux/arm64andlinux/arm/v7, whichinstall --remote-imagemakes the router pull. RouterOS takes the registry host from the global/, which ships ascontainer/ config registry-url https:/, so the Docker Hub reference needs nothing set on the device and the GHCR one needs that setting changed first./ registry-1. docker. io checksums.txt, covering every archive and every image tar, a keyless cosign signature over it, and an SPDX SBOM per archive, signed in its own right.
Installing from a release needs no Go toolchain and no checkout. A checkout and
Go 1.27 install an agent built from your own tree instead: make build for the
CLI, make build-agent for the agent, which install and image also compile
themselves. The four ways to get the agent onto a router, and what each one
needs, are on Installing the agent.
What the release publishes for linux/arm and linux/amd64 is cross-built and
checked in CI. Of the three architectures, only arm64 has run on hardware — on
the one RB5009 above.