Skip to main content
All configuration is via environment variables.
VariableDefaultDescription
MERIDIAN_BIND0.0.0.0:3000TCP bind address
MERIDIAN_DATA_DIR./dataPath to sled storage directory
MERIDIAN_SIGNING_KEY(random)64-char hex ed25519 seed (32 bytes)
MERIDIAN_WEBHOOK_URL(unset)HTTP endpoint to receive webhook events
MERIDIAN_WEBHOOK_SECRET(unset)Secret for X-Meridian-Signature HMAC-SHA256
For clustering variables (REDIS_URL, MERIDIAN_PEERS, MERIDIAN_NODE_ID, etc.), see the Clustering page. For S3 WAL archive variables (S3_BUCKET, S3_ENDPOINT, etc.), see the Storage page. For PostgreSQL and WAL replication variables, see the Postgres integration page.
VariableDefaultDescription
DATABASE_URL(unset)PostgreSQL URL — enables pg-sync transport and Postgres storage (--features pg-sync)
MERIDIAN_WAL_CONNSTR(unset)Replication connection URL — enables WAL stream for payloads > 8 KB
MERIDIAN_WAL_SLOTmeridian_walLogical replication slot name (auto-created)
MERIDIAN_WAL_PUBmeridian_pubPublication name (auto-created, FOR ALL TABLES)

MERIDIAN_SIGNING_KEY

Generate a secure key:
openssl rand -hex 32
This key is used to sign and verify auth tokens. Keep it secret and back it up — losing it invalidates all issued tokens. If unset, a random key is generated at startup. This is fine for local development but not for production (tokens become invalid on restart).

Webhooks

When MERIDIAN_WEBHOOK_URL is set, Meridian sends a POST request to that URL after every successful CRDT operation. Payload
{
  "ns": "my-room",
  "crdt_id": "counter",
  "source": "http",
  "timestamp_ms": 1712345678901
}
source is either "http" (REST op) or "ws" (WebSocket op). Signature verification Every request includes an X-Meridian-Signature header containing HMAC-SHA256(secret, body) as a lowercase hex string.
import { createHmac } from "crypto";

const signature = createHmac("sha256", process.env.MERIDIAN_WEBHOOK_SECRET!)
  .update(rawBody)
  .digest("hex");

if (signature !== req.headers["x-meridian-signature"]) {
  return res.status(401).end();
}
Failed deliveries are retried up to 3 times with exponential backoff (200ms, 400ms, 800ms). Events are dropped if the internal queue (1024 slots) is full.

Rate limiting

Each token is limited to 100 requests per second (sliding window). Requests over the limit return 429 Too Many Requests:
{ "error": "rate_limited", "message": "too many requests" }

Health checks

Meridian exposes two unauthenticated health endpoints:
EndpointStatusDescription
GET /health/live200 alwaysProcess is running. Used by load balancers to detect dead instances.
GET /health/ready200 / 503Storage and WAL are operational. Use for readiness probes.
/health/ready returns 503 if the store is unreachable. Example response:
{ "status": "ready", "wal_last_seq": 1042 }
Kubernetes example:
livenessProbe:
  httpGet:
    path: /health/live
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /health/ready
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10

Metrics

Meridian exposes a Prometheus-compatible metrics endpoint at GET /metrics (no authentication required).
MetricTypeDescription
meridian_ops_totalCounterCRDT operations applied, labeled by ns, crdt_id, op_type
meridian_ws_connections_activeGaugeActive WebSocket connections
meridian_wal_entries_totalCounterWAL entries written
meridian_merge_duration_secondsHistogramCRDT merge/apply latency, labeled by ns
meridian_subscriptions_activeGaugeActive WebSocket subscription receivers
Example Prometheus scrape config:
scrape_configs:
  - job_name: meridian
    static_configs:
      - targets: ["localhost:3000"]
    metrics_path: /metrics

MERIDIAN_DATA_DIR

Meridian uses sled as its embedded database. The data directory contains the sled tree files. In Docker, this is mounted as a named volume:
volumes:
  - meridian-data:/data