Help us grow — star us on GitHubGitHub stars
LinkedRecords

Configuration

Environment variables and server setup

Overview

LinkedRecords is configured primarily through environment variables. This guide covers all available configuration options. For a step-by-step deployment walkthrough, see Self-Hosting.

Quick Start

For local development, download the Docker Compose file from the repository and start the stack:

curl -fsSLO https://raw.githubusercontent.com/wolfoo2931/linkedrecords/main/docker-compose.yml
docker compose up

This starts PostgreSQL and LinkedRecords on port 6543 with a built-in mock OIDC provider (AUTH_DEV_MODE). The test accounts alice@example.com, bob@example.com, and charlie@example.com are available with one-click login.

The Docker Compose setup is for local development only. It includes a mock OIDC provider with test accounts and should not be used in production.

Server

VariableDescriptionDefault
PORTPort the server listens on6543
SERVER_BASE_URLPublic URL of the LinkedRecords server-
FRONTEND_BASE_URLURL of the frontend application (required; also used as CORS origin fallback and for OIDC redirects)-
HTTPSSet to true to terminate TLS in the Node.js server itselffalse
SSL_KEYPEM-encoded private key (required if HTTPS=true)-
SSL_CRTPEM-encoded certificate (required if HTTPS=true)-
SERVER_BASE_URL=https://api.your-app.com
FRONTEND_BASE_URL=https://your-app.com

Most deployments terminate TLS at a reverse proxy or load balancer and leave HTTPS unset.

Database Configuration

LinkedRecords uses PostgreSQL for data storage. The connection is configured with the standard PostgreSQL environment variables:

VariableDescriptionDefault
PGHOSTPostgreSQL hostlocalhost
PGPORTPostgreSQL port5432
PGUSERDatabase user-
PGPASSWORDDatabase password-
PGDATABASEDatabase name-
PGHOST=localhost
PGPORT=5432
PGUSER=linkedrecords
PGPASSWORD=your-secure-password
PGDATABASE=linkedrecords

The database schema is created automatically on server start; no separate migration step is needed.

For tests and quick experiments without a PostgreSQL server, an embedded PGlite database can be used instead:

VariableDescriptionDefault
USE_PGLITESet to true to use embedded PGlite instead of PostgreSQLfalse
PGLITE_DATA_DIRData directory for PGlite (setting this also enables PGlite)in-memory

Authentication (OIDC)

LinkedRecords supports any OpenID Connect compliant identity provider (Auth0, Okta, Keycloak, ...):

VariableDescriptionRequired
AUTH_ISSUER_BASE_URLOIDC provider URLYes
AUTH_CLIENT_IDOAuth client IDYes
AUTH_CLIENT_SECRETOAuth client secretConfidential client mode
AUTH_COOKIE_SIGNING_SECRETSecret for signing session cookiesConfidential client mode
AUTH_TOKEN_AUDIENCEExpected aud claim of bearer tokensPublic client mode
ALLOW_HTTP_AUTHENTICATION_HEADERSet to true to accept bearer tokens via the Authorization headerPublic client mode
AUTH_IDP_LOGOUTSet to true to also destroy the session at the identity provider on logoutNo
AUTH_DEV_MODESet to true to mount a built-in mock OIDC provider at /dev-oidc with one-click test accounts. Development only.No

Confidential Client Mode

For same-domain setups where the frontend and backend share a domain, the session is kept in an HttpOnly cookie:

AUTH_ISSUER_BASE_URL=https://your-tenant.auth0.com/
AUTH_CLIENT_ID=your-client-id
AUTH_CLIENT_SECRET=your-client-secret
AUTH_COOKIE_SIGNING_SECRET=a-long-random-secret-string

Public Client Mode

For cross-domain setups (SPA on a different domain than the API), the SPA stores the access token and sends it as a bearer token:

AUTH_ISSUER_BASE_URL=https://your-tenant.auth0.com/
AUTH_CLIENT_ID=your-client-id
AUTH_TOKEN_AUDIENCE=your-api-audience
ALLOW_HTTP_AUTHENTICATION_HEADER=true

CORS Configuration

For cross-domain access, configure allowed origins. If CORS_ORIGIN is not set, FRONTEND_BASE_URL is used.

VariableDescriptionDefault
CORS_ORIGINAllowed origin as a single URL string or JSON array of URLsvalue of FRONTEND_BASE_URL
CORS_ORIGIN='["https://your-app.com", "https://staging.your-app.com"]'

Redis Configuration (Optional)

Redis is not required for a single-instance deployment. Configure it when running multiple LinkedRecords instances behind a load balancer, so real-time updates reach clients connected to other instances (Socket.IO Redis Streams adapter):

VariableDescriptionDefault
REDIS_HOSTRedis host; setting this enables the Redis adapter-
REDIS_USERNAMERedis username-
REDIS_PASSWORDRedis password-
REDIS_HOST=redis.your-provider.com
REDIS_USERNAME=default
REDIS_PASSWORD=your-redis-password

S3/MinIO Configuration (Optional)

For blob storage, configure S3-compatible object storage:

VariableDescriptionDefault
S3_ENDPOINTS3-compatible endpoint hostname-
S3_PORTEndpoint port-
S3_BUCKETBucket name (must already exist)-
S3_ACCESS_KEYAccess key ID-
S3_SECRET_KEYSecret access key-
S3_USE_SSLUse TLS when talking to the endpoint (true/false)true
S3_COPY_FROM_BL_ATTRIBUTE_TABLEOne-time migration: copy existing blobs from PostgreSQL to S3 on startupfalse
S3_ENDPOINT=localhost
S3_PORT=9000
S3_BUCKET=linkedrecords
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin
S3_USE_SSL=false

If S3 is not configured, blob values are stored in PostgreSQL (with reduced performance for large files). Configuring S3 is recommended.

Quota Configuration

Control storage limits per accountable user or organization:

VariableDescriptionDefault
DEFAULT_STORAGE_SIZE_QUOTADefault storage quota in MB500
QUOTA_COUNT_KV_RECORDSSet to true to count key-value record storage against the accountee's quotafalse
QUOTA_COUNT_LT_RECORDSSet to true to count long-text record storage against the accountee's quotafalse
DEFAULT_STORAGE_SIZE_QUOTA=500
QUOTA_COUNT_KV_RECORDS=true
QUOTA_COUNT_LT_RECORDS=true

Blob storage always counts against the quota. See Quotas for how quota accounting works.

Payment Integration (Optional)

For paid quota upgrades with Paddle:

VariableDescription
PADDLE_API_KEYPaddle API key
PADDLE_API_URLPaddle API URL (e.g. https://sandbox-api.paddle.com for testing)
PADDLE_NOTIFICATION_SECRETWebhook signing secret used to verify /payment_events notifications
PADDLE_API_KEY=your-paddle-api-key
PADDLE_API_URL=https://api.paddle.com
PADDLE_NOTIFICATION_SECRET=webhook-signing-secret

Performance Tuning

VariableDescriptionDefault
ENABLE_AUTH_RULE_CACHESet to true to cache authorization lookups in memory. Can require a lot of memory.false
SHORT_LIVED_ACCESS_TOKEN_SIGNINGA signing secret. When set, short-lived signed read tokens are issued with query results and used to authorize real-time subscriptions, reducing database load.not set
ENABLE_AUTH_RULE_CACHE=true
SHORT_LIVED_ACCESS_TOKEN_SIGNING=a-long-random-secret-string

Complete Example Configuration

Development

The simplest way to get these is docker compose up (see Quick Start above). Equivalent manual configuration:

# Database
PGHOST=localhost
PGPORT=5432
PGUSER=linkedrecords
PGPASSWORD=linkedrecords
PGDATABASE=linkedrecords
 
# Auth (built-in mock OIDC provider)
AUTH_DEV_MODE=true
AUTH_ISSUER_BASE_URL=http://localhost:6543/dev-oidc
AUTH_CLIENT_ID=linkedrecords-dev
AUTH_TOKEN_AUDIENCE=linkedrecords-dev
ALLOW_HTTP_AUTHENTICATION_HEADER=true
 
# URLs
SERVER_BASE_URL=http://localhost:6543
FRONTEND_BASE_URL=http://localhost:6543
 
# CORS
CORS_ORIGIN='*'

Production

# Database
PGHOST=db.your-provider.com
PGPORT=5432
PGUSER=linkedrecords
PGPASSWORD=secure-production-password
PGDATABASE=linkedrecords
 
# Auth (Auth0 example, confidential client mode)
AUTH_ISSUER_BASE_URL=https://your-tenant.auth0.com/
AUTH_CLIENT_ID=production-client-id
AUTH_CLIENT_SECRET=production-client-secret
AUTH_COOKIE_SIGNING_SECRET=production-secret-at-least-32-chars
 
# URLs
SERVER_BASE_URL=https://api.your-app.com
FRONTEND_BASE_URL=https://your-app.com
 
# S3
S3_ENDPOINT=s3.amazonaws.com
S3_BUCKET=your-linkedrecords-bucket
S3_ACCESS_KEY=AKIA...
S3_SECRET_KEY=...
S3_USE_SSL=true
 
# CORS
CORS_ORIGIN='["https://your-app.com"]'
 
# Quotas
DEFAULT_STORAGE_SIZE_QUOTA=1000
QUOTA_COUNT_KV_RECORDS=true
QUOTA_COUNT_LT_RECORDS=true
 
# Performance
ENABLE_AUTH_RULE_CACHE=true
SHORT_LIVED_ACCESS_TOKEN_SIGNING=another-long-random-secret

OIDC Provider Setup

Auth0

  1. Create an Application (Regular Web Application for confidential, SPA for public)
  2. Configure Allowed Callback URLs: https://your-backend.com/callback
  3. Configure Allowed Logout URLs: https://your-app.com
  4. Configure Allowed Web Origins: https://your-app.com
  5. Get the Client ID and Client Secret

Other Providers

LinkedRecords works with any standard OIDC provider:

  • Okta: Use the OIDC metadata URL
  • Keycloak: Configure a client with appropriate scopes
  • Google: Create OAuth 2.0 credentials

Health Check

There is no dedicated health endpoint. GET /oidc/discovery is served without authentication and is suitable as a liveness probe:

curl http://localhost:6543/oidc/discovery

Logging

LinkedRecords logs structured JSON to stdout using pino. Request and response headers are redacted. Use your platform's log collector to ship and filter the output.