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

# Configuration

> Environment variables for the Meridian server.

All configuration is via environment variables.

| Variable | Default | Description |
| - | - | - |
| `MERIDIAN_BIND` | `0.0.0.0:3000` | TCP bind address |
| `MERIDIAN_DATA_DIR` | `./data` | Path 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 |
| `MERIDIAN_CRDT_GC_GRACE_MS` | `60000` | Grace period before GC removes old CRDT versions (ms) |

**PostgreSQL / pg-sync** (`--features pg-sync`):

| Variable | Default | Description |
| - | - | - |
| `DATABASE_URL` | *(unset)* | PostgreSQL URL — enables pg-sync and Postgres storage |
| `PG_SYNC_DATABASE_URL` | *(unset)* | Alias for `DATABASE_URL` |
| `MERIDIAN_WAL_CONNSTR` | *(unset)* | Replication connection URL — enables WAL stream for payloads > 8 KB |
| `MERIDIAN_WAL_SLOT` | `meridian_wal` | Logical replication slot name (auto-created) |
| `MERIDIAN_WAL_PUB` | `meridian_pub` | Publication name (auto-created, `FOR ALL TABLES`) |

**Clustering** (`--features cluster` or `cluster-http`):

| Variable | Default | Description |
| - | - | - |
| `REDIS_URL` | *(unset)* | Redis URL — enables Redis Pub/Sub cluster transport |
| `MERIDIAN_PEERS` | *(unset)* | Comma-separated peer URLs — enables HTTP cluster transport |
| `MERIDIAN_NODE_ID` | *(auto)* | Unique node ID — derived from `hostname:port` hash if unset |
| `MERIDIAN_INTERNAL_BIND` | `0.0.0.0:3001` | Internal bind address for inter-node HTTP sync (`cluster-http` only) |
| `MERIDIAN_ANTI_ENTROPY_SECS` | `30` | Anti-entropy gossip interval in seconds |

For S3 WAL archive variables (`S3_BUCKET`, `S3_ENDPOINT`, etc.), see the [Storage](https://chahine.mintlify.site/self-hosting/storage.md) page.

| Variable | Default | Description |
| - | - | - |
| `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_SLOT` | `meridian_wal` | Logical replication slot name (auto-created) |
| `MERIDIAN_WAL_PUB` | `meridian_pub` | Publication name (auto-created, `FOR ALL TABLES`) |

## MERIDIAN\_SIGNING\_KEY

Generate a secure key:

```bash theme={null}
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**

```json theme={null}
{
  "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.

```ts theme={null}
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`:

```json theme={null}
{ "error": "rate_limited", "message": "too many requests" }
```

## Health checks

Meridian exposes two unauthenticated health endpoints:

| Endpoint | Status | Description |
| - | - | - |
| `GET /health/live` | 200 always | Process is running. Used by load balancers to detect dead instances. |
| `GET /health/ready` | 200 / 503 | Storage and WAL are operational. Use for readiness probes. |

`/health/ready` returns `503` if the store is unreachable. Example response:

```json theme={null}
{ "status": "ready", "wal_last_seq": 1042 }
```

**Kubernetes example:**

```yaml theme={null}
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).

| Metric | Type | Description |
| - | - | - |
| `meridian_ops_total` | Counter | CRDT operations applied, labeled by `ns`, `crdt_id`, `op_type` |
| `meridian_ws_connections_active` | Gauge | Active WebSocket connections |
| `meridian_wal_entries_total` | Counter | WAL entries written |
| `meridian_merge_duration_seconds` | Histogram | CRDT merge/apply latency, labeled by `ns` |
| `meridian_subscriptions_active` | Gauge | Active WebSocket subscription receivers |

Example Prometheus scrape config:

```yaml theme={null}
scrape_configs:
  - job_name: meridian
    static_configs:
      - targets: ["localhost:3000"]
    metrics_path: /metrics
```

## MERIDIAN\_DATA\_DIR

Meridian uses [sled](https://github.com/spacejam/sled) as its embedded database. The data directory contains the sled tree files.

In Docker, this is mounted as a named volume:

```yaml theme={null}
volumes:
  - meridian-data:/data
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.