# Install with the CLI

Install the agent with mikroscope install over ssh, what it checks and writes in order, how it marks what it owns, and the flags and variables it reads.

Source: https://docs.jmrp.io/mikroscope/install/

`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:

- **Registry pull**

  ```sh
  mikroscope install --router admin@192.168.88.1 --remote-image jmrplens/mikroscope-agent:1.6.1
  ```

  The 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/jmrplens/mikroscope-agent:1.6.1`.

- **From source**

  ```sh
  git clone https://github.com/jmrplens/mikroscope
  cd mikroscope
  make build
  bin/mikroscope install --router admin@192.168.88.1
  ```

  With neither `--remote-image` nor `--agent-tar`, the CLI builds the agent from the checkout:
  `go build ./cmd/mikroscope-agent` 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](https://docs.jmrp.io/mikroscope/install/offline/).

## Before you install

- The router meets the [Requirements](https://docs.jmrp.io/mikroscope/install/prerequisites/): RouterOS 7.24 or later,
  the `container` package and `device-mode container=yes`. Run
  `mikroscope doctor --router admin@192.168.88.1` with 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](https://docs.jmrp.io/mikroscope/install/routes/) compares the
  routes.

## Install steps

1. **Reads the router.** `install` runs `doctor`'s checks in one connect and reads the router's
   architecture there (`--arch auto`, the default). A `MISSING` check stops it with
   `N prerequisite(s) missing; nothing was written`; a `WARN` is printed and does not.
   `--no-doctor` skips 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.

2. **Gets the image.** With `--remote-image` nothing is fetched. With `--agent-tar` it checks that
   the tar is a mikroscope agent image for the router's architecture and prints
   `using mikroscope-agent-amd64.tar: linux/amd64, agent <size> KiB`. With neither it builds one
   from the checkout.

3. **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, or `the router pulls <image> (nothing is uploaded)`. It ends with
   `nothing above has been written yet`. `mikroscope plan` and `install --dry-run` stop here
   without connecting, and assume `arm64` where the architecture matters.

4. **Asks** `write the objects above to the router? [y/N]`. `--yes` skips the question.

5. **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.

6. **Writes.** The install manifest first, then each object, one connect per write:

   ```text
     new   install manifest mikroscope/mikroscope.manifest.txt
     new   veth interface veth-mikroscope
     new   router address 172.30.10.1
     new   interface-list membership LAN
     new   address-list membership LANs
     pull  the router pulls registry-1.docker.io/jmrplens/mikroscope-agent:1.6.1 itself
     new   container mikroscope
   install done: 6 step(s) created
   ```

   On 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,
   which `uninstall` removes.

7. **Probes the agent** from your host and says which transport works:

   ```text
   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 trip
   ```

   When the probe fails, it asks the router whether the container runs before it suggests
   anything else: [Network access](https://docs.jmrp.io/mikroscope/install/reaching-the-agent/).

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/<name>.manifest.txt` on the install's disk that lists the options and every object below
- 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-image` has 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](https://docs.jmrp.io/mikroscope/install/layout/) has where each object lives, what the
envlist carries and the container's settings. [Firewall lists](https://docs.jmrp.io/mikroscope/install/firewall/)
explains the two list memberships.

## 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/envs` and `/file` carry no comment. The envlist is signed by an entry
  `MIKROSCOPE_TAG` whose value is the exact tag, which the agent ignores, and the manifest by its
  `tag=` line.
- Every `find` quotes its address and port values. Unquoted, RouterOS compares them as typed values
  and an unquoted `dst-port=9123` matches nothing ([verified](https://docs.jmrp.io/mikroscope/about/status/#verified-find-quoting)).
- 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](https://docs.jmrp.io/mikroscope/security/installer/) lists what else the installer refuses.

`upgrade` and `uninstall` are on [Upgrade and uninstall](https://docs.jmrp.io/mikroscope/install/upgrade/).

## Check the install

```sh
mikroscope status --router admin@192.168.88.1
```

`status` 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:

```text
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 mikroscope
agent: 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
  router` and probes nothing.
- Otherwise it probes `/healthz` with 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](https://docs.jmrp.io/mikroscope/reference/port-names/)).
- If the agent does not answer, it prints `agent: not reachable from this host` with the error, and
  still exits 0.

## Command summary

```sh
mikroscope doctor                          # read-only: prerequisites, then the running agent's ring
mikroscope plan                            # every command, nothing written
mikroscope install                         # checks, listing, confirmation, writes, probe
mikroscope status                          # the install's shape, ownership counts, agent health
mikroscope upgrade --remote-image …        # a new image; the container step and anything missing
mikroscope uninstall --yes                 # removes everything the install created (without --yes: lists)
mikroscope image --arch arm64              # the image tar, for side-loading by hand
mikroscope 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

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`](https://github.com/jmrplens/mikroscope/blob/main/.env.example) documents them. The CLI does not read `.env` itself:

```sh
set -a; . ./.env; set +a
```

`status`, `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](https://docs.jmrp.io/mikroscope/reference/cli/#flags-of-the-deployment-verbs) has every flag, and
[Environment variables](https://docs.jmrp.io/mikroscope/reference/environment/) every variable.

## 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](https://docs.jmrp.io/mikroscope/cost/#ssh-cost) has the measurement.

## See also

- [Requirements](https://docs.jmrp.io/mikroscope/install/prerequisites/): the architecture, the package and the
  device-mode step, and what `doctor` checks.
- [Upgrade and uninstall](https://docs.jmrp.io/mikroscope/install/upgrade/): a new release, and removing everything the
  install created.
- [Install methods](https://docs.jmrp.io/mikroscope/install/routes/): the other ways to install, without ssh or without
  a registry.
- [Network access](https://docs.jmrp.io/mikroscope/install/reaching-the-agent/): the probe, and direct, relay and
  `--expose`.
- [Installer safeguards](https://docs.jmrp.io/mikroscope/security/installer/): what the installer refuses to build on or
  remove.
