# Variables de entorno

Las variables MIKROSCOPE_* que lee la CLI, las credenciales que solo existen como variables y las variables de la envlist que lee el agente en el router.

Source: https://docs.jmrp.io/mikroscope/es/reference/environment/

Dos programas leen variables de entorno, y leen variables distintas. La
**CLI** en tu máquina lee variables `MIKROSCOPE_*` como valores por defecto de
las opciones, además de un puñado de credenciales que no tienen opción, además
de `GRAFANA_URL` y `GRAFANA_TOKEN`. El **agente** en el router lee variables
sin prefijo (`RATE_HZ`, `TOKEN` …) de la envlist de su contenedor, que escribe
`install`. Las de la CLI se definen en [`cmd/mikroscope/`](https://github.com/jmrplens/mikroscope/tree/main/cmd/mikroscope), las del
agente en [`internal/agent/config.go`](https://github.com/jmrplens/mikroscope/blob/main/internal/agent/config.go).

## Precedencia

- Una variable fija el **valor por defecto** de una opción; la opción en la
  línea de órdenes gana.
- Una variable con la cadena vacía se trata como no definida.
- Las variables de forma (`MIKROSCOPE_VETH`, `MIKROSCOPE_SUBNET`,
  `MIKROSCOPE_IFACE_LIST`, `MIKROSCOPE_ADDR_LIST`, `MIKROSCOPE_DISK`,
  `MIKROSCOPE_LAN_ADDRESS`, `MIKROSCOPE_REMOTE_IMAGE`) fijan lo que escribe
  `install`. `status`, `upgrade` y `uninstall` toman en su lugar la forma de la
  instalación que hay en el router, de su manifiesto o de sus objetos
  etiquetados, y rechazan una variable u opción que la contradiga, nombrando
  los dos valores; `MIKROSCOPE_REMOTE_IMAGE` es la excepción, que se rellena
  para `status` y `uninstall` y nunca se rechaza. Por eso `.env.example` las
  deja sin definir.
- La CLI nunca lee un fichero `.env`. Expórtalo antes a la shell:

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

- Entrecomilla una URL que contenga `&` cuando vive en un fichero que cargas
  con `source`: sin comillas, `&` es un operador de la shell. [`.env.example`](https://github.com/jmrplens/mikroscope/blob/main/.env.example)
  entrecomilla `MIKROSCOPE_INFLUX_URL` por eso.

La mayoría de las opciones de despliegue **no** tienen variable. `--rate`,
`--buffer`, `--port`, `--memory-max`, `--mem-limit-mb`, `--capture-mb`,
`--triggers`, `--floor-hz`, `--privileged`, `--ephemeral`, `--expose`,
`--restart-max-count`, `--restart-interval`, `--start-on-boot`,
`--container-name` y `--extract-timeout` se fijan en la línea de órdenes o no
se fijan. [CLI](https://docs.jmrp.io/mikroscope/es/reference/cli/) tiene todas las opciones.

## Router y despliegue

| Variable                  | Opción           | La leen                                               | Por defecto en el código        | Valor en [`.env.example`](https://github.com/jmrplens/mikroscope/blob/main/.env.example) |
| ------------------------- | ---------------- | ----------------------------------------------------- | ------------------------------- | ----------------------- |
| `MIKROSCOPE_ROUTER`       | `--router`       | `doctor`, `install`, `upgrade`, `uninstall`, `status` | ninguno (obligatoria)           | definida                |
| `MIKROSCOPE_SSH_PORT`     | `--ssh-port`     | los mismos                                            | configuración de ssh            | `22`                    |
| `MIKROSCOPE_SSH_KEY`      | `--ssh-key`      | los mismos                                            | agente o configuración de ssh   | vacío                   |
| `MIKROSCOPE_SSH_OPTIONS` | `--ssh-option` | los mismos | ninguno | comentada, `StrictHostKeyChecking=accept-new` |
| `MIKROSCOPE_ARCH` | `--arch` | los verbos de despliegue | `auto` | comentada, `auto` |
| `MIKROSCOPE_AGENT_TAR`    | `--agent-tar`    | `doctor`, `plan`, `install`, `upgrade`, `image`       | vacío (compilar el agente aquí) | comentada               |
| `MIKROSCOPE_REMOTE_IMAGE` | `--remote-image` | los verbos de despliegue                              | vacío (subir un tar)            | comentada               |
| `MIKROSCOPE_NAME`         | `--name`         | los verbos de despliegue                              | `mikroscope`                    | comentada               |
| `MIKROSCOPE_VETH` | `--veth` | los verbos de despliegue | `veth-mikroscope` | comentada |
| `MIKROSCOPE_SUBNET` | `--subnet` | los verbos de despliegue, `record`, `forward` | `172.30.10.0/30` | comentada |
| `MIKROSCOPE_IFACE_LIST` | `--iface-list` | los verbos de despliegue | `LAN`; `none` no se une a ninguna lista | comentada |
| `MIKROSCOPE_ADDR_LIST` | `--addr-list` | los verbos de despliegue | `LANs`; `none` no se une a ninguna lista | comentada |
| `MIKROSCOPE_DISK`         | `--disk`         | los verbos de despliegue                              | vacío (flash interna)           | comentada, `tmpfs`      |
| `MIKROSCOPE_LAN_ADDRESS`  | `--lan-address`  | los verbos de despliegue                              | vacío                           | comentada               |
| `MIKROSCOPE_TOKEN`        | `--token`        | los verbos de despliegue, `record`, `forward`         | vacío                           | comentada               |

`MIKROSCOPE_SSH_OPTIONS` es una lista de pares `Clave=valor` separados por
comas, lo mismo que repetir `--ssh-option`; el primer `--ssh-option` en la
línea de órdenes sustituye la lista entera. [CLI](https://docs.jmrp.io/mikroscope/es/reference/cli/) tiene las claves
que admite.

`MIKROSCOPE_TOKEN` es dos cosas a la vez. Para `install` y `upgrade` es el
token que se escribe en la envlist del agente, y que el agente pasa entonces a
exigir; la CLI entrega a ssh la orden que lo escribe por su entrada estándar,
nunca en una línea de órdenes. `status` y `uninstall` no lo necesitan. Para
`record`, `forward` y la sección de salud de `doctor` es el token que se envía
al agente por el transporte directo; sin él, `doctor` se salta el anillo de un
agente que lo tenga. La envlist no
es un almacén de secretos: cualquier usuario de RouterOS con la política `read`
puede listar la envlist de todos los contenedores por la API, y por eso el
token es la única credencial que va ahí.

## Agente y API

| Variable                  | Opción         | La leen                     | Significado                                                                                           |
| ------------------------- | -------------- | --------------------------- | ----------------------------------------------------------------------------------------------------- |
| `MIKROSCOPE_API_ADDR`     | `--api`        | `record`, `mark`, `forward` | `host:port` de la API binaria de RouterOS; [`.env.example`](https://github.com/jmrplens/mikroscope/blob/main/.env.example) tiene `192.168.88.1:8728`                   |
| `MIKROSCOPE_API_USER`     | `--api-user`   | `record`, `mark`, `forward` | el usuario dedicado de la API                                                                         |
| `MIKROSCOPE_API_PASSWORD` | ninguna        | `record`, `mark`, `forward` | la contraseña de ese usuario; a propósito no hay opción                                               |
| `MIKROSCOPE_INTERFACES`   | `--interfaces` | `forward`                   | interfaces separadas por comas para `monitor-traffic`; [`.env.example`](https://github.com/jmrplens/mikroscope/blob/main/.env.example) tiene `bridge,ether1` comentada |

El relay, `--log-markers` y la capa de la API fallan, o en el caso de `forward`
se desactivan con un aviso, salvo que estén definidas las tres: `--api`,
`--api-user` y `MIKROSCOPE_API_PASSWORD`. [El usuario de la
API](https://docs.jmrp.io/mikroscope/es/security/api-user/) tiene la política que necesita ese
usuario.

## Destinos

| Variable                   | Opción          | Por defecto en el código | Significado                                                                             |
| -------------------------- | --------------- | ------------------------ | --------------------------------------------------------------------------------------- |
| `MIKROSCOPE_INFLUX_URL`    | `--influx`      | vacío                    | servidor de InfluxDB 3, `http://host:8181`; una URL de escritura completa se toma tal cual |
| `MIKROSCOPE_INFLUX_DB`     | `--influx-db`   | vacío                    | la base de datos a la que escribe `--influx`, cuando `--influx` es solo un servidor      |
| `MIKROSCOPE_POSTGRES_DSN`  | `--postgres`    | vacío                    | cadena de conexión de PostgreSQL para el destino SQL que conecta                        |
| `MIKROSCOPE_LOKI_URL`      | `--loki`        | vacío                    | URL de push de Loki                                                                     |
| `MIKROSCOPE_LOKI_TENANT`   | `--loki-tenant` | vacío                    | `X-Scope-OrgID`                                                                         |
| `MIKROSCOPE_OTLP_URL`      | `--otlp`        | vacío                    | endpoint de métricas OTLP/HTTP                                                          |
| `MIKROSCOPE_GRAPHITE_ADDR` | `--graphite`    | vacío                    | `host:port` de texto plano de carbon                                                    |
| `MIKROSCOPE_ELASTIC_URL`   | `--elastic`     | vacío                    | URL base de Elasticsearch u OpenSearch                                                  |
| `MIKROSCOPE_TELEGRAF_URL`  | `--telegraf`    | vacío                    | URL del receptor de Telegraf (`http://`, `tcp://` o `udp://`)                           |
| `MIKROSCOPE_HOST_TAG`      | `--host-tag`    | `router`                 | etiqueta de host en cada punto de cada destino; [`.env.example`](https://github.com/jmrplens/mikroscope/blob/main/.env.example) la tiene comentada |

Basta con definir la variable de URL de un destino para activarlo: `forward`
construye todos los destinos cuya opción acaba no vacía, venga de la línea de
órdenes o de la variable. Todos estos nombres llevan el prefijo `MIKROSCOPE_`:
un `LOKI_URL` a secas no lo lee nada.

## Variables de credenciales

Una opción se ve en `ps` y en el historial de la shell, así que todos los
secretos que usa la CLI se leen solo del entorno.

| Variable                    | Se usa para                                            |
| --------------------------- | ------------------------------------------------------ |
| `MIKROSCOPE_API_PASSWORD`   | el usuario de la API de RouterOS                       |
| `MIKROSCOPE_INFLUX_TOKEN`   | InfluxDB, enviado como `Authorization: Bearer <token>` |
| `MIKROSCOPE_LOKI_TOKEN`     | Loki                                                   |
| `MIKROSCOPE_OTLP_TOKEN`     | el endpoint OTLP                                       |
| `MIKROSCOPE_ELASTIC_AUTH`   | Elasticsearch u OpenSearch                             |
| `MIKROSCOPE_TELEGRAF_TOKEN` | el receptor HTTP de Telegraf                           |
| `GRAFANA_TOKEN`             | `dashboards publish`, `import` y `check`, `forward --grafana`, `uninstall --targets dashboard` |

## Grafana

`GRAFANA_TOKEN` es el único token de Grafana que usan todos los verbos, sin prefijo, y ninguna opción
lo recibe. El Grafana en sí es `--grafana`, y cada verbo busca su valor por defecto en un orden
distinto:

| Verbo                                                 | Lee, después de `--grafana`                    |
| ----------------------------------------------------- | ---------------------------------------------- |
| `dashboards publish`, `uninstall --targets dashboard` | `MIKROSCOPE_GRAFANA_URL`, y luego `GRAFANA_URL` |
| `dashboards import`, `dashboards check`               | `GRAFANA_URL`, y luego `MIKROSCOPE_GRAFANA_URL` |
| `forward`                                             | solo `MIKROSCOPE_GRAFANA_URL`                  |

`forward` nunca lee `GRAFANA_URL`, así que un `GRAFANA_URL` por sí solo no hace que el colector
publique. Otras herramientas de Grafana leen también ese nombre y `GRAFANA_TOKEN`, y un colector
arrancado desde una shell preparada para una de ellas empezaría a escribir una carpeta, una fuente de
datos y un dashboard en ese Grafana sin que nadie se lo pidiera. [`.env.example`](https://github.com/jmrplens/mikroscope/blob/main/.env.example) lleva
todas las variables de Grafana, comentadas, en sus dos bloques de Grafana.

`forward`, `dashboards publish` y `uninstall` admiten `--grafana` y las opciones de abajo, y sus
variables **sí** llevan el prefijo, porque son ajustes del colector y no de `dashboards import` y
`check`:

| Variable                                | Opción                         | Valor por defecto | Significado                                                                                                     |
| --------------------------------------- | ------------------------------ | ----------------- | --------------------------------------------------------------------------------------------------------------- |
| `MIKROSCOPE_GRAFANA_URL`                | `--grafana`                    | vacío             | el Grafana en el que publicar; vacío no publica nada                                                            |
| `MIKROSCOPE_GRAFANA_FOLDER`             | `--grafana-folder`             | `mikroscope`      | carpeta donde publicar; una variable vacía se ignora, así que la carpeta General se pide con `--grafana-folder ""` |
| `MIKROSCOPE_GRAFANA_DATASOURCE_UID`     | `--grafana-datasource-uid`     | vacío             | adoptar esta fuente de datos en lugar de crear una; un almacén por ejecución                                    |
| `MIKROSCOPE_GRAFANA_DATASOURCE_URL`     | `--grafana-datasource-url`     | vacío             | la dirección que consulta Grafana, para los destinos que no pueden saberla; un almacén por ejecución            |
| `MIKROSCOPE_GRAFANA_DATASOURCE_SSLMODE` | `--grafana-datasource-sslmode` | vacío             | sslmode para la fuente de datos de PostgreSQL: `disable`, `require`, `verify-ca` o `verify-full`, y nada más |

`uninstall` solo lee `MIKROSCOPE_GRAFANA_URL` y `MIKROSCOPE_GRAFANA_DATASOURCE_UID` de estas, e
ignora las demás.

Una ejecución que publica más de un almacén y fija `MIKROSCOPE_GRAFANA_DATASOURCE_UID` o
`MIKROSCOPE_GRAFANA_DATASOURCE_URL` se rechaza antes de hacer ninguna petición: cada una nombra una
sola fuente de datos. Publica aparte, con `dashboards publish`, el almacén que la necesita ([Publicación en
Grafana](https://docs.jmrp.io/mikroscope/es/reference/cli/#publicar-en-grafana)).

La publicación se niega a escribir sin `GRAFANA_TOKEN`: hay Grafanas que aceptan una petición
anónima, y uno que lo hiciera escribiría como aquel por quien el servidor tome a quien pregunta.
`--grafana-dry-run` no escribe nada ni envía ninguna petición, así que funciona sin el token.

## Envlist del agente

El agente lee estas variables del entorno de su contenedor al arrancar. Un
valor no válido le hace imprimir una línea que empieza por
`mikroscope-agent: bad configuration:`, que RouterOS guarda en su log bajo el
tema `container`, y salir con estado 2.

| Variable               | Por defecto en el agente                           | Acepta                           | Significado                                                                                                                                                                        |
| ---------------------- | -------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `RATE_HZ`              | `10`                                               | 1–100                            | cadencia del muestreador                                                                                                                                                           |
| `BUFFER_S`             | `60`                                               | 10–3600                          | longitud del anillo en segundos                                                                                                                                                    |
| `PORT`                 | `9123`                                             | 1–65535                          | puerto HTTP                                                                                                                                                                        |
| `ADDR`                 | vacío                                              | una dirección                    | dirección de escucha. Vacía escucha en `:PORT`, todas las direcciones del espacio de nombres de red del contenedor; `install` siempre la fija a la dirección de la veth del agente |
| `TOKEN`                | vacío                                              |                                  | cuando está definida, todas las rutas salvo `/healthz` exigen `Authorization: Bearer <token>`                                                                                      |
| `IRQ_TOP_K`            | `8`                                                | 0–64                             | líneas de interrupción que se guardan por muestra, las más ocupadas primero; `0` no envía líneas de interrupción ni `irq_total`                                                    |
| `MEM_LIMIT_MB`         | `14`                                               | 8–1024                           | límite blando de memoria de Go en MiB                                                                                                                                              |
| `FLOOR_HZ`             | `0`                                                | 0–1000                           | `0` mantiene los suelos por fuente; N pone todas las fuentes de nivel a N Hz y desactiva la emisión solo al cambiar                                                                |
| `SOURCES`              | vacío (todas las fuentes detectadas)               | nombres separados por comas      | restringe las fuentes opcionales a esta lista; `/proc/stat` se lee siempre                                                                                                         |
| `TRIGGERS`             | `softnet-drop,oom,kmsg<=3,reset,irq-err,flash-bad` | condiciones, separadas por comas | condiciones de disparo de captura                                                                                                                                                  |
| `CAPTURE_MB`           | `4`                                                | 0–256                            | presupuesto de bytes retenidos para capturas; `0` las desactiva                                                                                                                    |
| `CAPTURE_PRE_S`        | `5`                                                | 1–60                             | segundos que se guardan antes de la muestra en la que salta un disparo                                                                                                             |
| `CAPTURE_POST_S`       | `5`                                                | 1–60                             | segundos que se guardan después                                                                                                                                                    |
| `CAPTURE_POLICY`       | `first`                                            | `first` o `last`                 | con el presupuesto lleno: `first` rechaza la captura nueva, `last` expulsa la más antigua                                                                                          |
| `TRIGGER_REFRACTORY_S` | `10`                                               | 0–3600                           | tiempo de silencio por condición después de saltar                                                                                                                                 |
| `PROC_ROOT`            | `/proc`                                            | una ruta                         | de dónde se lee `/proc`; el log del kernel es `/dev/kmsg` solo cuando esto es `/proc`, y si no `dev/kmsg` junto al directorio dado                                                 |
| `SYS_ROOT`             | `/sys`                                             | una ruta                         | de dónde se lee `/sys`                                                                                                                                                             |

Los nombres que acepta `SOURCES` son las fuentes opcionales que informa
`/capabilities`: `meminfo`, `loadavg`, `softnet`, `softirqs`, `interrupts`,
`vmstat`, `psi`, `schedstat`, `self`, `buddyinfo`, `yaffs`, `diskstats`,
`slabinfo`, `kmsg`, `thermal`, `cpufreq`, `mtd` y `perf`. Las condiciones de
disparo y qué compara cada una están en [Captura por
disparo](https://docs.jmrp.io/mikroscope/es/record/triggers/).

Antes de escuchar, el agente compara el anillo con el propio `memory.max` del
contenedor: `RATE_HZ × BUFFER_S × 3 456 B` más `CAPTURE_MB` tiene que caber, o
se niega a arrancar y nombra las tres variables y `--memory-max`. Si esa cifra
supera la mitad de `MEM_LIMIT_MB`, arranca y registra un aviso con el límite al
que subir. 3 456 B no es una medida en sí: es la clase de tamaño del asignador
de Go que sirve una línea con todas las fuentes activas
(3 230 B, 4 núcleos, `IRQ_TOP_K` 8), porque es esa
clase lo que se le carga al heap. Una placa con más núcleos o más líneas de
interrupción escribe líneas más largas, así que la comprobación peca de
permisiva. Cuando el agente no puede leer su `memory.max`, la
comprobación que lo niega no se ejecuta.

### Escrito por install

`install` y `upgrade` escriben la envlist `<name>-env` con estas entradas, en
este orden, y nada más. La forma de la instalación (sus listas, su disco y su
exposición) no está en la envlist: `install` la registra en el manifiesto de la
instalación, `mikroscope/<name>.manifest.txt` en el disco de la instalación,
que tampoco guarda ningún secreto.

Las entradas que install escribe en la envlist del agente:

| Clave | Se escribe | Viene de | Contiene |
| --- | --- | --- | --- |
| `MIKROSCOPE_TAG` | siempre | `--name` | la marca de propiedad `mikroscope:<name> (managed by mikroscope)`, que se escribe la primera y se borra la última; el agente la ignora |
| `RATE_HZ` | siempre | `--rate`, por defecto `10`, 1–100 | la cadencia del muestreador, en Hz |
| `BUFFER_S` | siempre | `--buffer`, por defecto `60`, 10–3600 | la longitud del anillo, en segundos |
| `PORT` | siempre | `--port`, por defecto `9123`, 1–65535 | el puerto HTTP del agente |
| `ADDR` | siempre | `--subnet` | la dirección del agente, la `.2` de la /30; el agente solo escucha ahí |
| `MEM_LIMIT_MB` | siempre | `--mem-limit-mb`, 8–1024 | el límite blando de memoria de Go del agente, en MiB; se deriva del anillo (cadencia × búfer × línea, × 2,5, con un mínimo de 16 MiB y un máximo de tres cuartos de `--memory-max` mientras en él aún quepa el anillo) salvo que lo fije `--mem-limit-mb` |
| `FLOOR_HZ` | solo cuando es mayor que 0 | `--floor-hz`, por defecto `0`, 0–1000 | una sola cadencia para todas las fuentes de nivel, en Hz |
| `CAPTURE_MB` | siempre | `--capture-mb`, por defecto `4`, 0–256 | el presupuesto de capturas por disparo, en MiB; `0` las desactiva |
| `TRIGGERS` | solo cuando se da | `--triggers` | las condiciones de disparo; sin ella, el agente usa su conjunto por defecto |
| `TOKEN` | solo cuando se da | `--token` | el token bearer que exige el agente, de `--token` o `MIKROSCOPE_TOKEN`, con o sin `--expose` |

> **Los valores por defecto del agente y los de install no son los mismos**
>
> Un agente arrancado sin ninguna entrada en la envlist usa un límite blando de memoria de 14 MiB. El
> anillo son **60 s** por defecto. Lo que compra es cuánto tiempo puede estar ausente el colector
> antes de que se pierdan muestras, y no es una ventana que nadie lea: el colector lo vacía dos veces
> por segundo. 60 s cubren el reinicio de cualquiera de las dos partes en una LAN y cuestan 2,0 MiB
> de anillo a 10 Hz. Un enlace que caiga más tiempo lo sube con `--buffer`, y el límite de memoria lo
> sigue porque se deriva del anillo.
>
> `install` **deriva** `MEM_LIMIT_MB` del anillo: ritmo × búfer × tamaño de línea, por 2,5, con suelo
> de 16 MiB y tope en tres cuartos de `memory-max`. La instalación por defecto escribe
> `MEM_LIMIT_MB=16` para un anillo de 60 s a 10 Hz. Un número fijo no puede ser correcto para todos
> los ritmos, y por debajo de unas dos veces el anillo el recolector de Go se ejecuta tan a menudo que
> la CPU por muestra se dispara
> ([Coste del agente](https://docs.jmrp.io/mikroscope/es/cost/#dimensiona-el-límite-de-memoria-según-los-datos)).
>
> `IRQ_TOP_K`, `CAPTURE_PRE_S`, `CAPTURE_POST_S`, `CAPTURE_POLICY`, `TRIGGER_REFRACTORY_S`,
> `SOURCES`, `PROC_ROOT` y `SYS_ROOT` no tienen opción, así que un agente instalado funciona con sus
> valores por defecto.

## Véase también

- [CLI](https://docs.jmrp.io/mikroscope/es/reference/cli/): todas las opciones, incluidas las que no fija
  ninguna variable.
- [Usuario de la API](https://docs.jmrp.io/mikroscope/es/security/api-user/): el usuario de RouterOS detrás de
  `MIKROSCOPE_API_USER`.
- [Cadencias de las fuentes](https://docs.jmrp.io/mikroscope/es/limits/source-floors/): lo que anula `FLOOR_HZ`.
- [Coste del agente](https://docs.jmrp.io/mikroscope/es/cost/): por qué el límite de memoria se dimensiona según
  el anillo.
