Install with the CLI
mikroscope install puts the agent on the router over ssh. It checks the router, reads its
architecture, lists every RouterOS command, asks, writes, and probes the agent. Pick where the image
comes from:
mikroscope install --router admin@192.168.88.1 --remote-image jmrplens/mikroscope-agent:1.6.1The router pulls the image for its own architecture from the registry. Nothing is built or
uploaded, and /container/config is not touched. The same image is on GHCR as
ghcr.io/.
git clone https://github.com/jmrplens/mikroscopecd mikroscopemake buildbin/mikroscope install --router admin@192.168.88.1With neither --remote-image nor --agent-tar, the CLI builds the agent from the checkout:
go build ./ for the router’s architecture with CGO_ENABLED=0, packed into
an image tar without Docker, and uploaded with scp. It needs Go 1.27 and must run from the
checkout; without Go on PATH it stops before anything is listed and names the other two routes.
A router that cannot reach a registry takes the published image tar: Offline install.
Before you install
Section titled “Before you install”- The router meets the Requirements: RouterOS 7.24 or later,
the
containerpackage anddevice-mode container=yes. Runmikroscope doctor --router admin@192.168.88.1with the same flags you will install with; it writes nothing and names the fix for anything missing. - ssh reaches the router as an admin user with a key or an ssh agent. For a router not yet in
known_hosts, add--ssh-option StrictHostKeyChecking=accept-new. - Choose where the image comes from: Install methods compares the routes.
Install steps
Section titled “Install steps”-
Reads the router.
installrunsdoctor’s checks in one connect and reads the router’s architecture there (--arch auto, the default). AMISSINGcheck stops it withN prerequisite(s) missing; nothing was written; aWARNis printed and does not.--no-doctorskips the checks; the architecture then takes one connect of its own, or none with--remote-image, where the router picks it from the image’s index. -
Gets the image. With
--remote-imagenothing is fetched. With--agent-tarit checks that the tar is a mikroscope agent image for the router’s architecture and printsusing mikroscope-agent-amd64.. With neither it builds one from the checkout.tar: linux/amd64, agent <size> KiB -
Prints the plan. One line of options (a token shows as
token=(set), never its value), the tag, then every RouterOS command numbered, the install manifest first. The container step names the upload with its size, orthe router pulls <image> (nothing is uploaded). It ends withnothing above has been written yet.mikroscope planandinstall --dry-runstop here without connecting, and assumearm64where the architecture matters. -
Asks
write the objects above to the router? [y/N].--yesskips the question. -
Checks every step at once. One connect asks, for each step, whether mikroscope’s object is there and whether something else already holds it. A step already this install’s prints
ok … (already present)and is skipped. A step something else holds stops the install before the first write. -
Writes. The install manifest first, then each object, one connect per write:
new install manifest mikroscope/mikroscope.manifest.txtnew veth interface veth-mikroscopenew router address 172.30.10.1new interface-list membership LANnew address-list membership LANspull the router pulls registry-1.docker.io/jmrplens/mikroscope-agent:1.6.1 itselfnew container mikroscopeinstall done: 6 step(s) createdOn the tar route the container step uploads the tar (
up uploading mikroscope.tar (<size> KiB)), waits for RouterOS to extract it, up to--extract-timeout(120 s by default), then deletes the tar and starts the container. If extraction is not done by then, it stops and leaves the tar, whichuninstallremoves. -
Probes the agent from your host and says which transport works:
probing http://172.30.10.2:9123/healthz from this host …direct transport ok: agent 1.6.1 (<commit>) built <time>, 10 Hz, seq 7, 0 slipped, 2ms round tripWhen the probe fails, it asks the router whether the container runs before it suggests anything else: Network access.
A second install on a router where every step is already this install’s creates nothing and goes
straight to the probe.
What install writes to your router
- the install manifest, a file
mikroscope/on the install's disk that lists the options and every object below<name>. manifest.txt - a veth
- one address
- one interface-list membership, unless
--iface-list none - one address-list entry, unless
--addr-list none - an envlist
- the image tar, deleted once the container is extracted, unless
--remote-imagehas the router pull the image - the container, and its root
mikroscope/<name>on the same disk
Every object carries the comment mikroscope:<name> (managed by mikroscope)
mikroscope plan prints every command before anything is written.
uninstall removes by exact tag plus identity, never by pattern, and fails naming the step if anything remains.
Storage and container settings has where each object lives, what the envlist carries and the container’s settings. Firewall lists explains the two list memberships.
Ownership tags
Section titled “Ownership tags”Every object install creates carries the comment mikroscope:<name> (managed by mikroscope), and
the install manifest lists each one with its identity: the veth by name, the address by interface,
a list membership by list and interface, an address-list entry by list and address. A removal
matches the tag and that identity, never a pattern.
/container/envsand/filecarry no comment. The envlist is signed by an entryMIKROSCOPE_TAGwhose value is the exact tag, which the agent ignores, and the manifest by itstag=line.- Every
findquotes its address and port values. Unquoted, RouterOS compares them as typed values and an unquoteddst-port=9123matches nothing (verified). - RouterOS can report an error over ssh with exit status 0, and abandons the rest of a
;-joined line at the first one. So a write that prints anything counts as a failure. If the container step fails after an upload, the tar is taken back (undo removed the uploaded …).
Installer safeguards lists what else the installer refuses.
upgrade and uninstall are on Upgrade and uninstall.
Check the install
Section titled “Check the install”mikroscope status --router admin@192.168.88.1status reads the install’s shape from its manifest, or from its tagged objects for an install
made without one, and prints the ownership count of every step from one connect:
install on the router (manifest mikroscope/mikroscope.manifest.txt): veth veth-mikroscope, subnet 172.30.10.0/30, lists LAN/LANs, port 9123, pulled from registry-1.docker.io/jmrplens/mikroscope-agent:1.6.1 manifest mikroscope/mikroscope.manifest.txt: the plan comes from it 1 install manifest mikroscope/mikroscope.manifest.txt 1 veth interface veth-mikroscope 1 router address 172.30.10.1 1 interface-list membership LAN 1 address-list membership LANs 8 container mikroscopeagent: 1.6.1 (<commit>) built <time>, 10 Hz, seq 14 (oldest 1), up 1s, 0 slipped, 1ms round trip- When nothing is installed it ends with
verified: nothing mikroscope created remains on the routerand probes nothing. - Otherwise it probes
/healthzwith a 3 s timeout and prints the agent’s version, rate, sequence and oldest sequence, uptime, slipped ticks and round trip, then one line about the board: whether this build maps the kernel’s port names to RouterOS’s on it (Port names). - If the agent does not answer, it prints
agent: not reachable from this hostwith the error, and still exits 0.
Command summary
Section titled “Command summary”mikroscope doctor # read-only: prerequisites, then the running agent's ringmikroscope plan # every command, nothing writtenmikroscope install # checks, listing, confirmation, writes, probemikroscope status # the install's shape, ownership counts, agent healthmikroscope upgrade --remote-image … # a new image; the container step and anything missingmikroscope uninstall --yes # removes everything the install created (without --yes: lists)mikroscope image --arch arm64 # the image tar, for side-loading by handmikroscope plan --rsc --out install.rsc # the same install, as a RouterOS script--router takes user@host or an ssh config alias, and every verb that connects needs it.
Flags and variables
Section titled “Flags and variables”These flags read their default from a MIKROSCOPE_* variable: --router, --ssh-port,
--ssh-key, --ssh-option, --name, --veth, --subnet, --iface-list, --addr-list,
--disk, --arch, --token, --lan-address, --agent-tar and --remote-image. The
file .env.example documents them. The CLI does not read .env itself:
set -a; . ./.env; set +astatus, upgrade and uninstall read the install’s shape from the router, so a shape flag you
leave out takes the installed value. A flag or a variable that contradicts the install is refused,
naming both values, before anything is written.
Every value that reaches a RouterOS command is bounded before the first connection: names, disks,
the architecture, memory syntax, the token’s characters, the port and rate ranges, the registry
reference, the subnet (an IPv4 /30 at its network address) and --triggers, which the agent’s own
parser checks. A value out of bounds fails the verb with exit status 2 and writes nothing. The agent
parses TRIGGERS again when it starts, since an envlist can be edited by hand on the router, and a
value it rejects there makes it exit and log bad configuration.
CLI has every flag, and
Environment variables every variable.
SSH connections
Section titled “SSH connections”Each ssh connect costs the router CPU for as long as it lasts, so the CLI batches every read into
one connect: doctor is one, status is one, and install’s questions about every step are one.
Each write takes one more, plus the scp upload on the tar route. ssh is never a data path:
record and forward reach the agent over HTTP or the RouterOS API.
SSH cost has the measurement.