### Start Redis Sentinel Setup Source: https://github.com/centrifugal/centrifugo/blob/master/misc/redis_sentinel/readme.md Execute this script to start the local Redis Sentinel and Redis nodes. Ensure you have the necessary setup files in place. ```bash bash start.sh ``` -------------------------------- ### Run Clickhouse Cluster Setup Source: https://github.com/centrifugal/centrifugo/blob/master/misc/clickhouse_cluster/readme.md Use these commands to generate configuration and start the Clickhouse cluster. ```bash make config ``` ```bash docker compose up ``` -------------------------------- ### Generate Example Configuration (JSON) Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html Generates a configuration example string in JSON format based on the provided documentation object and its level. Handles complex types, arrays, and default values. ```javascript function generateExample(doc, format = 'json') { const parts = doc.field.split('.'); let example = ''; if (format === 'json') { const indent = ' '.repeat(Math.max(0, doc.level - 1)); const fieldName = doc.name; if (doc.is_complex_type) { if (doc.default === '[]') { example = `${indent}"${fieldName}": []`; } else { example = `${indent}"${fieldName}": {}`; } } else { let exampleValue = doc.default; if (doc.type.startsWith('[]')) { exampleValue = exampleValue || '[]'; } else if (doc.type === 'string' || doc.type === 'PEMData') { if (exampleValue === undefined || exampleValue === null) { exampleValue = '""'; } else if (exampleValue === '') { exampleValue = '""'; } else if (!exampleValue.startsWith('"')) { exampleValue = `"${exampleValue}"`; } } else if (doc.type === 'bool') { exampleValue = exampleValue || 'false'; } else if (doc.type === 'int' || doc.type.includes('int')) { exampleValue = exampleValue || '0'; } else { exampleValue = exampleValue || '""'; } example = `${indent}"${fieldName}": ${exampleValue}`; } } else if (format === 'yaml') { const indent = ' '.repeat(Math.max(0, doc.level - 1)); const fieldName = doc.name; if (doc.is_complex_type) { example = `${indent}${fieldName}:`; } else { let exampleValue = doc.default || ''; if (doc.type.startsWith('[]')) { exampleValue = exampleValue || '[]'; } else if (doc.type === 'string') { exampleValue = exampleValue || '""'; } else if (doc.type === 'bool') { exampleValue = exampleValue || 'false'; } else if (doc.type === 'int' || doc.type.includes('int')) { exampleValue = exampleValue || '0'; } example = `${indent}${fieldName}: ${exampleValue}`; } } return example; } ``` -------------------------------- ### Install RPM Development Tools Source: https://github.com/centrifugal/centrifugo/blob/master/misc/rpm/README.md Installs the necessary tools for RPM development on your system. ```bash yum -y install rpmdevtools ``` -------------------------------- ### Start Local Redis Instances Source: https://github.com/centrifugal/centrifugo/blob/master/misc/redis_instances/readme.md Use this script to create N isolated Redis master instances starting from port 6000. Redis nodes will run until the script is interrupted. This is intended for development and benchmarking. ```bash bash start_instances.sh 3 ``` -------------------------------- ### Render All Configuration Documentation Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html Initializes the configuration documentation by rendering cards for all configuration items and setting up event listeners for copy and show example buttons. ```javascript function renderConfigDocs() { const container = document.getElementById('config-content'); container.innerHTML = ''; configData.forEach(doc => renderConfigCard(doc, container)); // Setup copy buttons document.querySelectorAll('.copy-btn').forEach(btn => { btn.addEventListener('click', function() { copyToClipboard(this.dataset.copy, this); }); }); document.querySelectorAll('.copy-example').forEach(btn => { btn.addEventListener('click', function() { copyToClipboard(this.dataset.example, this); }); }); // Setup show config example buttons document.querySelectorAll('.show-config-example').forEach(btn => { btn.addEventListener('click', function() { const field = this.dataset.field; const fieldId = generateFieldId(field); const exampleDiv = document.getElementById(fieldId + '-example'); if (exampleDiv.classList.contains('hidden')) { const jsonConfig = generateFullContextConfig(field, 'json'); const yamlConfig = generateFullContextConfig(field, 'yaml'); exampleDiv.innerHTML = `
' : '';
  });
}
```

--------------------------------

### Running k6 Benchmark Script

Source: https://github.com/centrifugal/centrifugo/blob/master/misc/benchmarking/k6/readme.md

Execute the k6 benchmark script using the 'k6 run' command. Ensure the 'benchmark.js' file is in the current directory.

```bash
k6 run benchmark.js
```

--------------------------------

### Serve Static Files with Centrifugo CLI

Source: https://context7.com/centrifugal/centrifugo/llms.txt

Use the `serve` command to run a development server for static files. Specify the directory and port if needed.

```bash
centrifugo serve
```

```bash
centrifugo serve --dir=./public --port=8080
```

--------------------------------

### Configure TLS

Source: https://context7.com/centrifugal/centrifugo/llms.txt

Enable TLS for HTTP server and gRPC API. Specify certificate and key paths, or use autocert for automatic certificate management.

```json
{
  "http_server": {
    "tls": {
      "enabled": true,
      "cert_pem": "/path/to/cert.pem",
      "key_pem": "/path/to/key.pem"
    },
    "tls_autocert": {
      "enabled": true,
      "host_whitelist": ["example.com"],
      "cache_dir": "/tmp/certs",
      "email": "admin@example.com"
    }
  },
  "grpc_api": {
    "enabled": true,
    "tls": {
      "enabled": true,
      "cert_pem": "/path/to/grpc-cert.pem",
      "key_pem": "/path/to/grpc-key.pem"
    }
  }
}
```

--------------------------------

### JavaScript: Copy Configuration to Clipboard

Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html

Attaches event listeners to copy configuration snippets to the clipboard and format them as JSON or YAML.

```javascript
const copyBtn = exampleDiv.querySelector('.copy-example');
const exampleContent = exampleDiv.querySelector('.example-content');
const formatBtns = exampleDiv.querySelectorAll('.format-btn');
let currentFormat = 'json';
if (copyBtn) {
    copyBtn.addEventListener('click', function() {
        const textToCopy = exampleContent.dataset[currentFormat].replace(/\\n/g, '\n').replace(/"/g, '"');
        copyToClipboard(textToCopy, this);
    });
}
formatBtns.forEach(btn => {
    btn.addEventListener('click', function() {
        const format = this.dataset.format;
        currentFormat = format;
        formatBtns.forEach(b => {
            b.classList.remove('btn-primary', 'active');
            b.classList.add('btn-outline-secondary');
        });
        this.classList.remove('btn-outline-secondary');
        this.classList.add('btn-primary', 'active');
        if (format === 'json') {
            exampleContent.innerHTML = jsonConfig.html;
        } else {
            exampleContent.innerHTML = yamlConfig.html;
        }
    });
});
```

--------------------------------

### Get Node Information (Centrifugo API)

Source: https://context7.com/centrifugal/centrifugo/llms.txt

Retrieves runtime information about all running Centrifugo nodes, including client and channel counts, metrics, and process statistics.

```bash
curl -X POST http://localhost:8000/api/info \
  -H "Authorization: apikey my-api-key" \
  -H "Content-Type: application/json" \
  -d '{}'
```

--------------------------------

### JavaScript: Generate Full Context JSON

Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html

Builds a map of configuration documentation by field path and generates the JSON representation for a given target field, including highlighting.

```javascript
function generateFullContextJSON(targetField) {
    // Build a map of all docs by field path for quick lookup
    const docsMap = new Map();
    function buildMap(docs) {
        docs.forEach(doc => {
            docsMap.set(doc.field, doc);
            if (doc.children && doc.children.length > 0) {
                buildMap(doc.children);
            }
        });
    }
    buildMap(configData);
    const targetDoc = docsMap.get(targetField);
    if (!targetDoc) return { html: 'Field not found', plain: '' };
    // Parse the field path - handle array notation
    const cleanField = targetField.replace(/\\\[\\\]/g, '');
    const pathParts = cleanField.split('.');
    const rootSection = pathParts[0];
    // Find the root section doc
    const rootDoc = configData.find(d => d.name === rootSection);
    if (!rootDoc) return { html: 'Root section not found', plain: '' };
    let jsonLines = [];
    let plainLines = [];
    function addLine(text, highlight = false) {
        if (highlight) {
            jsonLines.push(`${text}`);
        } else {
            jsonLines.push(text);
        }
        plainLines.push(text);
    }
    function getFieldValue(doc, showArrayStructure = false) {
        if (doc.is_complex_type) {
            if (doc.default === '[]') {
                return showArrayStructure ? '[{}]' : '[]';
            }
            return '{}';
        }
        let val = doc.default;
        if (doc.type.startsWith('[]')) {
            // Array type - check if default is actually an array string or a plain value
            if (!val || val === '[]') {
                return '[]';
            }
            // If default doesn't start with '[', it's a plain value that should be wrapped in array
            if (!va
```

--------------------------------

### Get Environment Variable Name

Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html

Constructs the expected environment variable name for a given configuration field. It handles nested fields, arrays, and complex types, prefixing with 'CENTRIFUGO_' and converting to uppercase.

```javascript
function getEnvVar(doc) {
  if (doc.field.includes('[]')) {
    const hasNamespacedArray = doc.field.match(/\bnamespaces\[\]/) || doc.field.match(/\bproxies\[\]/) || doc.field.match(/\bconsumers\[\]/);
    if (!doc.field.endsWith('.name') && hasNamespacedArray && (!doc.is_complex_type || doc.default === '[]')) {
      let field = doc.field.replace(/\\\[\\\]/g, '_');
      let envVar = 'CENTRIFUGO_' + field.toUpperCase().replace(/\./g, '_');
      return envVar;
    }
    return '';
  } else if (!doc.is_complex_type) {
    return 'CENTRIFUGO_' + doc.field.toUpperCase().replace(/\./g, '_');
  } else if (doc.field.includes('namespaces') || doc.field.includes('proxies') || doc.field.includes('consumers') || doc.default === '[]') {
    return 'CENTRIFUGO_' + doc.field.toUpperCase().replace(/\./g, '_');
  }
  return '';
}
```

--------------------------------

### Get Channel Presence Statistics via HTTP API

Source: https://context7.com/centrifugal/centrifugo/llms.txt

Obtain compact presence statistics, such as the number of clients and unique users in a channel, without retrieving full presence data, by using the `/api/presence_stats` endpoint.

```bash
curl -X POST http://localhost:8000/api/presence_stats \
  -H "Authorization: apikey my-api-key" \
  -H "Content-Type: application/json" \
  -d '{"channel": "chat:room1"}'
```

--------------------------------

### Render Configuration Card

Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html

Creates and appends a configuration card element to the DOM for a given configuration documentation object. It includes the field name, type, and action buttons like a copy link.

```javascript
function renderConfigCard(doc, parent) {
  const fieldId = generateFieldId(doc.field);
  const card = document.createElement('div');
  card.className = 'config-card';
  card.id = fieldId;
  card.dataset.field = doc.field;
  card.dataset.level = doc.level;
  const header = document.createElement('div');
  header.className = 'config-header';
  const titleDiv = document.createElement('div');
  titleDiv.className = 'config-title';
  const heading = document.createElement(`h${Math.min(doc.level, 6)}`);
  const typeIndicator = doc.is_complex_type ? (doc.default === '[]' ? '[array]' : '{object}') : doc.type;
  heading.innerHTML = ` ${doc.field} ${typeIndicator} `;
  titleDiv.appendChild(heading);
  const actions = document.createElement('div');
  actions.className = 'config-actions';
  actions.innerHTML = `    `;
  header.appendChild(titleDiv);
  header.appendChild(actions);
  card.appendChild(header);
  parent.appendChild(card);
}
```

--------------------------------

### Backend Handler for Centrifugo Connect Proxy

Source: https://context7.com/centrifugal/centrifugo/llms.txt

Example backend handler (using Flask) to process connect requests proxied by Centrifugo. It should validate session information and return a JSON response with user details or a disconnect status.

```python
# Backend handler for /centrifugo/connect (Django/Flask/FastAPI example)
# Centrifugo POST body: {"client":"...","transport":"websocket","protocol":"json","encoding":"json","data":{...}}
from flask import Flask, request, jsonify
app = Flask(__name__)

@app.route("/centrifugo/connect", methods=["POST"])
def connect():
    # Validate session cookie, extract user
    session_token = request.cookies.get("session")
    user_id = validate_session(session_token)  # your auth logic
    if not user_id:
        return jsonify({"disconnect": {"code": 4001, "reason": "Unauthorized"}}), 200
    return jsonify({
        "result": {
            "user": user_id,
            "expire_at": int(time.time()) + 3600,
            "info": {"ip": request.remote_addr}
        }
    })
```

--------------------------------

### Minimal Centrifugo Configuration File

Source: https://context7.com/centrifugal/centrifugo/llms.txt

A sample JSON configuration file for Centrifugo, demonstrating settings for client authentication, API keys, Redis engine, gRPC API, admin UI, and channel namespaces.

```json
{
  "client": {
    "token": {
      "hmac_secret_key": "my-secret-key"
    },
    "allowed_origins": ["http://localhost:3000"]
  },
  "http_api": {
    "key": "my-api-key"
  },
  "engine": {
    "type": "redis",
    "redis": {
      "address": "localhost:6379"
    }
  },
  "grpc_api": {
    "enabled": true,
    "port": 10000
  },
  "admin": {
    "enabled": true
  },
  "prometheus": {
    "enabled": true
  },
  "health": {
    "enabled": true
  },
  "channel": {
    "namespaces": [
      {
        "name": "chat",
        "presence": true,
        "join_leave": true,
        "history_size": 100,
        "history_ttl": "5m",
        "force_recovery": true,
        "allow_subscribe_for_client": true,
        "allow_publish_for_subscriber": true
      }
    ]
  }
}
```

--------------------------------

### JavaScript: Generate Full Context Configuration

Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html

Generates the full configuration in JSON or YAML format for a specified target field. Includes HTML highlighting for JSON.

```javascript
function generateFullContextConfig(targetField, format = 'json') {
    const jsonResult = generateFullContextJSON(targetField);
    if (format === 'yaml') {
        const yamlPlain = jsonToYAML(jsonResult.plain);
        // Apply highlighting to YAML
        let yamlHtml = yamlPlain;
        if (jsonResult.html.includes('highlight-line')) {
            // Extract the highlighted field name from the last part of the path
            const highlightMatch = jsonResult.html.match(/\s\*("\w+"):/);
            if (highlightMatch) {
                const fieldName = highlightMatch[1];
                const lines = yamlPlain.split('\n');
                // Find the last occurrence of this field (most nested one)
                let lastMatchIndex = -1;
                lines.forEach((line, idx) => {
                    if (line.match(new RegExp(`^\\s*${fieldName}:\\s`))) {
                        lastMatchIndex = idx;
                    }
                });
                if (lastMatchIndex >= 0) {
                    yamlHtml = lines.map((line, idx) => idx === lastMatchIndex ? `${line}` : line ).join('\n');
                }
            }
        }
        return { html: yamlHtml, plain: yamlPlain };
    }
    return jsonResult;
}
```

--------------------------------

### Initialize Search Functionality

Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html

Sets up event listeners for a search input field and a clear button. It manages search term state and triggers search operations.

```javascript
function initSearch() {
  const searchInput = document.getElementById('search-input');
  const searchClear = document.getElementById('search-clear');
  searchInput.addEventListener('input', function() {
    state.searchTerm = this.value.toLowerCase();
    searchClear.classList.toggle('visible', this.value.length > 0);
    performSearch();
  });
  searchClear.addEventListener('click', function() {
    searchInput.value = '';
    state.searchTerm = '';
    this.classList.remove('visible');
    performSearch();
  });
}
```

--------------------------------

### Render Configuration Card

Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html

Renders a card for a single configuration option, displaying its default value, environment variable, and description. Handles complex types and nested children.

```javascript
function renderConfigCard(doc, parent) {
  const card = document.createElement('div');
  card.className = 'config-card';
  const fieldId = generateFieldId(doc.field);
  card.id = fieldId;

  // Header with field name and type
  const header = document.createElement('div');
  header.className = 'header';
  header.innerHTML = `
    
${doc.field}
${doc.type}
`; // ld(actions); card.appendChild(header); // Meta information let metaHTML = ''; // Only show defaults for non-complex types if (!doc.is_complex_type) { const isStringType = doc.type === 'string' || doc.type === 'PEMData' || doc.type === 'Duration'; const isBoolType = doc.type === 'bool'; let shouldShow = false; let displayDefault = doc.default; if (isStringType) { // Always show string defaults (including empty strings) with quotes shouldShow = true; displayDefault = `"${doc.default || ''}"`; } else if (isBoolType) { // Show boolean defaults, use 'false' if not set shouldShow = true; displayDefault = doc.default || 'false'; } else if (doc.default && doc.default !== '') { // For other types (int, etc.), only show if there's a non-empty default shouldShow = true; displayDefault = doc.default; } if (shouldShow) { metaHTML += `
Default: ${displayDefault}
`; } } const envVar = getEnvVar(doc); if (envVar) { const escapedEnvVar = envVar.replace(//g, '>'); metaHTML += `
Env: ${escapedEnvVar}
`; } // Only add meta div if there's content if (metaHTML) { const meta = document.createElement('div'); meta.className = 'meta-info'; meta.innerHTML = metaHTML; card.appendChild(meta); } // Description const desc = document.createElement('div'); desc.className = 'description'; desc.textContent = doc.comment || 'No documentation available.'; card.appendChild(desc); // Config example button const exampleBtnContainer = document.createElement('div'); exampleBtnContainer.className = 'example-btn-container'; exampleBtnContainer.innerHTML = ` `; card.appendChild(exampleBtnContainer); // Example configuration (hidden by default) const exampleDiv = document.createElement('div'); exampleDiv.className = 'example-config hidden'; exampleDiv.id = fieldId + '-example'; card.appendChild(exampleDiv); parent.appendChild(card); // Handle children if (doc.children && doc.children.length > 0) { const expandIndicator = document.createElement('div'); expandIndicator.className = 'expand-indicator'; expandIndicator.innerHTML = ` ${doc.children.length} nested element${doc.children.length > 1 ? 's' : ''} `; card.appendChild(expandIndicator); const nestedContent = document.createElement('div'); nestedContent.className = 'nested-content'; nestedContent.id = fieldId + '-nested'; doc.children.forEach(child => renderConfigCard(child, nestedContent)); card.appendChild(nestedContent); expandIndicator.addEventListener('click', function() { const isExpanded = nestedContent.classList.contains('open'); if (isExpanded) { nestedContent.classList.remove('open'); expandIndicator.classList.remove('expanded'); state.expandedSections.delete(fieldId); } else { nestedContent.classList.add('open'); expandIndicator.classList.add('expanded'); state.expandedSections.add(fieldId); } saveExpandedState(); }); } } ``` -------------------------------- ### Initialize Centrifuge Client and Connect Source: https://github.com/centrifugal/centrifugo/blob/master/internal/devpage/index.html Sets up the Centrifuge client with multiple transport endpoints and event handlers for connection status. It also defines functions to fetch connection and subscription tokens. ```javascript window.addEventListener('load', function () { const statusEl = document.getElementById('status'); const logEl = document.getElementById('log'); function updateStatus(text, state) { statusEl.textContent = text; statusEl.className = `status ${state}`; } function log(message, isError) { const entry = document.createElement('div'); entry.className = `log-entry${isError ? ' error' : ''}`; const timestamp = new Date().toLocaleTimeString(); entry.innerHTML = `[${timestamp}] ${message}`; logEl.insertBefore(entry, logEl.firstChild); } // Detect protocol and construct transport endpoints const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const httpProtocol = window.location.protocol; const host = window.location.host; const transports = [ { 'transport': 'websocket', 'endpoint': `${wsProtocol}//${host}/connection/websocket` }, { 'transport': 'http_stream', 'endpoint': `${httpProtocol}//${host}/connection/http_stream` }, { 'transport': 'sse', 'endpoint': `${httpProtocol}//${host}/connection/sse` } ]; log('Initializing connection with transports: websocket, http_stream, sse'); const CHANNEL = 'dev'; function getConnectionToken(ctx) { return fetch('/dev/connection_token', { method: 'POST', headers: { 'Content-Type': 'application/json' } }) .then(res => { if (!res.ok) { throw new Error('Failed to get connection token'); } return res.json(); }) .then(data => { log('Received connection token'); return data.token; }) .catch(err => { log(`Error getting connection token: ${err.message}`, true); throw err; }); } function getSubscriptionToken(ctx) { return fetch('/dev/subscription_token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel: ctx.channel }) }) .then(res => { if (!res.ok) { throw new Error('Failed to get subscription token'); } return res.json(); }) .then(data => { log(`Received subscription token for channel: ${ctx.channel}`); return data.token; }) .catch(err => { log(`Error getting subscription token: ${err.message}`, true); throw err; }); } const centrifuge = new Centrifuge(transports, { debug: true, getToken: getConnectionToken, // Workaround to reliably use HTTP/2 Extended CONNECT in Chrome upon reconnections // (when no HTTP/2 connection in pool exists Chrome selects HTTP/1.1 for WebSocket). getData: function () { return fetch(`${httpProtocol}//${host}/connection/init`, {method: 'GET'}).then(function() { return null; }); } }); centrifuge.on('connecting', function (ctx) { updateStatus('Connecting to server...', 'connecting'); log(`Connecting: ${ctx.reason}`); }); centrifuge.on('connected', function (ctx) { updateStatus(`Connected (Client ID: ${ctx.client}, Transport: ${ctx.transport})`, 'connected'); log(`Connected: ${JSON.stringify(ctx)}`); }); centrifuge.on('disconnected', function (ctx) { updateStatus('Disconnected', 'disconnected'); log(`Disconnected: ${ctx.reason}${ctx.reconnect ? ', will try to reconnect' : ', will not reconnect'}`); }); centrifuge.on('error', function (ctx) { log(`Error: ${JSON.stringify(ctx)}`, true); }); // Subscribe to channel const sub = centrifuge.newSubscription(CHANNEL, { getToken: getSubscriptionToken }); sub.on('publication', function (ctx) { log(`Publication received: ${JSON.stringify(ctx)}`); }); sub.on('subscribed', function (ctx) { log(`Subscribed to channel: ${JSON.stringify(ctx)}`); }); sub.on('subscribing', function (ctx) { log(`Subscribing to channel "${CHANNEL}": ${ctx.reason}`); }); sub.on('unsubscribed', function (ctx) { log(`Unsubscribed from channel "${CHANNEL}": ${ctx.reason}`); }); sub.on('error', function (ctx) { log(`Subscription error in channel "${CHANNEL}": ${JSON.stringify(ctx)}`, true); }); sub.subscribe(); log('Starting connection...'); centrifuge.connect(); }); ``` -------------------------------- ### Implement Subscribe Proxy Backend Logic Source: https://context7.com/centrifugal/centrifugo/llms.txt Implement a backend endpoint to handle subscribe requests proxied by Centrifugo. Check user permissions for the requested channel and return a forbidden error or an empty result to allow subscription. ```python @app.route("/centrifugo/subscribe", methods=["POST"]) def subscribe(): body = request.json channel = body.get("channel", "") user = body.get("user", "") # Check user permission for the channel if not has_access(user, channel): return jsonify({"disconnect": {"code": 4003, "reason": "Forbidden"}}), 200 return jsonify({"result": {}}) ``` -------------------------------- ### Configure Client Transports Source: https://context7.com/centrifugal/centrifugo/llms.txt Configure various client transports like WebSocket, SSE, and gRPC. Each transport can have specific options and handler prefixes. ```json { "websocket": { "compression": true, "compression_level": 1, "write_timeout": "1000ms", "message_size_limit": 65536, "handler_prefix": "/connection/websocket" }, "sse": { "enabled": true, "handler_prefix": "/connection/sse" }, "http_stream": { "enabled": true, "handler_prefix": "/connection/http_stream" }, "uni_sse": { "enabled": true, "handler_prefix": "/connection/uni_sse" }, "uni_websocket": { "enabled": true, "handler_prefix": "/connection/uni_websocket" }, "uni_grpc": { "enabled": true, "port": 11000 } } ``` -------------------------------- ### Configure NATS Broker Source: https://context7.com/centrifugal/centrifugo/llms.txt Enable NATS as a PUB/SUB broker for at-most-once message delivery. Configure the NATS URL, prefix, and wildcard settings. ```json { "broker": { "enabled": true, "type": "nats", "nats": { "url": "nats://localhost:4222", "prefix": "centrifugo", "allow_wildcards": false } } } ``` -------------------------------- ### Create Local Redis Cluster Source: https://github.com/centrifugal/centrifugo/blob/master/misc/redis_cluster/readme.md Use this script to create a local Redis cluster with a specified number of nodes and replicas. The cluster is intended for development and benchmarking and stores data in the 'cluster_data' directory. Redis runs without RDB and AOF persistence by default. ```bash bash create_cluster.sh 3 0 ``` -------------------------------- ### Connect to Redis Sentinel Source: https://github.com/centrifugal/centrifugo/blob/master/misc/redis_sentinel/readme.md Use redis-cli to connect to the Sentinel instance running on port 26379. This is the first step before issuing Sentinel commands. ```bash redis-cli -p 26379 ``` -------------------------------- ### Implement Publish Proxy Backend Logic Source: https://context7.com/centrifugal/centrifugo/llms.txt Create a backend endpoint to handle publish requests proxied by Centrifugo. Validate message content and optionally modify it before returning a response. The backend can return an error or a result object. ```python @app.route("/centrifugo/publish", methods=["POST"]) def publish(): body = request.json # body: {"client":"...", "user":"alice", "channel":"chat:room1", "data":{...}} if len(body.get("data", {}).get("text", "")) > 500: return jsonify({"error": {"code": 1000, "message": "Message too long"}}), 200 # Optionally modify data before storing/delivering return jsonify({"result": {"skip_history": False}}) ``` -------------------------------- ### Generate Minimal Centrifugo Configuration Source: https://context7.com/centrifugal/centrifugo/llms.txt Use the `genconfig` command to create a basic configuration file for Centrifugo. This file can then be customized. ```bash centrifugo genconfig ```