Services Lender depends on
PostgreSQL, Valkey, and an identity provider are the three you cannot originate without. A first loan needs no Midaz and no RedPanda. Read Ledger postings below for what waits.
Run Lender locally
Lender is a single Go service. A local run needs the Go toolchain at 1.26 or later, Docker with Compose, and Make.
1
Create the environment file
make set-env copies config/.env.example to config/.env. Edit that file for every setting on this page.2
Start the dependencies
make up starts the Compose dependencies and the service on port 8080.3
Apply the migrations
make migrate-up applies both migration sets. See the next section.4
Run with live reload
make dev runs the service with Air.Apply both migration sets
Lender does not migrate the database when it starts. You apply migrations out of band, and there are two sets:
Apply both.
make migrate-up reads both paths.
The core set also seeds the jurisdiction registry: two active jurisdictions, BR for Brazil and XX, the generic profile. No API creates a jurisdiction. The deployed binary carries each profile’s behavior, and the table records which codes that binary knows. Read Jurisdictions for what a profile decides.
Required configuration
Two settings gate origination. Set both before the first call.
An authenticated subject — PLUGIN_AUTH_ENABLED=true
Lender records who acted on every loan application. It reads the assigned officer from the bearer token’s subject, not from the request body. Every loan-application call therefore needs an authenticated identity.
Set two variables:
Then send a bearer token on every call. The token needs a non-empty subject claim, and under the generic
XX profile the same subject must create, approve, and disburse the application.
make generate-casdoor writes Lender’s roles and permissions to config/casdoor/init_data.json. Load that seed into your identity provider. The officer’s role needs these permissions:
A durable posting sink — OUTBOX_ENABLED=true
A disbursed loan must record its posting intent durably. Lender writes that intent to a transactional outbox in the same database transaction that moves the application to disbursed. Set OUTBOX_ENABLED=true so the outbox is available when you disburse.
The dispatcher relays each intent afterwards, on the cadence OUTBOX_DISPATCH_INTERVAL_SEC sets. Read Accounting and accrual runs for what the ledger receives.
Ledger postings
Point Lender at Midaz when you want postings to land in the ledger:
The loan originates with or without these. Until you configure a ledger endpoint, posting intents stay durable in the outbox and the ledger entry waits. Configure the ledger before you expect bookings.
Single-tenant defaults
MULTI_TENANT_ENABLED is false by default. In that mode DEFAULT_TENANT_ID identifies the one tenant, and it must be a valid UUID.
Multi-tenant additions
Multi-tenant mode gives each tenant its own schema and its own ledger client pool. It adds four requirements:
- Set
MULTI_TENANT_ENABLED=trueand point Lender at the tenant manager. - Every token carries a
tenantIdclaim. public.tenant_jurisdictionscarries a row for each tenant and jurisdiction pair.- Each accounting profile declares its own
midazOrganizationIdandmidazLedgerId. Multi-tenant mode does not fall back to the environment defaults.
Event streaming
Streaming is off by default and origination does not need it. To publish the lifecycle event catalog, set
STREAMING_ENABLED=true, point STREAMING_BROKERS at RedPanda, and set STREAMING_CLOUDEVENTS_SOURCE=lender. Lender validates that source value when it starts.
Checklist
- PostgreSQL 17 and Valkey 8 reachable.
- Both migration sets applied.
PLUGIN_AUTH_ENABLED=trueand the identity provider reachable.- A bearer token with a subject and every permission in the table above.
OUTBOX_ENABLED=true.DEFAULT_TENANT_IDa valid UUID, in single-tenant mode.
Next steps
Quick start
Six calls from an empty database to a disbursed loan.
Configuration and deploy
The full container shape, the wider environment surface, and the ledger posting path.

