> For the complete documentation index, see [llms.txt](https://docs.facephi.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.facephi.com/sdks/backend-sdk/finger/installation/installation_instructions.md).

# Instalación y despliegue del servicio

## Requisitos previos

Antes de instalar Finger Service, asegúrate de tener:

* Docker instalado y en ejecución
* Un archivo de licencia válido de Facephi
* Acceso al registro Docker de Facephi

## Acceso al registro Docker

Inicia sesión en el registro Docker de Facephi:

```bash
docker login facephicorp.jfrog.io
```

Necesitarás las credenciales proporcionadas por Facephi.

## Instalación

{% hint style="danger" %}
**Aviso de cambio incompatible (2.0.0)** A partir de la versión 2.0.0, el contrato de payload REST de los endpoints de operaciones de huella dactilar ha cambiado y no es compatible con los clientes 1.x.x. Las rutas heredadas `POST /api/v1/finger/create-template` y `POST /api/v1/finger/verify` ya no están expuestas. Planifica la migración del cliente antes de actualizar las imágenes del servicio.
{% endhint %}

La imagen Docker ya incluye los componentes internos de procesamiento de huella dactilar, así que no es necesario montar archivos adicionales en tiempo de ejecución.

### Descargar la imagen Docker

```bash
docker pull facephicorp.jfrog.io/docker-pro-fphi/facephi-finger-service:2.2.1
```

Sustituye `2.2.1` por la versión deseada.

### Preparar los directorios

Crea los directorios para la licencia, la configuración y los logs:

```bash
mkdir -p ~/facephi_finger/{license,config,logs}
```

### Colocar el archivo de licencia

Copia tu archivo de licencia al directorio de licencia:

```bash
cp your-license.lic ~/facephi_finger/license/license.lic
chmod 644 ~/facephi_finger/license/license.lic
```

La ruta de licencia por defecto del servicio es `/app/license/license.lic`.

## Despliegue con Docker Compose

Crea `docker-compose.yml`:

```yaml
services:
  finger-service:
    image: facephicorp.jfrog.io/docker-pro-fphi/facephi-finger-service:2.2.1
    container_name: facephi-finger-service
    ports:
      - "6982:6982"
    volumes:
      - ~/facephi_finger/license:/app/license
      - ~/facephi_finger/config:/app/config
      - ~/facephi_finger/logs:/app/logs
    environment:
      - FACEPHI_FINGER_ENGINE_ROLE=master
      - FACEPHI_FINGER_ENGINE_SERVICES=Master,FingerTC,Verifier
      - FACEPHI_FINGER_ENGINE_MASTER=127.0.0.1
      # Protección JWT opcional
      # - FACEPHI_FINGER_REST_AUTH_ENABLED=true
      # - FACEPHI_FINGER_REST_AUTH_JWT_SECRET=shared-secret
      # - FACEPHI_FINGER_REST_AUTH_ACCEPT_AUTHORIZATION_HEADER=true
      # - FACEPHI_FINGER_REST_AUTH_ACCEPT_API_KEY_HEADER=true
      # - FACEPHI_FINGER_REST_AUTH_API_KEY_HEADER_NAME=x-api-key
    restart: unless-stopped
```

Los valores de `FACEPHI_FINGER_ENGINE_*` del ejemplo son los que la imagen usa por defecto en un despliegue de un solo nodo: puedes omitirlos.

### Iniciar el servicio

```bash
docker compose up -d
```

### Verificar el despliegue

```bash
# Check container status
docker ps | grep finger-service

# Check service health
curl http://localhost:6982/api/v1/finger/health

# Check version
curl http://localhost:6982/api/v1/finger/version

# Check operational metrics
curl http://localhost:6982/api/v1/finger/metrics

# View logs
docker logs facephi-finger-service
```

Al arrancar, el servicio obtiene la licencia, pone en marcha los componentes internos de procesamiento de huella y espera a que estén listos antes de empezar a aceptar peticiones en el puerto `6982`:

* En un nodo `master`, espera 60 segundos y después comprueba hasta 6 veces, cada 10 segundos, que todos los componentes de procesamiento están registrados.
* En un nodo `worker`, espera 30 segundos y después comprueba hasta 6 veces, cada 10 segundos, que los servicios de procesamiento locales aceptan conexiones.

Mientras tanto, las peticiones al puerto no obtienen respuesta aunque el contenedor aparezca como `Up`. Cuando el servicio está listo, el log muestra estas líneas:

```
License initialized successfully from: /app/license/license.lic
Finger service 2.2.1 started (role: master)
```

Si los componentes de procesamiento de un nodo `master` no están listos tras las 6 comprobaciones, el servicio registra `Engine failed to become healthy after 6 attempts` y arranca igualmente; en ese caso `GET /api/v1/finger/health` responde `Unhealthy` con `engine.healthy` igual a `false` hasta que estén disponibles. Si la licencia no se puede obtener, el servicio no llega a arrancar y el contenedor se detiene (ver «Solución de problemas»).

## Despliegue en clúster

Para escalar el procesamiento, Finger Service admite una topología multinodo con un nodo `master` y varios nodos `worker`:

* El nodo `master` (`FACEPHI_FINGER_ENGINE_ROLE=master`) recibe las peticiones de los clientes: expone la API completa y reparte el procesamiento de huellas entre los nodos del clúster.
* Los nodos `worker` (`FACEPHI_FINGER_ENGINE_ROLE=worker`) aportan capacidad de procesamiento y solo exponen los endpoints de gestión (`health`, `version`, `metrics`, `metrics/prometheus` y `config`). En ellos, `POST /api/v1/finger/extract` y `POST /api/v1/finger/authenticate` responden HTTP `404`.
* Todos los nodos deben apuntar al nodo `master` con `FACEPHI_FINGER_ENGINE_MASTER` y poder comunicarse entre sí por los puertos TCP `47500`-`47509`, que usan para descubrirse y formar el clúster.
* Todos los nodos necesitan acceso a un archivo de licencia válido.

### Variables de topología

Estas variables solo se configuran mediante variables de entorno; no forman parte de `config.json` ni se exponen a través de `GET|POST /api/v1/finger/config`:

| Variable                         | Por defecto                | Descripción                                                                                                                                        |
| -------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FACEPHI_FINGER_ENGINE_ROLE`     | `master`                   | Rol del nodo: `master` o `worker`. No distingue mayúsculas y minúsculas; un valor no válido se interpreta como `master`                            |
| `FACEPHI_FINGER_ENGINE_SERVICES` | `Master,FingerTC,Verifier` | Servicios internos de procesamiento que arranca el nodo, separados por comas. Valores admitidos: `Master`, `FingerTC` y `Verifier`                 |
| `FACEPHI_FINGER_ENGINE_MASTER`   | `127.0.0.1`                | Dirección del nodo `master` que usan los nodos para formar el clúster. Si se indica una lista separada por comas, solo se usa la primera dirección |

Combinaciones habituales:

| Topología    | Nodo     | `FACEPHI_FINGER_ENGINE_ROLE` | `FACEPHI_FINGER_ENGINE_SERVICES` | `FACEPHI_FINGER_ENGINE_MASTER` |
| ------------ | -------- | ---------------------------- | -------------------------------- | ------------------------------ |
| Un solo nodo | Único    | `master`                     | `Master,FingerTC,Verifier`       | `127.0.0.1`                    |
| Clúster      | `master` | `master`                     | `Master`                         | Dirección del nodo `master`    |
| Clúster      | `worker` | `worker`                     | `FingerTC,Verifier`              | Dirección del nodo `master`    |

### Ejemplo con Docker Compose

```yaml
services:
  master:
    image: facephicorp.jfrog.io/docker-pro-fphi/facephi-finger-service:2.2.1
    container_name: finger-master
    ports:
      - "6982:6982"
      - "47500-47509:47500-47509"
    volumes:
      - ~/facephi_finger/license:/app/license
      - ~/facephi_finger/config:/app/config
      - ~/facephi_finger/logs/master:/app/logs
    environment:
      - FACEPHI_FINGER_ENGINE_ROLE=master
      - FACEPHI_FINGER_ENGINE_SERVICES=Master
      - FACEPHI_FINGER_ENGINE_MASTER=master
    networks:
      - finger-cluster

  finger-worker:
    image: facephicorp.jfrog.io/docker-pro-fphi/facephi-finger-service:2.2.1
    volumes:
      - ~/facephi_finger/license:/app/license
      - ~/facephi_finger/config:/app/config
    environment:
      - FACEPHI_FINGER_ENGINE_ROLE=worker
      - FACEPHI_FINGER_ENGINE_SERVICES=FingerTC,Verifier
      - FACEPHI_FINGER_ENGINE_MASTER=master
    networks:
      - finger-cluster
    depends_on:
      - master
    deploy:
      replicas: 2

networks:
  finger-cluster:
    driver: bridge
```

Para cambiar el número de nodos `worker`:

```bash
docker compose up -d --scale finger-worker=3
```

En cada nodo, `GET /api/v1/finger/health` y `GET /api/v1/finger/metrics` informan del rol (`engine.role`, `service.role`) y de los servicios de procesamiento configurados (`engine.servicesConfigured`). En los nodos `worker`, la comprobación de salud verifica que cada servicio de procesamiento local acepta conexiones.

## Configuración de la licencia

El archivo de licencia debe ubicarse en `/app/license/license.lic` dentro del contenedor.

### Formato del archivo de licencia

```ini
LICENSE_TYPE=         # MACHINE, SHARED, or LOCAL
LICENSE_BEHAVIOUR=    # ONLINE or OFFLINE
LICENSE_KEY=          # Your license key
```

Si falta `LICENSE_TYPE`, el servicio usa `SHARED`, y si falta `LICENSE_BEHAVIOUR`, usa `ONLINE`. La combinación `SHARED` + `OFFLINE` no está soportada: con ella el servicio no arranca.

**Campos opcionales:**

* `LICENSE_URLS=` — URLs de los servidores de licencia separadas por comas (obligatorio para el tipo `LOCAL`; ver [Licenciamiento offline con servidor local](/sdks/backend-sdk/finger/installation/offline_licensing.md))
* `LICENSE_OFFLINE_CODE=` — Contenido del fichero de respuesta de activación offline que entrega Facephi (obligatorio para `MACHINE` + `OFFLINE`)
* `LICENSE_ADDRS=` — Direcciones MAC, separadas por comas, con las que se calcula la huella del equipo en las licencias `MACHINE`

`LICENSE_ID` y `LICENSE_DATA`, si están presentes, son ignorados por el servicio, que lo avisa en el log. Las credenciales del producto Finger Service se incrustan en tiempo de compilación, por lo que estos campos deben omitirse en las plantillas de despliegue.

### Conectividad de la licencia

* Con las licencias `ONLINE`, el servidor debe disponer de acceso HTTPS saliente a los servidores de licencia asignados al despliegue. Si el tráfico saliente está restringido, solicita a Facephi la lista de acceso exacta para tu despliegue antes de la instalación.
* Para instalaciones sin acceso a Internet, usa un servidor local de licencias (ver [Licenciamiento offline con servidor local](/sdks/backend-sdk/finger/installation/offline_licensing.md)).

## Resumen de la API pública

{% hint style="danger" %}
**Nota de compatibilidad** Las rutas siguientes corresponden al contrato público 2.x. Los clientes construidos para 1.x.x deben migrarse antes de poder llamar correctamente a esta versión.
{% endhint %}

Endpoints expuestos por el servicio:

* `POST /api/v1/finger/extract` (solo en nodos `master`)
* `POST /api/v1/finger/authenticate` (solo en nodos `master`)
* `GET /api/v1/finger/version`
* `GET /api/v1/finger/health`
* `GET /api/v1/finger/metrics`
* `GET /api/v1/finger/metrics/prometheus`
* `GET|POST /api/v1/finger/config`

## Autenticación JWT

La autenticación JWT es opcional y está deshabilitada por defecto.

* Configúrala al inicio en `/app/config/config.json` con `auth_enabled`, `auth_jwt_secret`, `auth_accept_authorization_header`, `auth_accept_api_key_header` y `auth_api_key_header_name`
* Sobrescribe esos valores con las variables de entorno `FACEPHI_FINGER_REST_AUTH_*`
* Con la autenticación habilitada, todos los endpoints salvo `/health`, `/version`, `/metrics`, `/metrics/prometheus` y las peticiones `OPTIONS` exigen un JWT firmado con HS256 usando `auth_jwt_secret` y con el claim `exp`
* Define siempre `auth_jwt_secret` al habilitar la autenticación: si está vacío, todos los endpoints protegidos responden HTTP `401` con `Authentication secret is not configured`
* `GET /api/v1/finger/config` nunca devuelve las claves de autenticación JWT
* `POST /api/v1/finger/config` rechaza las claves de autenticación JWT; gestiónalas únicamente al inicio y reinicia el servicio para cambiarlas

El detalle de cómo se envía el token y de las respuestas `401` está en el apartado «Autenticación JWT» de la [descripción general del servicio](/sdks/backend-sdk/finger.md).

Ejemplo de configuración de inicio:

```json
{
  "auth_enabled": true,
  "auth_jwt_secret": "replace-with-secret",
  "auth_accept_authorization_header": true,
  "auth_accept_api_key_header": true,
  "auth_api_key_header_name": "x-api-key"
}
```

Ejemplo de variables de entorno:

```bash
export FACEPHI_FINGER_REST_AUTH_ENABLED=true
export FACEPHI_FINGER_REST_AUTH_JWT_SECRET=replace-with-secret
export FACEPHI_FINGER_REST_AUTH_ACCEPT_AUTHORIZATION_HEADER=true
export FACEPHI_FINGER_REST_AUTH_ACCEPT_API_KEY_HEADER=true
export FACEPHI_FINGER_REST_AUTH_API_KEY_HEADER_NAME=x-api-key
```

## Configuración del servicio

Crea `/app/config/config.json` para personalizar el comportamiento del servicio. El archivo es opcional: si no existe, el servicio arranca con los valores por defecto. Los parámetros que no incluyas conservan su valor por defecto.

### Ubicación del archivo de configuración

* **Ubicación por defecto:** `/app/config/config.json` (dentro del contenedor)
* **Montar un archivo externo:** Usa el mapeo de volúmenes en docker-compose

### Ejemplo de configuración completa (valores por defecto actuales)

```json
{
  "port": 6982,
  "number_of_threads": 1,
  "connection_timeout": 60,
  "keep_alive_request_number": 0,
  "client_max_body_size": 100,
  "logger_level": "info",
  "logger_path": "",
  "logger_file_prefix": "app",
  "logger_rotation": "daily",
  "logger_max_files": 7,
  "metrics_enabled": true,
  "auth_enabled": false,
  "auth_jwt_secret": "",
  "auth_accept_authorization_header": true,
  "auth_accept_api_key_header": true,
  "auth_api_key_header_name": "x-api-key",
  "engine_connection_timeout": 10000,
  "engine_request_timeout": 60000,
  "engine_max_retries": 3,
  "engine_retry_delay": 1000,
  "engine_verify_ssl": false,
  "engine_verbose": false,
  "engine_pool_size": 4,
  "engine_url": "http://localhost:8080",
  "metrics_saturation_queue_p95_ms": 250,
  "metrics_saturation_min_inflight_ratio": 1.0,
  "metrics_error_rate_warning": 0.05,
  "metrics_idle_rps_threshold": 0.01,
  "metrics_quality_alert_window_seconds": 900
}
```

### Parámetros del servicio

| Parámetro                          | Tipo    | Por defecto | Descripción                                                                                                                                                                                |
| ---------------------------------- | ------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `port`                             | integer | 6982        | Puerto de escucha del servicio                                                                                                                                                             |
| `number_of_threads`                | integer | 1           | Hilos de trabajo para el procesamiento de solicitudes                                                                                                                                      |
| `connection_timeout`               | integer | 60          | Tiempo máximo, en segundos, que una conexión inactiva permanece abierta                                                                                                                    |
| `keep_alive_request_number`        | integer | 0           | Número máximo de peticiones por conexión keep-alive (0 = sin límite)                                                                                                                       |
| `client_max_body_size`             | integer | 100         | Tamaño máximo del cuerpo de la solicitud en MB. Las peticiones más grandes reciben HTTP `413`                                                                                              |
| `logger_path`                      | string  | ""          | Directorio donde se escriben los archivos de log. Vacío desactiva el log en archivo; los logs siguen saliendo por la salida estándar del contenedor                                        |
| `logger_file_prefix`               | string  | "app"       | Prefijo de la carpeta diaria de logs dentro de `logger_path` (`<prefijo>-<AAAA-MM-DD>`, con la fecha en UTC). Los caracteres distintos de letras, números, `-` y `_` se sustituyen por `_` |
| `logger_level`                     | string  | "info"      | Nivel de log: `trace`, `debug`, `info`, `warning`, `error`, `critical` u `off`. Un valor no reconocido se interpreta como `off`                                                            |
| `logger_rotation`                  | string  | "daily"     | Rotación de los archivos de log: `daily` u `hourly`. Cualquier otro valor se interpreta como `daily`                                                                                       |
| `logger_max_files`                 | integer | 7           | Número máximo de archivos de log a conservar (0 = sin límite)                                                                                                                              |
| `metrics_enabled`                  | boolean | true        | Recoge los contadores de tráfico, saturación y latencia que publican `GET /api/v1/finger/metrics` y `GET /api/v1/finger/metrics/prometheus`. Con `false`, esos bloques quedan a cero       |
| `auth_enabled`                     | boolean | false       | Requiere autenticación JWT para los endpoints protegidos                                                                                                                                   |
| `auth_jwt_secret`                  | string  | ""          | Secreto compartido HS256 usado para validar los JWT                                                                                                                                        |
| `auth_accept_authorization_header` | boolean | true        | Acepta `Authorization: Bearer <jwt>`                                                                                                                                                       |
| `auth_accept_api_key_header`       | boolean | true        | Acepta JWT en la cabecera de API key configurada                                                                                                                                           |
| `auth_api_key_header_name`         | string  | "x-api-key" | Nombre de la cabecera usada cuando la extracción de token por API key está habilitada                                                                                                      |

### Parámetros de procesamiento

Configuran la comunicación del servicio con sus componentes internos de procesamiento de huella dactilar. Solo se usan en los nodos `master`.

| Parámetro                   | Tipo    | Por defecto             | Descripción                                                                                                                             |
| --------------------------- | ------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `engine_connection_timeout` | integer | 10000                   | Timeout de conexión (ms)                                                                                                                |
| `engine_request_timeout`    | integer | 60000                   | Timeout de solicitud (ms)                                                                                                               |
| `engine_max_retries`        | integer | 3                       | Número máximo de reintentos de una petición que no obtiene respuesta (error de conexión o timeout) o que recibe un `5xx`, `408` o `429` |
| `engine_retry_delay`        | integer | 1000                    | Retardo inicial entre reintentos (ms); se duplica en cada reintento                                                                     |
| `engine_verify_ssl`         | boolean | false                   | Verifica los certificados SSL de la comunicación con los componentes de procesamiento                                                   |
| `engine_verbose`            | boolean | false                   | Habilita el logging HTTP detallado de la comunicación con los componentes de procesamiento                                              |
| `engine_pool_size`          | integer | 4                       | Tamaño del pool de conexiones                                                                                                           |
| `engine_url`                | string  | `http://localhost:8080` | URL base de los componentes internos de procesamiento                                                                                   |

### Umbrales de las métricas operativas

Estos parámetros solo cambian cómo `GET /api/v1/finger/metrics` clasifica el estado del servicio (`state.classification`) y qué alertas publica; no cambian el procesamiento de las huellas.

| Parámetro                               | Tipo    | Por defecto | Descripción                                                                                                                                                |
| --------------------------------------- | ------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `metrics_saturation_queue_p95_ms`       | integer | 250         | Percentil 95 de la latencia de cola (1 minuto, ms) a partir del cual la cola se considera saturada                                                         |
| `metrics_saturation_min_inflight_ratio` | number  | 1.0         | Fracción de `number_of_threads` que deben superar las peticiones en curso para considerar el servicio saturado                                             |
| `metrics_error_rate_warning`            | number  | 0.05        | Tasa de error de 5 minutos (respuestas `4xx` y `5xx`) a partir de la cual el servicio se clasifica como `degraded` y se activa la alerta `HIGH_ERROR_RATE` |
| `metrics_idle_rps_threshold`            | number  | 0.01        | Peticiones por segundo de 15 minutos por debajo de las cuales el servicio se clasifica como `idle`                                                         |
| `metrics_quality_alert_window_seconds`  | integer | 900         | Ventana, en segundos, durante la que una extracción de baja calidad mantiene activa la alerta `LOW_QUALITY`. Con `0`, la alerta se desactiva               |

Un valor que no se pueda interpretar, o negativo, se descarta con un aviso en el log: si viene de una variable de entorno se usa el de `config.json`, y si tampoco es válido, el valor por defecto.

### Variables de entorno

Las variables de entorno tienen prioridad sobre `config.json`:

* Los parámetros del servicio, los de autenticación JWT y los umbrales `metrics_*` se sobrescriben con `FACEPHI_FINGER_REST_<PARÁMETRO EN MAYÚSCULAS>`, por ejemplo `FACEPHI_FINGER_REST_NUMBER_OF_THREADS` o `FACEPHI_FINGER_REST_METRICS_ERROR_RATE_WARNING`.
* Los parámetros de procesamiento se sobrescriben con estas variables:

| Parámetro                   | Variable de entorno                  |
| --------------------------- | ------------------------------------ |
| `engine_url`                | `FACEPHI_FINGER_ENGINE_URL`          |
| `engine_connection_timeout` | `FACEPHI_FINGER_ENGINE_CONN_TIMEOUT` |
| `engine_request_timeout`    | `FACEPHI_FINGER_ENGINE_REQ_TIMEOUT`  |
| `engine_max_retries`        | `FACEPHI_FINGER_ENGINE_MAX_RETRIES`  |
| `engine_retry_delay`        | `FACEPHI_FINGER_ENGINE_RETRY_DELAY`  |
| `engine_verify_ssl`         | `FACEPHI_FINGER_ENGINE_VERIFY_SSL`   |
| `engine_verbose`            | `FACEPHI_FINGER_ENGINE_VERBOSE`      |
| `engine_pool_size`          | `FACEPHI_FINGER_ENGINE_POOL_SIZE`    |

* La topología del clúster se configura con `FACEPHI_FINGER_ENGINE_ROLE`, `FACEPHI_FINGER_ENGINE_SERVICES` y `FACEPHI_FINGER_ENGINE_MASTER` (ver «Variables de topología»).

Las variables booleanas de procesamiento aceptan `true`, `TRUE`, `yes`, `YES` o `1`, y `false`, `FALSE`, `no`, `NO` o `0`. Un valor no válido en una variable `FACEPHI_FINGER_ENGINE_*` se ignora con un aviso en el log y se mantiene el de `config.json` o el valor por defecto.

`GET /api/v1/finger/config` no refleja los valores establecidos mediante variables de entorno: muestra los de `config.json` y los valores por defecto.

### Actualizaciones dinámicas de configuración

Algunos parámetros pueden actualizarse sin reiniciar el servicio:

```bash
# Update configuration
curl -X POST http://localhost:6982/api/v1/finger/config \
  -H "Content-Type: application/json" \
  -d '{"config_json_string":"{\"engine_request_timeout\":45000,\"metrics_error_rate_warning\":0.1}"}'

# Verify current configuration
curl http://localhost:6982/api/v1/finger/config
```

`POST /api/v1/finger/config` espera un objeto JSON con el campo `config_json_string`, que contiene los parámetros que se quieren modificar serializados como una cadena JSON; los parámetros que no se incluyen conservan su valor.

* Solo se aplican en tiempo de ejecución `engine_url`, `engine_connection_timeout`, `engine_request_timeout`, `engine_max_retries`, `engine_retry_delay`, `engine_verify_ssl` y `engine_verbose` (únicamente en los nodos `master`), y los umbrales `metrics_*` con los que `GET /api/v1/finger/metrics` clasifica el estado del servicio.
* El resto de parámetros, incluido `engine_pool_size`, son de solo lectura: el endpoint los acepta, pero no cambian el comportamiento del servicio hasta que se actualizan en `config.json` o en las variables de entorno y se reinicia. La referencia de la operación `POST /api/v1/finger/config` incluye la tabla completa de parámetros.
* Si está definida la variable de entorno equivalente (`FACEPHI_FINGER_ENGINE_*` o `FACEPHI_FINGER_REST_METRICS_*`), su valor prevalece sobre el enviado al endpoint.
* `GET /api/v1/finger/config` devuelve todos los valores como cadenas de texto (por ejemplo, `"metrics_error_rate_warning": "0.1"`).
* Si `config_json_string` no contiene un objeto JSON válido, la petición responde HTTP `500` con `exception` igual a `Failed to load configuration from config_json_string` y la configuración no cambia.
* La configuración de autenticación JWT son ajustes de arranque: el endpoint rechaza con HTTP `400` las peticiones que la incluyen, con `message` igual a `JWT auth settings are not accessible from the config endpoint`. Actualiza `config.json` o las variables de entorno y reinicia el servicio.

**Nota:** Las actualizaciones en tiempo de ejecución son en memoria y no se persisten automáticamente en `config.json`. Para conservar los cambios tras un reinicio, actualiza el archivo montado en `/app/config/config.json`.

## Contadores de uso

`GET /api/v1/finger/metrics` informa, en el bloque `usage` (y en los campos `usageCountersEnabled`, `extractCount`, `authenticateCount` y `totalCount`), de los contadores de uso de la licencia para las operaciones `POST /api/v1/finger/extract` y `POST /api/v1/finger/authenticate` completadas con éxito.

* Los contadores dependen de la licencia. Si tu licencia no los tiene habilitados, `usage.enabled` vale `false` y los contadores valen `0`: consulta con Facephi si necesitas habilitarlos.
* Solo cuentan las operaciones completadas con éxito (HTTP `200`). Las peticiones inválidas y los errores de procesamiento no se cuentan.
* Los valores son los acumulados por la licencia, incluidos los incrementos de esta instancia que todavía no se han sincronizado con el servidor de licencias, así que no se reinician al reiniciar el servicio.
* `usage.total` (y `totalCount`) siempre es la suma de las dos operaciones.

## Solución de problemas

### El servicio no arranca

**Comprueba el archivo de licencia:**

```bash
docker exec facephi-finger-service ls -la /app/license/
```

**Comprueba los logs:**

```bash
docker logs facephi-finger-service
```

Si la licencia no se puede obtener al arrancar, el servicio termina y el contenedor se detiene. El log indica el motivo con líneas como estas:

| Línea de log                                                                    | Causa                                                                                         |
| ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `Failed to open license file: /app/license/license.lic`                         | El archivo de licencia no está en la carpeta montada o no se puede leer                       |
| `SHARED licenses with OFFLINE behaviour are not supported`                      | `license.lic` combina `LICENSE_TYPE=SHARED` con `LICENSE_BEHAVIOUR=OFFLINE`                   |
| `License validation failed [License initialization]: <motivo> (code: <código>)` | La licencia no es válida o no se ha podido validar (ver «Falla la validación de la licencia») |

### El servicio no responde justo después de arrancar

El servicio espera a que los componentes internos de procesamiento estén listos antes de abrir el puerto `6982`: al menos 60 segundos en un nodo `master` y 30 segundos en un nodo `worker`. Espera a ver `Finger service 2.2.1 started` en el log antes de enviar peticiones o de dar por fallida una sonda de arranque.

### `GET /api/v1/finger/health` responde `Unhealthy`

Revisa los campos de la respuesta:

* `license.valid` igual a `false`: la licencia no es válida; `license.statusMessage` indica el motivo (ver «Falla la validación de la licencia»).
* `license.valid` igual a `true` y `engine.healthy` igual a `false`: los componentes internos de procesamiento no están listos. Suele ocurrir mientras todavía se están poniendo en marcha, con `engine.lastError` igual a `Engine nodes not yet fully registered`; vuelve a consultarlo pasados unos segundos. Si persiste:
  * Revisa el log en busca de `Engine failed to become healthy after 6 attempts`.
  * Comprueba los recursos disponibles del contenedor con `docker stats facephi-finger-service`.
  * En un clúster, comprueba los valores de `FACEPHI_FINGER_ENGINE_ROLE`, `FACEPHI_FINGER_ENGINE_SERVICES` y `FACEPHI_FINGER_ENGINE_MASTER` de cada nodo, y que los nodos se alcanzan por los puertos `47500`-`47509`.
  * Mientras los componentes de procesamiento no responden, `POST /api/v1/finger/extract` y `POST /api/v1/finger/authenticate` devuelven HTTP `400` con `message` igual a `Fingerprint processing communication error: <detalle>`.

### Los endpoints de huella dactilar responden `404`

`POST /api/v1/finger/extract` y `POST /api/v1/finger/authenticate` solo existen en los nodos `master`. Comprueba que `FACEPHI_FINGER_ENGINE_ROLE` vale `master` (o no está definida) en el nodo que recibe las peticiones, y que el cliente no usa las rutas heredadas de 1.x.x (`/create-template`, `/verify`).

### Falla la validación de la licencia

* Verifica los permisos del archivo de licencia: `chmod 644 license.lic`
* Asegúrate de que el firewall permite el tráfico HTTPS saliente hacia los servidores de licencia
* Comprueba la fecha de caducidad de la licencia
* Confirma que la licencia corresponde al producto Finger Service

Si la licencia deja de ser válida con el servicio ya en marcha, `GET /api/v1/finger/health` responde `Unhealthy` con `license.valid` igual a `false`, `GET /api/v1/finger/version` añade `Invalid license: <motivo> (code: <código>)` al mensaje y `GET /api/v1/finger/metrics` clasifica el servicio como `unhealthy` con la alerta `LICENSE_EXPIRED`.

### Los contadores de uso no cambian

* Confirma que `GET /api/v1/finger/metrics` devuelve `usage.enabled: true`. Si devuelve `false`, tu licencia no tiene habilitados los contadores de uso: consúltalo con Facephi.
* Recuerda que solo cuentan las operaciones completadas con éxito.

### Problemas de rendimiento

* Aumenta `engine_pool_size` para gestionar más solicitudes concurrentes
* Ajusta `number_of_threads` según los núcleos de CPU disponibles
* Añade nodos `worker` al clúster para repartir el procesamiento
* Monitoriza el uso de recursos: `docker stats facephi-finger-service`
* Revisa `state.classification`, `saturation` y `latency` en `GET /api/v1/finger/metrics`

## Actualización

Para actualizar a una nueva versión:

{% hint style="warning" %}
**Importante** Actualizar de 1.x.x a 2.x es una migración de API con cambios incompatibles, no un reemplazo directo. Valida todas las llamadas del cliente frente al contrato público 2.x antes de promover el despliegue.
{% endhint %}

```bash
# Pull new version
docker pull facephicorp.jfrog.io/docker-pro-fphi/facephi-finger-service:<new_version>

# Update docker-compose.yml with new version
# Stop current service
docker compose down

# Start new version
docker compose up -d

# Verify upgrade
curl http://localhost:6982/api/v1/finger/version
```

**Revisa siempre las notas de versión antes de actualizar.**
