> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Instalar Matcher

> Despliega Matcher en local con Docker Compose o en producción con el Helm chart oficial. Configura PostgreSQL, Redis, RabbitMQ y el almacenamiento de objetos.

Matcher automatiza la conciliación financiera entre varias fuentes de datos, elimina el trabajo manual de coincidencia y ofrece un registro de auditoría completo para cada transacción. Esta guía te lleva por el despliegue de Matcher en entornos de desarrollo y producción.

<Note>
  Matcher está disponible para clientes con licencia. Lerian mantiene su repositorio de forma interna. Las instrucciones siguientes suponen que ya tienes acceso a los archivos del proyecto Matcher necesarios.
</Note>

## Docker compose (desarrollo)

***

Docker Compose es el enfoque recomendado para desarrollo y pruebas en local.

### 1. Accede al proyecto Matcher

Desde el directorio del proyecto Matcher:

```bash theme={null}
cd matcher
```

### 2. Configura el entorno

El archivo `docker-compose.yml` incluye valores predeterminados razonables para el desarrollo local. Puedes sobrescribir cualquier valor definiendo variables de entorno en tu shell o creando un archivo `.env` en la raíz del proyecto.

Consulta [Variables de entorno](#environment-variables) para más detalles sobre los ajustes disponibles.

### 3. Inicia los servicios

Inicia los servicios de infraestructura necesarios:

```bash theme={null}
docker-compose up -d postgres redis rabbitmq
```

Espera hasta que todos los servicios reporten un estado saludable:

```bash theme={null}
docker-compose ps
```

Inicia la aplicación Matcher:

```bash theme={null}
docker-compose up -d app
```

Para iniciar todos los servicios de una vez:

```bash theme={null}
docker-compose up -d
```

### 4. Verifica la instalación

Lista los contextos de configuración para confirmar que Matcher está activo. En una instalación nueva, la respuesta paginada por cursor tiene un arreglo `items` vacío:

```bash theme={null}
curl -H "Authorization: Bearer $TOKEN" http://localhost:4018/v1/contexts
```

Luego verifica las dependencias necesarias mediante el endpoint público de readiness:

```bash theme={null}
curl http://localhost:4018/readyz
```

El endpoint devuelve `200` cuando cada dependencia necesaria está lista. Devuelve `503` con detalles por verificación cuando una dependencia necesaria no está disponible.

### Servicios de Docker compose

El `docker-compose.yml` predeterminado incluye:

| Servicio | Puerto | Propósito |
| - | - | - |
| `postgres` | 5432 | Base de datos primaria PostgreSQL |
| `postgres-replica` | 5433 | Réplica de lectura de PostgreSQL |
| `redis` | 6379 | Caché Valkey (compatible con Redis) |
| `rabbitmq` | 5672, 15672 | RabbitMQ (AMQP y UI de administración) |
| `seaweedfs` | 8333, 9333 | Almacenamiento de objetos compatible con S3 |
| `app` | 4018 | API de Matcher |

### Desarrollo con hot reload

Para desarrollo activo, usa:

```bash theme={null}
make dev
```

Esto inicia Matcher con la recarga en vivo habilitada usando Air.

## Kubernetes / helm (producción)

***

Se recomienda que los despliegues de producción usen el Helm chart oficial.

### Requisitos previos

* Kubernetes 1.28+
* Helm 3.12+
* `kubectl` configurado para el cluster destino

### 1. Crea un namespace

```bash theme={null}
kubectl create namespace matcher
```

### 2. Configura los valores

Crea un archivo `values.yaml` con la configuración de tu despliegue:

```yaml theme={null}
replicaCount: 2

image:
 repository: lerianstudio/matcher
 tag: "latest"
 pullPolicy: IfNotPresent

service:
 type: ClusterIP
 port: 4018

ingress:
 enabled: true
 className: nginx
 hosts:
 - host: matcher.example.com
 paths:
 - path: /
 pathType: Prefix
 tls:
 - secretName: matcher-tls
 hosts:
 - matcher.example.com

postgresql:
 external: true
 host: postgres.example.com
 port: 5432
 database: matcher
 username: matcher
 existingSecret: matcher-db-credentials
 existingSecretKey: password

redis:
 external: true
 host: redis.example.com
 port: 6379
 existingSecret: matcher-redis-credentials

rabbitmq:
 external: true
 host: rabbitmq.example.com
 port: 5672
 username: matcher
 existingSecret: matcher-rabbitmq-credentials

auth:
 enabled: true
 serviceAddress: https://auth.example.com

observability:
 enabled: true
 otelExporterEndpoint: http://otel-collector:4317

resources:
 requests:
 cpu: 500m
 memory: 512Mi
 limits:
 cpu: 2000m
 memory: 2Gi
```

### 3. Crea los secrets

Crea secrets de Kubernetes para las credenciales sensibles:

```bash theme={null}
kubectl create secret generic matcher-db-credentials \
 --from-literal=password=your-db-password \
 -n matcher

kubectl create secret generic matcher-redis-credentials \
 --from-literal=password=your-redis-password \
 -n matcher

kubectl create secret generic matcher-rabbitmq-credentials \
 --from-literal=password=your-rabbitmq-password \
 -n matcher
```

### 4. Instala el chart

```bash theme={null}
helm install matcher oci://registry-1.docker.io/lerianstudio/matcher-helm \
 --version 4.1.0 \
 --namespace matcher \
 --values values.yaml
```

### 5. Verifica el despliegue

```bash theme={null}
kubectl get pods -n matcher
kubectl get svc -n matcher
kubectl logs -f deployment/matcher -n matcher
```

### Actualización

Para actualizar un despliegue existente:

```bash theme={null}
helm upgrade matcher oci://registry-1.docker.io/lerianstudio/matcher-helm \
 --version 4.1.0 \
 --namespace matcher \
 --values values.yaml
```

<h2 id="environment-variables">
  Variables de entorno
</h2>

***

Las variables de entorno proporcionan la configuración de bootstrap de Matcher. Systemplane puede sobrescribir los ajustes mutables en tiempo de ejecución después del arranque.

### Aplicación

| Variable | Predeterminado | Descripción |
| - | - | - |
| `ENV_NAME` | `development` | Nombre del entorno de ejecución |
| `LOG_LEVEL` | `info` | Nivel de detalle del log |
| `DEPLOYMENT_MODE` | `local` | Modo de despliegue (`local`, `byoc`, `saas`) |
| `SERVER_ADDRESS` | `:4018` | Dirección de enlace del servidor HTTP |
| `HTTP_BODY_LIMIT_BYTES` | `104857600` | Tamaño máximo del cuerpo de la solicitud con buffer (bytes, 100 MiB de forma predeterminada) |

### CORS

| Variable | Predeterminado | Descripción |
| - | - | - |
| `CORS_ALLOWED_ORIGINS` | `http://localhost:3000` | Orígenes permitidos |
| `CORS_ALLOWED_METHODS` | `GET,POST,PUT,PATCH,DELETE,OPTIONS` | Métodos HTTP permitidos |
| `CORS_ALLOWED_HEADERS` | `Origin,Content-Type,Accept,Authorization,X-Request-ID` | Headers de solicitud permitidos |

### Base de datos (PostgreSQL)

| Variable | Predeterminado | Descripción |
| - | - | - |
| `POSTGRES_HOST` | `localhost` | Host de la base de datos primaria |
| `POSTGRES_PORT` | `5432` | Puerto de la base de datos primaria |
| `POSTGRES_USER` | `matcher` | Usuario |
| `POSTGRES_PASSWORD` | `matcher_dev_password` | Contraseña |
| `POSTGRES_DB` | `matcher` | Nombre de la base de datos |
| `POSTGRES_SSLMODE` | `disable` | Modo SSL |
| `POSTGRES_TLS_REQUIRED` | `false` | Exige TLS en el bootstrap |
| `POSTGRES_MAX_OPEN_CONNS` | `25` | Máximo de conexiones abiertas |
| `POSTGRES_MAX_IDLE_CONNS` | `5` | Máximo de conexiones inactivas |
| `POSTGRES_CONN_MAX_LIFETIME_MINS` | `30` | Vida máxima de la conexión (minutos) |
| `POSTGRES_CONN_MAX_IDLE_TIME_MINS` | `5` | Tiempo máximo de inactividad de la conexión (minutos) |
| `POSTGRES_CONNECT_TIMEOUT_SEC` | `10` | Timeout de conexión (segundos) |
| `POSTGRES_QUERY_TIMEOUT_SEC` | `30` | Timeout de consulta (segundos) |

### Réplica de la base de datos (PostgreSQL)

| Variable | Predeterminado | Descripción |
| - | - | - |
| `POSTGRES_REPLICA_HOST` | — | Host de la réplica |
| `POSTGRES_REPLICA_PORT` | — | Puerto de la réplica |
| `POSTGRES_REPLICA_USER` | — | Usuario de la réplica |
| `POSTGRES_REPLICA_PASSWORD` | — | Contraseña de la réplica |
| `POSTGRES_REPLICA_DB` | — | Nombre de la base de datos de la réplica |
| `POSTGRES_REPLICA_SSLMODE` | — | Modo SSL de la réplica |
| `POSTGRES_REPLICA_TLS_REQUIRED` | `false` | Exige TLS para la réplica |

### Caché (Redis)

| Variable | Predeterminado | Descripción |
| - | - | - |
| `REDIS_HOST` | `localhost:6379` | Dirección de Redis (host:port) |
| `REDIS_MASTER_NAME` | — | Nombre del master de Sentinel |
| `REDIS_PASSWORD` | — | Contraseña |
| `REDIS_DB` | `0` | Índice de la base de datos |
| `REDIS_TLS` | `false` | Habilita TLS |
| `REDIS_TLS_REQUIRED` | `false` | Exige TLS en el bootstrap |
| `REDIS_CA_CERT` | — | Ruta del certificado de CA |
| `REDIS_POOL_SIZE` | `10` | Tamaño del pool de conexiones |
| `REDIS_MIN_IDLE_CONNS` | `2` | Mínimo de conexiones inactivas |
| `REDIS_READ_TIMEOUT_MS` | `3000` | Timeout de lectura (milisegundos) |
| `REDIS_WRITE_TIMEOUT_MS` | `3000` | Timeout de escritura (milisegundos) |
| `REDIS_DIAL_TIMEOUT_MS` | `5000` | Timeout de conexión inicial (milisegundos) |

### Mensajería (RabbitMQ)

| Variable | Predeterminado | Descripción |
| - | - | - |
| `RABBITMQ_URI` | `amqp` | Esquema de URI (`amqp` o `amqps`) |
| `RABBITMQ_HOST` | `localhost` | Host del broker |
| `RABBITMQ_PORT` | `5672` | Puerto del broker |
| `RABBITMQ_USER` | `matcher_admin` | Usuario |
| `RABBITMQ_PASSWORD` | `matcher_dev_password` | Contraseña |
| `RABBITMQ_VHOST` | `/` | Host virtual |
| `RABBITMQ_HEALTH_URL` | `http://localhost:15672` | URL de la API de administración para los health checks |
| `RABBITMQ_ALLOW_INSECURE_HEALTH_CHECK` | `false` | Permite health check HTTP (sin TLS) |
| `RABBITMQ_TLS_REQUIRED` | `false` | Exige TLS en el bootstrap |

### Autenticación

| Variable | Predeterminado | Descripción |
| - | - | - |
| `PLUGIN_AUTH_ENABLED` | `false` | Habilita la autenticación |
| `PLUGIN_AUTH_ADDRESS` | — | URL del servicio de autenticación (la validación del token se delega aquí; Matcher no guarda ningún secreto JWT local) |

### Almacenamiento de objetos (compatible con S3)

| Variable | Predeterminado | Descripción |
| - | - | - |
| `OBJECT_STORAGE_ENDPOINT` | `http://localhost:8333` | URL del endpoint de S3 |
| `OBJECT_STORAGE_REGION` | `us-east-1` | Región de S3 |
| `OBJECT_STORAGE_BUCKET` | `matcher-exports` | Bucket para las exportaciones |
| `OBJECT_STORAGE_ACCESS_KEY_ID` | — | ID de la clave de acceso |
| `OBJECT_STORAGE_SECRET_ACCESS_KEY` | — | Clave de acceso secreta |
| `OBJECT_STORAGE_USE_PATH_STYLE` | `true` | Usa direccionamiento de estilo ruta |
| `OBJECT_STORAGE_ALLOW_INSECURE_ENDPOINT` | `false` | Permite endpoint HTTP (sin TLS) |
| `OBJECT_STORAGE_TLS_REQUIRED` | `false` | Exige TLS en el bootstrap |

### Observabilidad

| Variable | Predeterminado | Descripción |
| - | - | - |
| `ENABLE_TELEMETRY` | `false` | Habilita OpenTelemetry |
| `OTEL_RESOURCE_SERVICE_NAME` | `matcher` | Nombre del servicio para traces/métricas |
| `OTEL_LIBRARY_NAME` | `github.com/LerianStudio/matcher` | Nombre de la librería de instrumentación |
| `OTEL_RESOURCE_SERVICE_VERSION` | `1.1.0` | Versión del servicio |
| `OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT` | `development` | Etiqueta del entorno de despliegue |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `localhost:4317` | Endpoint del collector OTLP |
| `DB_METRICS_INTERVAL_SEC` | `15` | Intervalo de recolección de métricas de la base de datos |

### TLS

| Variable | Predeterminado | Descripción |
| - | - | - |
| `SERVER_TLS_CERT_FILE` | — | Ruta del certificado TLS |
| `SERVER_TLS_KEY_FILE` | — | Ruta de la clave privada TLS |
| `TLS_TERMINATED_UPSTREAM` | `false` | Confía en la terminación TLS upstream (p. ej., load balancer) |
| `TRUSTED_PROXIES` | — | Rangos CIDR de proxies de confianza |

### Rate limiting

| Variable | Predeterminado | Descripción |
| - | - | - |
| `RATE_LIMIT_ENABLED` | `true` | Habilita el rate limiting global |
| `RATE_LIMIT_MAX` | `100` | Máximo de solicitudes por ventana |
| `RATE_LIMIT_EXPIRY_SEC` | `60` | Ventana de rate limit (segundos) |
| `EXPORT_RATE_LIMIT_MAX` | `10` | Máximo de solicitudes de exportación por ventana |
| `EXPORT_RATE_LIMIT_EXPIRY_SEC` | `60` | Ventana de rate limit de exportación (segundos) |
| `DISPATCH_RATE_LIMIT_MAX` | `50` | Máximo de solicitudes de despacho por ventana |
| `DISPATCH_RATE_LIMIT_EXPIRY_SEC` | `60` | Ventana de rate limit de despacho (segundos) |
| `ADMIN_RATE_LIMIT_MAX` | `30` | Máximo de solicitudes de administración por ventana |
| `ADMIN_RATE_LIMIT_EXPIRY_SEC` | `60` | Ventana de rate limit de administración (segundos) |

### Swagger

| Variable | Predeterminado | Descripción |
| - | - | - |
| `SWAGGER_ENABLED` | `false` | Habilita la UI de Swagger |
| `SWAGGER_HOST` | — | Sobrescribe el host de la especificación de Swagger |
| `SWAGGER_SCHEMES` | `https` | Esquemas de la especificación de Swagger (separados por comas) |

### Idempotencia

| Variable | Predeterminado | Descripción |
| - | - | - |
| `IDEMPOTENCY_RETRY_WINDOW_SEC` | `300` | Ventana para reintentar solicitudes idempotentes fallidas (segundos) |
| `IDEMPOTENCY_SUCCESS_TTL_HOURS` | `168` | Cuánto tiempo se guardan en caché las claves de idempotencia completadas (horas) |
| `IDEMPOTENCY_HMAC_SECRET` | — | Secreto HMAC para firmar las claves de idempotencia (mín. 32 bytes) |

### Deduplicación

| Variable | Predeterminado | Descripción |
| - | - | - |
| `DEDUPE_TTL_SEC` | `3600` | TTL para las claves de deduplicación (segundos) |

### Outbox

| Variable | Predeterminado | Descripción |
| - | - | - |
| `OUTBOX_RETRY_WINDOW_SEC` | `300` | Enfriamiento antes de reintentar los eventos fallidos (segundos) |
| `OUTBOX_DISPATCH_INTERVAL_SEC` | `2` | Intervalo de sondeo del dispatcher (segundos) |

### Workers

| Variable | Predeterminado | Descripción |
| - | - | - |
| `EXPORT_WORKER_ENABLED` | `true` | Habilita el worker de exportación |
| `EXPORT_WORKER_POLL_INTERVAL_SEC` | `5` | Intervalo de sondeo del worker de exportación (segundos) |
| `EXPORT_WORKER_PAGE_SIZE` | `1000` | Filas por página de exportación |
| `EXPORT_PRESIGN_EXPIRY_SEC` | `3600` | Expiración de la URL prefirmada para las exportaciones (segundos) |
| `CLEANUP_WORKER_ENABLED` | `true` | Habilita el worker de limpieza |
| `CLEANUP_WORKER_INTERVAL_SEC` | `3600` | Intervalo del worker de limpieza (segundos) |
| `CLEANUP_WORKER_BATCH_SIZE` | `100` | Tamaño del lote de limpieza |
| `CLEANUP_WORKER_GRACE_PERIOD_SEC` | `3600` | Período de gracia antes de la limpieza (segundos) |
| `WEBHOOK_TIMEOUT_SEC` | `30` | Timeout de despacho del webhook (segundos) |
| `CALLBACK_RATE_LIMIT_PER_MIN` | `60` | Máximo de callbacks por sistema externo por minuto |

### Planificador

| Variable | Predeterminado | Descripción |
| - | - | - |
| `SCHEDULER_INTERVAL_SEC` | `60` | Intervalo de sondeo del planificador basado en cron (segundos) |

### Archivado

| Variable | Predeterminado | Descripción |
| - | - | - |
| `ARCHIVAL_WORKER_ENABLED` | `false` | Habilita el worker de archivado del log de auditoría |
| `ARCHIVAL_WORKER_INTERVAL_HOURS` | `24` | Intervalo de ejecución del archivado (horas) |
| `ARCHIVAL_HOT_RETENTION_DAYS` | `90` | Días para mantener los datos en almacenamiento caliente |
| `ARCHIVAL_WARM_RETENTION_MONTHS` | `24` | Meses para mantener los datos en almacenamiento tibio |
| `ARCHIVAL_COLD_RETENTION_MONTHS` | `84` | Meses para mantener los datos en almacenamiento frío |
| `ARCHIVAL_BATCH_SIZE` | `5000` | Filas por lote de archivado |
| `ARCHIVAL_STORAGE_BUCKET` | `matcher-archives` | Bucket de S3 para los archivos |
| `ARCHIVAL_STORAGE_CLASS` | `GLACIER` | Clase de almacenamiento de S3 para los archivos |
| `ARCHIVAL_PARTITION_LOOKAHEAD` | `3` | Conteo de anticipación de particiones |
| `ARCHIVAL_PRESIGN_EXPIRY_SEC` | `3600` | Expiración de la URL prefirmada para los archivos (segundos) |

### Discovery

Estos ajustes controlan Discovery, que lee de bases de datos externas mediante un motor de extracción en proceso embebido en Matcher, no un servicio de red aparte. Consulta [Discovery](/es/products/matcher/integrations/matcher-discovery) para ver cómo funciona.

| Variable | Predeterminado | Descripción |
| - | - | - |
| `FETCHER_DISCOVERY_INTERVAL_SEC` | `60` | Intervalo de sondeo de Discovery (segundos) |
| `FETCHER_SCHEMA_CACHE_TTL_SEC` | `300` | TTL de la caché de esquema (segundos) |
| `FETCHER_EXTRACTION_TIMEOUT_SEC` | `600` | Timeout de extracción (segundos) |
| `FETCHER_MAX_EXTRACTION_BYTES` | `2147483648` | Tamaño máximo del payload de extracción (bytes, 2 GiB de forma predeterminada) |
| `APP_ENC_KEY` | — | Clave maestra codificada en Base64 para el protector de credenciales del motor embebido |

### Infraestructura

| Variable | Predeterminado | Descripción |
| - | - | - |
| `INFRA_CONNECT_TIMEOUT_SEC` | `30` | Timeout de conexión al arranque de la infraestructura (segundos) |
| `HEALTH_CHECK_TIMEOUT_SEC` | `5` | Timeout heredado de sonda por verificación (segundos) |
| `HEALTH_CHECK_TIMEOUT_MS` | `800` | Timeout de sonda por verificación (milisegundos, preferido) |

<Note>
  Para los ajustes de despliegue multi-tenant, consulta [Modo multi-tenant](/es/products/matcher/configuration/matcher-multi-tenant). Para la gestión de la configuración en tiempo de ejecución, consulta [Configuración en tiempo de ejecución (Systemplane)](/es/products/matcher/configuration/matcher-systemplane).
</Note>

## Verifica la instalación

***

Valida que Matcher y sus dependencias necesarias estén listos:

```bash theme={null}
curl http://localhost:4018/readyz
```

El endpoint devuelve `200` cuando cada dependencia necesaria está lista. Devuelve `503` con detalles por verificación cuando una dependencia necesaria no está disponible. Configura las sondas de readiness de Kubernetes para que usen este endpoint.

## Solución de problemas

***

### Problemas comunes

<AccordionGroup>
  <Accordion title="Conexión rechazada a PostgreSQL">
    * **Causa:** PostgreSQL está caído o inalcanzable.
    * **Resolución:**

    1. Verifica que PostgreSQL esté activo: `docker-compose ps postgres`
    2. Revisa los valores de conexión en `.env`
    3. Prueba la conectividad: `nc -zv localhost 5432`
    4. Revisa los logs: `docker-compose logs postgres`
  </Accordion>

  <Accordion title="Timeout de conexión a Redis">
    * **Causa:** Redis está caído o las credenciales son incorrectas.
    * **Resolución:**

    1. Verifica que Redis esté activo: `docker-compose ps redis`
    2. Confirma `REDIS_PASSWORD`
    3. Prueba la conectividad: `redis-cli -h localhost ping`
  </Accordion>

  <Accordion title="Las colas de RabbitMQ no se crean">
    * **Causa:** RabbitMQ sigue arrancando, o el host virtual no existe.
    * **Resolución:**

    1. Espera hasta que RabbitMQ esté saludable
    2. Accede a la UI de administración en [http://localhost:15672](http://localhost:15672)
    3. Verifica `RABBITMQ_VHOST`
  </Accordion>

  <Accordion title="Errores de autenticación">
    * **Causa:** el servicio de autenticación es inalcanzable o el token no es válido.
    * **Resolución:**

    1. Verifica `PLUGIN_AUTH_ADDRESS`
    2. Deshabilita la autenticación para desarrollo: `PLUGIN_AUTH_ENABLED=false`
    3. Revisa los logs del servicio de autenticación
  </Accordion>

  <Accordion title="La migración falló">
    * **Causa:** no se pudieron aplicar las migraciones de la base de datos.
    * **Resolución:**

    1. Revisa el estado de la migración: `make migrate-status`
    2. Revisa los logs de la migración
    3. Aplica las migraciones manualmente: `make migrate-up`
    4. Inspecciona la tabla `schema_migrations` si hace falta
  </Accordion>
</AccordionGroup>

### Ver los logs

```bash theme={null}
docker-compose logs -f app
kubectl logs -f deployment/matcher -n matcher
```

### Modo debug

Habilita el log de debug para más visibilidad:

```bash theme={null}
LOG_LEVEL=debug docker-compose up app
```

## Próximos pasos

***

<Card title="Inicio rápido" icon="rocket" href="/es/products/matcher/getting-started/matcher-quick-start" horizontal>
  Ejecuta tu primera conciliación.
</Card>

<Card title="Configuración" icon="gear" href="/es/products/matcher/configuration/matcher-contexts-and-sources" horizontal>
  Configura contextos, fuentes y reglas de coincidencia.
</Card>
