Skip to main content

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.

Matcher automates financial reconciliation across multiple data sources, eliminating manual matching work and providing a full audit trail for every transaction. Setting up Matcher means putting in place the foundation for exception management, compliance reporting, and operational visibility. This guide walks you through deploying Matcher in development and production environments.

Docker compose (development)


Docker Compose is the recommended approach for local development and testing.

1. Clone the repository

git clone https://github.com/LerianStudio/matcher.git
cd matcher

2. Configure the environment

The docker-compose.yml file includes sensible defaults for local development. You can override any value by setting environment variables in your shell or by creating a .env file in the project root. Refer to Environment variables for details on available settings.
One required variable has no default: SYSTEMPLANE_SECRET_MASTER_KEY. See config/.config-map.example in the repository for generation instructions.

3. Start services

Start the required infrastructure services:
docker-compose up -d postgres redis rabbitmq
Wait until all services report a healthy status:
docker-compose ps
Start the Matcher application:
docker-compose up -d app
To start all services at once:
docker-compose up -d

4. Verify the installation

Confirm that Matcher is running:
curl http://localhost:4018/readyz
Expected response:
{
  "status": "healthy",
  "checks": {
    "postgres": { "status": "up" },
    "redis": { "status": "up" },
    "rabbitmq": { "status": "up" },
    "object_storage": { "status": "up" }
  },
  "version": "1.1.0",
  "deployment_mode": "local"
}

Docker compose services

The default docker-compose.yml includes:
ServicePortPurpose
postgres5432PostgreSQL primary database
postgres-replica5433PostgreSQL read replica
redis6379Valkey (Redis-compatible) cache
rabbitmq5672, 15672RabbitMQ (AMQP and management UI)
seaweedfs8333, 9333S3-compatible object storage
app4018Matcher API

Development with hot reload

For active development, use:
make dev
This starts Matcher with live reload enabled using Air.

Kubernetes / helm (production)


Production deployments should use the official Helm chart.

Prerequisites

  • Kubernetes 1.28+
  • Helm 3.12+
  • kubectl configured for the target cluster

1. Create a namespace

kubectl create namespace matcher

2. Configure values

Create a values.yaml file with your deployment configuration:
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. Create secrets

Create Kubernetes secrets for sensitive credentials:
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. Install the chart

helm install matcher oci://registry-1.docker.io/lerianstudio/matcher-helm \
 --version 2.1.1 \
 --namespace matcher \
 --values values.yaml

5. Verify the deployment

kubectl get pods -n matcher
kubectl get svc -n matcher
kubectl logs -f deployment/matcher -n matcher

Upgrading

To upgrade an existing deployment:
helm upgrade matcher oci://registry-1.docker.io/lerianstudio/matcher-helm \
 --version 2.1.1 \
 --namespace matcher \
 --values values.yaml

Environment variables


Matcher is configured entirely through environment variables.

Application

VariableDefaultDescription
ENV_NAMEdevelopmentRuntime environment name
LOG_LEVELinfoLog verbosity
DEPLOYMENT_MODElocalDeployment mode (local, byoc, saas)
SERVER_ADDRESS:4018HTTP server bind address
HTTP_BODY_LIMIT_BYTES33554432Maximum request body size (bytes)

CORS

VariableDefaultDescription
CORS_ALLOWED_ORIGINShttp://localhost:3000Allowed origins
CORS_ALLOWED_METHODSGET,POST,PUT,PATCH,DELETE,OPTIONSAllowed HTTP methods
CORS_ALLOWED_HEADERSOrigin,Content-Type,Accept,Authorization,X-Request-IDAllowed request headers

Database (PostgreSQL)

VariableDefaultDescription
POSTGRES_HOSTlocalhostPrimary database host
POSTGRES_PORT5432Primary database port
POSTGRES_USERmatcherUsername
POSTGRES_PASSWORDmatcher_dev_passwordPassword
POSTGRES_DBmatcherDatabase name
POSTGRES_SSLMODEdisableSSL mode
POSTGRES_TLS_REQUIREDfalseEnforce TLS at bootstrap
POSTGRES_MAX_OPEN_CONNS25Max open connections
POSTGRES_MAX_IDLE_CONNS5Max idle connections
POSTGRES_CONN_MAX_LIFETIME_MINS30Connection max lifetime (minutes)
POSTGRES_CONN_MAX_IDLE_TIME_MINS5Connection max idle time (minutes)
POSTGRES_CONNECT_TIMEOUT_SEC10Connection timeout (seconds)
POSTGRES_QUERY_TIMEOUT_SEC30Query timeout (seconds)
MIGRATIONS_PATHmigrationsPath to migration files

Database replica (PostgreSQL)

VariableDefaultDescription
POSTGRES_REPLICA_HOSTReplica host
POSTGRES_REPLICA_PORTReplica port
POSTGRES_REPLICA_USERReplica username
POSTGRES_REPLICA_PASSWORDReplica password
POSTGRES_REPLICA_DBReplica database name
POSTGRES_REPLICA_SSLMODEReplica SSL mode
POSTGRES_REPLICA_TLS_REQUIREDfalseEnforce TLS for replica

Cache (Redis)

VariableDefaultDescription
REDIS_HOSTlocalhost:6379Redis address (host:port)
REDIS_MASTER_NAMESentinel master name
REDIS_PASSWORDPassword
REDIS_DB0Database index
REDIS_PROTOCOL3RESP protocol version
REDIS_TLSfalseEnable TLS
REDIS_TLS_REQUIREDfalseEnforce TLS at bootstrap
REDIS_CA_CERTCA certificate path
REDIS_POOL_SIZE10Connection pool size
REDIS_MIN_IDLE_CONNS2Minimum idle connections
REDIS_READ_TIMEOUT_MS3000Read timeout (milliseconds)
REDIS_WRITE_TIMEOUT_MS3000Write timeout (milliseconds)
REDIS_DIAL_TIMEOUT_MS5000Dial timeout (milliseconds)

Messaging (RabbitMQ)

VariableDefaultDescription
RABBITMQ_URIamqpURI scheme (amqp or amqps)
RABBITMQ_HOSTlocalhostBroker host
RABBITMQ_PORT5672Broker port
RABBITMQ_USERmatcher_adminUsername
RABBITMQ_PASSWORDmatcher_dev_passwordPassword
RABBITMQ_VHOST/Virtual host
RABBITMQ_HEALTH_URLhttp://localhost:15672Management API URL for health checks
RABBITMQ_ALLOW_INSECURE_HEALTH_CHECKfalseAllow HTTP (non-TLS) health check
RABBITMQ_TLS_REQUIREDfalseEnforce TLS at bootstrap

Authentication

VariableDefaultDescription
PLUGIN_AUTH_ENABLEDfalseEnable authentication
PLUGIN_AUTH_ADDRESSAuth service URL
AUTH_JWT_SECRETJWT signing secret

Object storage (S3-compatible)

VariableDefaultDescription
OBJECT_STORAGE_ENDPOINThttp://localhost:8333S3 endpoint URL
OBJECT_STORAGE_REGIONus-east-1S3 region
OBJECT_STORAGE_BUCKETmatcher-exportsBucket for exports
OBJECT_STORAGE_ACCESS_KEY_IDAccess key ID
OBJECT_STORAGE_SECRET_ACCESS_KEYSecret access key
OBJECT_STORAGE_USE_PATH_STYLEtrueUse path-style addressing
OBJECT_STORAGE_ALLOW_INSECURE_ENDPOINTfalseAllow HTTP (non-TLS) endpoint
OBJECT_STORAGE_TLS_REQUIREDfalseEnforce TLS at bootstrap

Observability

VariableDefaultDescription
ENABLE_TELEMETRYfalseEnable OpenTelemetry
OTEL_RESOURCE_SERVICE_NAMEmatcherService name for traces/metrics
OTEL_LIBRARY_NAMEgithub.com/LerianStudio/matcherInstrumentation library name
OTEL_RESOURCE_SERVICE_VERSION1.1.0Service version
OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENTdevelopmentDeployment environment label
OTEL_EXPORTER_OTLP_ENDPOINTlocalhost:4317OTLP collector endpoint
DB_METRICS_INTERVAL_SEC15DB metrics collection interval

TLS

VariableDefaultDescription
SERVER_TLS_CERT_FILETLS certificate path
SERVER_TLS_KEY_FILETLS private key path
TLS_TERMINATED_UPSTREAMfalseTrust upstream TLS termination (e.g., load balancer)
TRUSTED_PROXIESTrusted proxy CIDR ranges

Rate limiting

VariableDefaultDescription
RATE_LIMIT_ENABLEDtrueEnable global rate limiting
RATE_LIMIT_MAX100Max requests per window
RATE_LIMIT_EXPIRY_SEC60Rate limit window (seconds)
EXPORT_RATE_LIMIT_MAX10Max export requests per window
EXPORT_RATE_LIMIT_EXPIRY_SEC60Export rate limit window (seconds)
DISPATCH_RATE_LIMIT_MAX50Max dispatch requests per window
DISPATCH_RATE_LIMIT_EXPIRY_SEC60Dispatch rate limit window (seconds)
ADMIN_RATE_LIMIT_MAX30Max admin requests per window
ADMIN_RATE_LIMIT_EXPIRY_SEC60Admin rate limit window (seconds)

Swagger

VariableDefaultDescription
SWAGGER_ENABLEDfalseEnable Swagger UI
SWAGGER_HOSTOverride Swagger spec host
SWAGGER_SCHEMEShttpsSwagger spec schemes (comma-separated)

Idempotency

VariableDefaultDescription
IDEMPOTENCY_RETRY_WINDOW_SEC300Window for retrying failed idempotent requests (seconds)
IDEMPOTENCY_SUCCESS_TTL_HOURS168How long completed idempotency keys are cached (hours)
IDEMPOTENCY_HMAC_SECRETHMAC secret for signing idempotency keys (min 32 bytes)

Deduplication

VariableDefaultDescription
DEDUPE_TTL_SEC3600TTL for deduplication keys (seconds)

Outbox

VariableDefaultDescription
OUTBOX_RETRY_WINDOW_SEC300Cooldown before retrying failed events (seconds)
OUTBOX_DISPATCH_INTERVAL_SEC2Dispatcher poll interval (seconds)

Workers

VariableDefaultDescription
EXPORT_WORKER_ENABLEDtrueEnable export worker
EXPORT_WORKER_POLL_INTERVAL_SEC5Export worker poll interval (seconds)
EXPORT_WORKER_PAGE_SIZE1000Rows per export page
EXPORT_PRESIGN_EXPIRY_SEC3600Pre-signed URL expiry for exports (seconds)
CLEANUP_WORKER_ENABLEDtrueEnable cleanup worker
CLEANUP_WORKER_INTERVAL_SEC3600Cleanup worker interval (seconds)
CLEANUP_WORKER_BATCH_SIZE100Cleanup batch size
CLEANUP_WORKER_GRACE_PERIOD_SEC3600Grace period before cleanup (seconds)
WEBHOOK_TIMEOUT_SEC30Webhook dispatch timeout (seconds)
CALLBACK_RATE_LIMIT_PER_MIN60Max callbacks per external system per minute

Scheduler

VariableDefaultDescription
SCHEDULER_INTERVAL_SEC60Cron-based scheduler poll interval (seconds)

Archival

VariableDefaultDescription
ARCHIVAL_WORKER_ENABLEDfalseEnable audit log archival worker
ARCHIVAL_WORKER_INTERVAL_HOURS24Archival run interval (hours)
ARCHIVAL_HOT_RETENTION_DAYS90Days to keep data in hot storage
ARCHIVAL_WARM_RETENTION_MONTHS24Months to keep data in warm storage
ARCHIVAL_COLD_RETENTION_MONTHS84Months to keep data in cold storage
ARCHIVAL_BATCH_SIZE5000Rows per archival batch
ARCHIVAL_STORAGE_BUCKETmatcher-archivesS3 bucket for archives
ARCHIVAL_STORAGE_PREFIXarchives/audit-logsS3 key prefix for archives
ARCHIVAL_STORAGE_CLASSGLACIERS3 storage class for archives
ARCHIVAL_PARTITION_LOOKAHEAD3Partition lookahead count
ARCHIVAL_PRESIGN_EXPIRY_SEC3600Pre-signed URL expiry for archives (seconds)

Fetcher / Discovery

VariableDefaultDescription
FETCHER_ENABLEDfalseEnable Fetcher-backed discovery
FETCHER_URLhttp://localhost:4006Fetcher service URL
FETCHER_ALLOW_PRIVATE_IPSfalseAllow private IP ranges
FETCHER_HEALTH_TIMEOUT_SEC5Fetcher health check timeout (seconds)
FETCHER_REQUEST_TIMEOUT_SEC30Fetcher request timeout (seconds)
FETCHER_DISCOVERY_INTERVAL_SEC60Discovery poll interval (seconds)
FETCHER_SCHEMA_CACHE_TTL_SEC300Schema cache TTL (seconds)
FETCHER_EXTRACTION_POLL_INTERVAL_SEC5Extraction poll interval (seconds)
FETCHER_EXTRACTION_TIMEOUT_SEC600Extraction timeout (seconds)
FETCHER_MAX_EXTRACTION_BYTES2147483648Max extraction payload size (bytes, default 2 GiB)
FETCHER_BRIDGE_INTERVAL_SEC30Bridge worker poll interval (seconds)
FETCHER_BRIDGE_BATCH_SIZE50Extractions per tenant per bridge cycle
FETCHER_BRIDGE_TENANT_CONCURRENCY4Concurrent tenants in bridge cycle
FETCHER_BRIDGE_STALE_THRESHOLD_SEC3600Stale extraction threshold (seconds)
FETCHER_BRIDGE_RETRY_MAX_ATTEMPTS5Max retry attempts per extraction
FETCHER_CUSTODY_RETENTION_SWEEP_INTERVAL_SEC900Custody sweep interval (seconds)
FETCHER_CUSTODY_RETENTION_GRACE_PERIOD_SEC3600Grace period before custody deletion (seconds)
APP_ENC_KEYBase64-encoded master key shared with Fetcher

M2M credentials

VariableDefaultDescription
M2M_TARGET_SERVICEfetcherTarget service name for credential lookup
M2M_CREDENTIAL_CACHE_TTL_SEC300Redis cache TTL for M2M credentials (seconds)
AWS_REGIONAWS region for Secrets Manager

Infrastructure

VariableDefaultDescription
INFRA_CONNECT_TIMEOUT_SEC30Infrastructure startup connection timeout (seconds)
HEALTH_CHECK_TIMEOUT_SEC5Legacy per-check probe timeout (seconds)
HEALTH_CHECK_TIMEOUT_MS800Per-check probe timeout (milliseconds, preferred)
For multi-tenant deployment settings, see Multi-Tenant Mode. For runtime configuration management, see Runtime Configuration (Systemplane).

Verify the installation


Validate that Matcher is operating correctly.

Liveness check

curl http://localhost:4018/health

Readiness check

curl http://localhost:4018/readyz

API verification

curl -X POST http://localhost:4018/v1/contexts \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "name": "Test Context",
   "type": "1:1"
 }'

Troubleshooting


Common issues

  • Cause: PostgreSQL is not running or unreachable.
  • Resolution:
  1. Verify PostgreSQL is running: docker-compose ps postgres
  2. Check connection values in .env
  3. Test connectivity: nc -zv localhost 5432
  4. Review logs: docker-compose logs postgres
  • Cause: Redis is not running or credentials are incorrect.
  • Resolution:
  1. Verify Redis is running: docker-compose ps redis
  2. Confirm REDIS_PASSWORD
  3. Test connectivity: redis-cli -h localhost ping
  • Cause: RabbitMQ is still initializing or the virtual host is missing.
  • Resolution:
  1. Wait until RabbitMQ is healthy
  2. Access the management UI at http://localhost:15672
  3. Verify RABBITMQ_VHOST
  • Cause: Auth service is unreachable or the token is invalid.
  • Resolution:
  1. Verify PLUGIN_AUTH_ADDRESS
  2. Disable auth for development: PLUGIN_AUTH_ENABLED=false
  3. Review auth service logs
  • Cause: Database migrations could not be applied.
  • Resolution:
  1. Check migration status: make migrate-status
  2. Review migration logs
  3. Apply migrations manually: make migrate-up
  4. Inspect the schema_migrations table if needed
If upgrading from the main branch, migrations 000020 and 000021 run automatically. Migration 000020 renames systemplane configuration keys for cross-product standardization. Migration 000021 converts the external_system column from an enum type to VARCHAR(255), allowing arbitrary external system identifiers. If migrations fail, check the schema_migrations table and ensure no conflicting manual changes exist.

Viewing logs

docker-compose logs -f app
kubectl logs -f deployment/matcher -n matcher

Debug mode

Enable debug logging for additional visibility:
LOG_LEVEL=debug docker-compose up app

Next steps


Quick start

Run your first reconciliation.

Configuration

Configure contexts, sources, and match rules.