sockudo 2.3.2

A simple, fast, and secure WebSocket server for real-time applications.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
# Sockudo ๐Ÿš€

[![Build Status](https://img.shields.io/github/actions/workflow/status/RustNSparks/sockudo/release.yml?branch=main)](https://github.com/RustNSparks/sockudo/actions)
[![Crates.io](https://img.shields.io/crates/v/sockudo)](https://crates.io/crates/sockudo)
[![License](https://img.shields.io/badge/License-AGPL-blue.svg)](LICENSE)
[![Rust Version](https://img.shields.io/badge/rust-1.85+-blue.svg)](https://www.rust-lang.org)
[![WebSocket](https://img.shields.io/badge/websocket-RFC%206455-green.svg)](https://tools.ietf.org/html/rfc6455)

**High-performance, scalable WebSocket server built in Rust** ๐Ÿฆ€

Sockudo is a production-ready WebSocket server that provides real-time communication capabilities with built-in support for channels, presence, authentication, rate limiting, and horizontal scaling. Designed for modern applications that need reliable, fast, and scalable real-time features. It aims for Pusher protocol compatibility, making it a potential backend for clients like Laravel Echo.

---

## โœจ Features

### ๐Ÿš€ **Core Capabilities**
- **High Performance**: Built in Rust for maximum speed and efficiency.
- **WebSocket Protocol**: Full RFC 6455 compliance.
- **Channel System**: Organize connections with public, private, and presence channels. Support for private-encrypted channels is also included.
- **Real-time Messaging**: Instant bidirectional communication.
- **Horizontal Scaling**: Multi-node clustering with adapters for Redis, Redis Cluster, and NATS.

### ๐Ÿ” **Security & Authentication**
- **Channel Authentication**: Secure private and presence channels using token-based authentication.
- **User Authentication**: User sign-in support with custom data, typically validated with signatures.
- **Rate Limiting**: Configurable limits for API and WebSocket connections, with backends like Memory or Redis.
- **SSL/TLS Support**: Production-ready HTTPS and WSS.
- **CORS Configuration**: Flexible cross-origin request handling.

### ๐Ÿ“Š **Monitoring & Observability**
- **Prometheus Metrics**: Comprehensive performance monitoring for connections, messages, API calls, and adapter performance.
- **Health Checks**: Built-in health endpoints (`/up/{appId}`) for load balancers.
- **Structured Logging**: Configurable logging with multiple levels (DEBUG/INFO).
- **Debug Mode**: Enhanced debugging for development.

### ๐ŸŽ›๏ธ **Flexible Architecture**
- **Multiple Adapters**: Choose between Local (single-node), Redis, Redis Cluster, or NATS for message broadcasting and horizontal scaling.
- **Database Support for App Management**: Options include Memory, MySQL, PostgreSQL, and DynamoDB for storing application configurations.
- **Queue Systems**: Supports Memory, Redis, Redis Cluster, and SQS for background job processing (e.g., webhooks).
- **Cache Backends**: Options for Memory, Redis, or Redis Cluster for caching channel data and other information.

### ๐Ÿ”— **Integration Features**
- **Webhooks**: Real-time event notifications (e.g., `channel_occupied`, `channel_vacated`, `member_added`, `member_removed`, `client_event`) to your services.
- **AWS Lambda**: Direct Lambda function invocation for webhooks.
- **REST API**: Full HTTP API for triggering events, batch events, and managing channels/users.
- **Client Libraries**: Compatible with Pusher client libraries due to protocol adherence.

---

## ๐Ÿš€ Quick Start

### Using Docker (Recommended)

```bash
# Clone the repository
git clone https://github.com/RustNSparks/sockudo.git
cd sockudo

# Ensure you have an .env file (e.g., from .env.example)
# cp .env.example .env
# nano .env # Customize if needed

# Quick setup and start (assumes make command is configured)
make quick-start
# Or, using docker-compose directly:
# docker-compose up -d

# you can also use 
docker pull sockudo/sockudo:latest
````

Sockudo should now be running. Default endpoints (configurable):

- **WebSocket Server**: `ws://localhost:6001/app/your-app-key` (e.g., `demo-key` if using default app)
- **REST API**: `http://localhost:6001` (e.g., `http://localhost:6001/apps/demo-app/events`)
- **Metrics**: `http://localhost:9601/metrics`
- **Health Check**: `http://localhost:6001/up/your-app-id`

### Manual Installation

#### Prerequisites

- Rust 1.85+ (`rustup install stable`)
- Redis (optional, for scaling and certain drivers)
- MySQL/PostgreSQL (optional, for persistent app storage if using those drivers)

#### Install from Crates.io

```bash
# Install using cargo
cargo install sockudo

# Or install using cargo-binstall (faster)
cargo binstall sockudo

# Run Sockudo (ensure config/config.json exists or use ENV variables)
sockudo --config config/config.json
```

#### Build from Source

```bash
# Clone the repository
git clone [https://github.com/RustNSparks/sockudo.git](https://github.com/RustNSparks/sockudo.git)
cd sockudo

# Build the project
cargo build --release

# Run with a configuration file (recommended)
./target/release/sockudo --config config/config.json
# Or rely on environment variables and default app settings
# ./target/release/sockudo
```

-----

## ๐Ÿ“– Usage Examples

### WebSocket Client Connection (JavaScript)

```javascript
// Connect to a public channel
const ws = new WebSocket('ws://localhost:6001/app/your-app-key'); // Replace your-app-key

ws.onopen = () => {
    console.log('Connected to Sockudo!');
    // Subscribe to a channel
    ws.send(JSON.stringify({
        event: 'pusher:subscribe',
        data: { channel: 'my-channel' }
    }));
};

ws.onmessage = (event) => {
    const data = JSON.parse(event.data);
    console.log('Received:', data);

    // Example: Handle connection established
    if (data.event === 'pusher:connection_established') {
        const socketId = JSON.parse(data.data).socket_id;
        console.log('My Socket ID is:', socketId);
    }
    // Example: Handle subscription success
    if (data.event === 'pusher_internal:subscription_succeeded' && data.channel === 'my-channel') {
        console.log('Successfully subscribed to my-channel!');
    }
};

ws.onerror = (error) => {
    console.error('WebSocket Error:', error);
};

ws.onclose = () => {
    console.log('Disconnected from Sockudo.');
};
```

### Private Channel with Authentication (Conceptual Client-Side)

*Actual signature generation must happen on your backend.*

```javascript
const ws = new WebSocket('ws://localhost:6001/app/your-app-key');

ws.onopen = () => {
    // Assume 'generatedAuthSignature' is obtained from your backend
    // after authenticating the user for this channel and socket_id.
    // See Security section for more details on signature generation.
    const generatedAuthSignature = "your-app-key:backend_generated_signature_for_private_channel";

    ws.send(JSON.stringify({
        event: 'pusher:subscribe',
        data: {
            channel: 'private-orders',
            auth: generatedAuthSignature
        }
    }));
};
```

### Presence Channel (Conceptual Client-Side)

*Actual signature generation must happen on your backend.*

```javascript
const ws = new WebSocket('ws://localhost:6001/app/your-app-key');

ws.onopen = () => {
    // Assume 'generatedAuthSignatureForPresence' is obtained from your backend.
    // Channel data includes user_id and user_info for presence events.
    const channelData = JSON.stringify({
        user_id: 'user123',
        user_info: { name: 'John Doe', avatar: 'avatar.jpg' }
    });
    const generatedAuthSignatureForPresence = "your-app-key:backend_generated_signature_for_presence_channel";


    ws.send(JSON.stringify({
        event: 'pusher:subscribe',
        data: {
            channel: 'presence-chat',
            auth: generatedAuthSignatureForPresence,
            channel_data: channelData
        }
    }));
};

// Listen for presence events
ws.onmessage = (event) => {
    const message = JSON.parse(event.data);
    if (message.event === 'pusher_internal:member_added' && message.channel === 'presence-chat') {
        console.log('Member added:', message.data);
    }
    if (message.event === 'pusher_internal:member_removed' && message.channel === 'presence-chat') {
        console.log('Member removed:', message.data);
    }
};
```

### REST API - Trigger Events (from your Backend)

Requires authentication (see Security section).

```bash
# Send event to a channel
# Replace your-app-id, your-app-key, and generate a valid auth_signature
curl -X POST "http://localhost:6001/apps/your-app-id/events?auth_key=your-app-key&auth_timestamp=$(date +%s)&auth_version=1.0&auth_signature=your_generated_signature&body_md5=$(echo -n '{"name":"order-update","channels":["private-orders"],"data":{"order_id":123,"status":"shipped"}}' | md5sum | awk '{print $1}')" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "order-update",
    "channels": ["private-orders"],
    "data": {"order_id": 123, "status": "shipped"}
  }'
```

### Client Events (from an Authenticated WebSocket Client)

Client events can only be sent on private or presence channels by authenticated clients for whom the app has `enable_client_messages` set to `true`.

```javascript
// Ensure the WebSocket 'ws' is connected and subscribed to 'presence-chat' (a private or presence channel)
ws.send(JSON.stringify({
    event: 'client-message', // Must start with 'client-'
    channel: 'presence-chat',
    data: { message: 'Hello everyone!' }
}));
```

-----

## ๐Ÿ”ง Configuration

Sockudo can be configured via a JSON file (default: `config/config.json`) or environment variables. Environment variables generally override file settings for basic options, but the file provides more detailed structure.

### Environment Variables (Key Examples)

Create a `.env` file (or set them in your environment):

```bash
# Basic Settings
HOST=0.0.0.0
PORT=6001
DEBUG=false # Enables more verbose logging

# Database (Example for Redis, adapt for MySQL/Postgres/DynamoDB if used as primary for AppManager)
REDIS_URL=redis://redis:6379/0 # Used by multiple components if not overridden
# For MySQL AppManager:
# DATABASE_MYSQL_HOST=mysql
# DATABASE_MYSQL_USER=sockudo
# DATABASE_MYSQL_PASSWORD=your_mysql_password
# DATABASE_MYSQL_DATABASE=sockudo

# Drivers (Lowercase: "local", "redis", "redis-cluster", "nats", "memory", "mysql", "pgsql", "dynamodb", "sqs")
ADAPTER_DRIVER=redis        # For horizontal scaling
APP_MANAGER_DRIVER=memory   # For app definitions
CACHE_DRIVER=redis          # For caching
QUEUE_DRIVER=redis          # For webhooks & background tasks

# Security
SSL_ENABLED=false # Set to true for production and provide paths
# SSL_CERT_PATH=/path/to/cert.pem
# SSL_KEY_PATH=/path/to/key.pem
# REDIS_PASSWORD=your-redis-password # If Redis requires auth

# Application Defaults (if not using a persistent AppManager or for fallback)
SOCKUDO_DEFAULT_APP_ID=demo-app
SOCKUDO_DEFAULT_APP_KEY=demo-key
SOCKUDO_DEFAULT_APP_SECRET=demo-secret
SOCKUDO_ENABLE_CLIENT_MESSAGES=true # For the default app
```

*Refer to `src/options.rs` and `src/main.rs` for a comprehensive list of all ENV var overrides and their default behaviors.*

### Configuration File (`config/config.json`)

Provides detailed control over all aspects. Below is a snippet; refer to your uploaded `config/config.json` for the full structure.

```json
{
  "debug": false,
  "host": "0.0.0.0",
  "port": 6001,
  "adapter": {
    "driver": "redis", // "local", "redis", "redis-cluster", "nats"
    "redis": { // Settings for 'redis' or 'redis-cluster' if chosen
      "prefix": "sockudo_adapter:",
      "cluster_mode": false // Set true if using redis-cluster via the 'redis' driver config
      // "requests_timeout": 5000
    },
    "cluster": { // Specific settings if driver is 'redis-cluster'
      // "nodes": ["redis://node1:7000", "redis://node2:7001"],
      // "prefix": "sockudo_cluster_adapter:"
    },
    "nats": {
      // "servers": ["nats://nats-server:4222"],
      // "prefix": "sockudo_nats_adapter:"
    }
  },
  "app_manager": {
    "driver": "memory", // "memory", "mysql", "pgsql", "dynamodb"
    "array": { // Used if driver is "memory" and you want to define apps in config
      "apps": [
        {
          "id": "my-app-id",
          "key": "my-app-key",
          "secret": "my-app-secret",
          "max_connections": 1000,
          "enable_client_messages": true,
          // ... other app-specific limits from src/app/config.rs
        }
      ]
    }
    // database-specific app_manager configs would go under "database"
  },
  "cache": {
    "driver": "redis", // "memory", "redis", "redis-cluster", "none"
    "redis": {
      // "prefix": "sockudo_cache:",
      // "url_override": "redis://another-redis:6379"
    },
    "memory": {
      // "ttl": 300, "max_capacity": 10000
    }
  },
  "queue": {
    "driver": "redis", // "memory", "redis", "redis-cluster", "sqs", "none"
    "redis": {
      // "concurrency": 5, "prefix": "sockudo_queue:"
    },
    "sqs": {
      // "region": "us-east-1", "concurrency": 5
    }
  },
  "rate_limiter": {
    "enabled": true,
    "driver": "redis", // Usually "memory" or "redis"
    "api_rate_limit": {
      "max_requests": 100,
      "window_seconds": 60
    },
    "websocket_rate_limit": { // Applied on connection
      "max_requests": 20,
      "window_seconds": 60
    }
  },
  "metrics": {
    "enabled": true,
    "driver": "prometheus", // Currently only prometheus supported
    "port": 9601
  },
  "ssl": {
    "enabled": false,
    // "cert_path": "/path/to/fullchain.pem",
    // "key_path": "/path/to/privkey.pem"
  }
  // ... many other options available, see src/options.rs
}
```

-----

## ๐Ÿ—๏ธ Architecture

### System Overview

```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Client Apps   โ”‚    โ”‚  Load Balancer    โ”‚    โ”‚   Sockudo Node  โ”‚
โ”‚ (Web/Mobile/IoT)โ”‚โ—„โ”€โ”€โ–บโ”‚ (e.g., Nginx,    โ”‚โ—„โ”€โ”€โ–บโ”‚     (Rust)      โ”‚
โ”‚ using PusherJS  โ”‚    โ”‚      AWS ALB)     โ”‚    โ”‚                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                                        โ”‚ โ–ฒ
                                                        โ”‚ โ”‚ Pub/Sub
                        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”‚ โ–ผ
                        โ”‚ Pub/Sub Backend โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        โ”‚ (Redis/NATS for โ”‚
                        โ”‚ HORIZONTAL_ADAPTER)โ”‚
                        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
      โ”‚ App Config Storage  โ”‚โ—„โ”€โ”€โ”˜โ”€โ”€โ–บโ”‚  Cache Storage     โ”‚
      โ”‚ (MySQL/PG/DynamoDB/ โ”‚       โ”‚  (Redis/Memory)    โ”‚
      โ”‚      Memory)        โ”‚       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

### Core Components

- **Connection Handler (`adapter::handler`)**: Manages individual WebSocket connections, message parsing, authentication, and routing to appropriate handlers.
- **Channel Manager (`channel::manager`)**: Handles channel subscriptions, presence state, and signature validation for channel access.
- **Adapter (`adapter`)**: Abstract interface for connection management and message broadcasting.
    - **Local Adapter (`adapter::local_adapter`)**: Single-node operation.
    - **Horizontal Adapters (`adapter::redis_adapter`, `adapter::redis_cluster_adapter`, `adapter::nats_adapter`)**: Enable multi-node setups by using Redis, Redis Cluster, or NATS for pub/sub to synchronize state and broadcast messages across instances.
- **App Manager (`app::manager`)**: Manages application configurations (keys, secrets, limits, features), with backends like Memory, MySQL, PostgreSQL, or DynamoDB.
- **Cache Manager (`cache::manager`)**: Provides caching for frequently accessed data (e.g., channel information, app settings) with Memory or Redis backends.
- **Queue Manager (`queue::manager`)**: Handles background tasks, primarily for delivering webhooks, using Memory, Redis, Redis Cluster, or SQS.
- **Rate Limiter (`rate_limiter`)**: Protects against abuse with configurable limits, using Memory or Redis.
- **Webhook System (`webhook`)**: Delivers real-time server events (e.g., channel occupied/vacated, member added/removed) to configured HTTP endpoints or AWS Lambda functions.
- **Metrics Collector (`metrics`)**: Exposes Prometheus metrics for monitoring performance and health.
- **HTTP Handler (`http_handler`)**: Provides the Pusher-compatible REST API for triggering events and querying server state.
- **WebSocket Handler (`ws_handler`)**: Manages the WebSocket upgrade process and initial connection setup.

### Supported Architectures

- **Single Node**: Ideal for development, testing, and smaller applications. Uses `local` adapter and `memory` for other components if not otherwise configured.
- **Multi-Node (Clustered)**: Achieves horizontal scalability using Redis, Redis Cluster, or NATS as the `adapter.driver` for message broadcasting and state synchronization between Sockudo instances.
- **Microservices Integration**: Sockudo can act as the real-time layer in a microservices architecture, receiving events to broadcast via its HTTP API.
- **Serverless Integration**: Webhooks can trigger AWS Lambda functions, allowing for serverless processing of Sockudo events.

-----

## ๐Ÿ”— Client Integration

Sockudo implements the **Pusher WebSocket protocol**. This means you can use any Pusher-compatible **client-side library** to connect to Sockudo for real-time messaging.

For triggering events from **your application backend** (server-to-server), you would use Sockudo's HTTP API (see API Reference section), potentially with an HTTP client or a Pusher *server-side* library configured to point to Sockudo's HTTP API endpoints.

### Recommended Client-Side Libraries (for WebSocket Connection)

#### JavaScript/TypeScript (PusherJS)

This is the most common library for frontend integration.

```bash
npm install pusher-js
```

```javascript
import Pusher from 'pusher-js';

const pusher = new Pusher('your-app-key', { // Use the 'key' from your app config
  wsHost: 'localhost',      // Your Sockudo host
  wsPort: 6001,             // Your Sockudo WebSocket port
  wssPort: 6001,            // Your Sockudo WSS port (if SSL_ENABLED=true)
  forceTLS: false,          // Set true if using WSS
  enabledTransports: ['ws', 'wss'],
  cluster: 'mt1', // Required by pusher-js, value doesn't impact Sockudo directly
  disableStats: true, // Recommended
  authEndpoint: '/your-backend-auth-endpoint-for-private-channels', // For private/presence channels
  // authTransport: 'ajax' // or 'jsonp'
});

const channel = pusher.subscribe('public-channel');
channel.bind('some-event', (data) => {
  console.log('Received data:', data);
});
```

#### Other Pusher-Compatible Client Libraries

Most Pusher client libraries for other languages (Python, Ruby, Java, .NET/C\#, Swift, Android, etc.) can be configured to connect to a custom host and port, allowing them to work with Sockudo. You'll typically need to set:

- App Key
- Host (your Sockudo server address)
- Port (your Sockudo WebSocket port)
- `forceTLS` (or equivalent for secure connections)
- An `authEndpoint` if using private or presence channels.

#### Native WebSocket

You can also connect directly using any standard WebSocket client, but you'll need to implement the Pusher message format manually (e.g., for `pusher:subscribe`, `pusher:ping`).

```javascript
const ws = new WebSocket('ws://localhost:6001/app/your-app-key');

ws.onopen = () => {
  ws.send(JSON.stringify({
    event: 'pusher:subscribe',
    data: { channel: 'my-channel' }
  }));
};
// ... handle other WebSocket events
```

### Triggering Events from Your Backend (Server-Side)

Use Sockudo's HTTP API. Examples using `curl` are in the "Usage Examples" and "API Reference" sections.
Libraries like Pusher's official PHP or Go server libraries can also be used if you configure their host/port to point to your Sockudo HTTP API endpoint (`http://localhost:6001` by default).

**PHP (Pusher Server SDK - configured for Sockudo)**

```php
<?php
require __DIR__ . '/vendor/autoload.php';

$options = [
  'host' => 'localhost', // Sockudo HTTP API host
  'port' => 6001,        // Sockudo HTTP API port
  'scheme' => 'http',    // 'https' if SSL_ENABLED=true on Sockudo
  'encrypted' => false,  // Relevant for Pusher Cloud, less so here
  'useTLS' => false,     // Set true if Sockudo is using HTTPS
  // cluster parameter is often not needed when directly targeting Sockudo
];

// Ensure 'your-app-id' matches an 'id' field in one of your configured apps in Sockudo
$pusher = new Pusher\Pusher('your-app-key', 'your-app-secret', 'your-app-id', $options);

$data = ['message' => 'hello world from PHP backend via Sockudo'];
$pusher->trigger('my-channel', 'my-event', $data);
?>
```

**Go (Pusher HTTP Go - configured for Sockudo)**

```go
package main

import (
	"fmt"
	"[github.com/pusher/pusher-http-go/v5](https://github.com/pusher/pusher-http-go/v5)"
)

func main() {
	client := pusher.Client{
		AppID:   "your-app-id",     // Matches 'id' in Sockudo app config
		Key:     "your-app-key",    // Matches 'key'
		Secret:  "your-app-secret", // Matches 'secret'
		Host:    "localhost:6001",  // Sockudo HTTP API host and port
		Secure:  false,             // Set to true if Sockudo is using HTTPS
	}

	data := map[string]string{"message": "hello from Go backend via Sockudo"}
	events, err := client.Trigger("my-channel", "my-event", data)
	if err != nil {
		fmt.Println("Error triggering event:", err)
		return
	}
	fmt.Printf("Events triggered: %+v\n", events)
}
```

-----

## ๐Ÿšข Deployment

Refer to `README-Docker.md` for comprehensive Docker deployment instructions.

### Docker Deployment (Recommended)

#### Development

```bash
# Start development environment (uses docker-compose.yml and docker-compose.dev.yml)
make dev # (If Makefile provides this shortcut)
# OR
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

# View logs
docker-compose logs -f sockudo

# Run tests (if configured in a Makefile or script)
# make test
```

#### Production

```bash
# Production deployment (uses docker-compose.yml and docker-compose.prod.yml)
make prod # (If Makefile provides this shortcut)
# OR
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Scale to multiple instances (example)
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d --scale sockudo=3

# Monitor performance (example for Docker stats)
docker stats
```

### Kubernetes

A basic Kubernetes deployment might look like this (adapt as needed):

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sockudo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sockudo
  template:
    metadata:
      labels:
        app: sockudo
    spec:
      containers:
      - name: sockudo
        image: sockudo/sockudo:latest
        ports:
        - name: websocket
          containerPort: 6001 # Sockudo's application port
        - name: metrics
          containerPort: 9601 # Sockudo's metrics port
        env:
        - name: ADAPTER_DRIVER 
          value: "redis" # or "nats", "redis-cluster"
        - name: REDIS_URL
          value: "redis://your-redis-service:6379"
        # Add other necessary environment variables or configmap mounts
        # - name: CONFIG_FILE_PATH
        #   value: "/app/config/config.json"
        # volumeMounts:
        # - name: sockudo-config
        #   mountPath: /app/config
      # volumes:
      # - name: sockudo-config
      #   configMap:
      #     name: sockudo-configmap
```

*Ensure your Kubernetes setup includes appropriate services, ingress, and configuration for Redis/NATS if using horizontal scaling.*

### Cloud Platforms

Sockudo can be deployed to various cloud platforms:

#### AWS ECS / EKS

- Use the production Docker image.
- Configure Application Load Balancer (ALB) or Network Load Balancer (NLB) with WebSocket support (sticky sessions might be needed depending on adapter and client behavior, though ideally not with a proper horizontal adapter).
- Use ElastiCache for Redis (standalone or cluster) if using Redis adapter/cache/queue.
- Use RDS for MySQL/PostgreSQL if using those for AppManager.
- Use Amazon SQS if using the SQS queue driver.

#### Google Cloud Run / GKE

- Enable WebSocket support in Cloud Run or use GKE.
- Use Cloud Memorystore for Redis.
- Configure Cloud SQL for persistent app storage.

#### Azure Container Instances / AKS

- Use Azure Cache for Redis.
- Configure Azure Database for MySQL/PostgreSQL.

-----

## ๐Ÿ“Š Monitoring

### Metrics

Sockudo exposes Prometheus metrics at the `/metrics` endpoint (default port 9601).
Key metrics include:

- **Connection Metrics**:
    * `sockudo_connected`: Current number of active WebSocket connections.
    * `sockudo_new_connections_total`: Total number of new connections established.
    * `sockudo_new_disconnections_total`: Total number of disconnections.
- **Message Metrics**:
    * `sockudo_ws_messages_sent_total`: Total WebSocket messages sent from server to clients.
    * `sockudo_ws_messages_received_total`: Total WebSocket messages received by server from clients.
    * `sockudo_socket_transmitted_bytes`: Total bytes transmitted over WebSockets.
    * `sockudo_socket_received_bytes`: Total bytes received over WebSockets.
- **HTTP API Metrics**:
    * `sockudo_http_calls_received_total`: Total HTTP API calls received.
    * `sockudo_http_transmitted_bytes`: Total bytes sent in HTTP API responses.
    * `sockudo_http_received_bytes`: Total bytes received in HTTP API requests.
- **Horizontal Adapter Metrics** (if using Redis/NATS adapter):
    * `sockudo_horizontal_adapter_resolve_time`: Histogram of request resolution time between nodes.
    * `sockudo_horizontal_adapter_resolved_promises`: Total promises fulfilled by other nodes.
    * `sockudo_horizontal_adapter_sent_requests`: Total requests sent to other nodes.
    * `sockudo_horizontal_adapter_received_requests`: Total requests received from other nodes.
    * `sockudo_horizontal_adapter_received_responses`: Total responses received from other nodes.
- **Channel Statistics** (can be inferred or might require specific metrics not listed but common):
    * `sockudo_channel_count` (conceptual, often derived from `get_channels_with_socket_count`)
    * `sockudo_active_subscriptions` (conceptual)

### Grafana Dashboard

If using the Docker setup with Grafana (`docker-compose.prod.yml`), a pre-configured dashboard might be available. Otherwise, you can import/create dashboards in Grafana using Prometheus as a data source.
Access Grafana at `http://localhost:3000` (default credentials may vary, check `docker-compose.prod.yml` or `README-Docker.md`).

### Health Checks

- **Application Health**: `GET /up/{app_id}` - Checks if a specific application is responsive.
- **Metrics Endpoint**: `GET /metrics` (default on port 9601) - Exposes Prometheus metrics.
- **General Usage/Memory**: `GET /usage` - Provides memory usage statistics of the Sockudo server.

-----

## ๐Ÿ”’ Security

### Authentication

#### Channel Authentication (Private & Presence Channels)

Sockudo validates `auth` tokens for private and presence channel subscriptions. The signature is typically generated on your backend.
The string to sign depends on the channel type:

- **Private channels**: `socket_id:channel_name`
- **Presence channels**: `socket_id:channel_name:channel_data_json_string` (where `channel_data_json_string` is the JSON string provided in the `channel_data` field of the subscription request).

**Example Server-Side Signature Generation (Node.js conceptual):**

```javascript
const crypto = require('crypto');

function generateChannelAuth(socketId, channelName, appSecret, channelDataString = null) {
    let stringToSign = `${socketId}:${channelName}`;
    if (channelDataString) { // For presence channels
        stringToSign += `:${channelDataString}`;
    }
    const signature = crypto.createHmac('sha256', appSecret)
                              .update(stringToSign)
                              .digest('hex');
    return signature; // The client would send appKey:signature
}

// For a private channel:
// const appKey = "your-app-key";
// const signature = generateChannelAuth(clientSocketId, "private-mychannel", "your-app-secret");
// const authString = `${appKey}:${signature}`;

// For a presence channel:
// const channelData = JSON.stringify({ user_id: "123", user_info: { name: "Alice" } });
// const presenceSignature = generateChannelAuth(clientSocketId, "presence-mychannel", "your-app-secret", channelData);
// const presenceAuthString = `${appKey}:${presenceSignature}`;
```

*Refer to `src/channel/manager.rs` (method `get_data_to_sign_for_signature`) for the exact server-side string construction.*

#### User Authentication (pusher:signin)

If `enable_user_authentication` is true for an app, clients can send a `pusher:signin` event.
The string to sign for user authentication is `socket_id::user::user_data_json_string`.

**Example Server-Side User Auth Token Generation (Node.js conceptual):**

```javascript
const crypto = require('crypto');

function generateUserAuth(socketId, userDataJsonString, appSecret) {
    const stringToSign = `${socketId}::user::${userDataJsonString}`;
    const signature = crypto.createHmac('sha256', appSecret)
                              .update(stringToSign)
                              .digest('hex');
    return signature; // The client would send this as part of the pusher:signin 'auth' field
}

// const appKey = "your-app-key";
// const userData = JSON.stringify({ id: "user456", name: "Bob" });
// const userAuthSignature = generateUserAuth(clientSocketId, userData, "your-app-secret");
// Client sends: { event: "pusher:signin", data: { auth: `${appKey}:${userAuthSignature}`, user_data: userData }}
// Note: The `src/app/auth.rs` `validate_channel_auth` and `sing_in_token_for_user_data` suggest the client sends only the signature part in `auth`, and `user_data` separately. Your client example shows this correctly.
```

*Refer to `src/app/auth.rs` (method `sing_in_token_for_user_data`) for server-side logic.*

### Rate Limiting

Configure rate limits in `config/config.json` or via environment variables to protect against abuse. Sockudo supports separate limits for HTTP API requests and WebSocket connections.
Backends like Memory or Redis can be used for rate limiting.

```json
{
  "rate_limiter": {
    "enabled": true,
    "driver": "redis", // "memory" or "redis"
    "api_rate_limit": {
      "max_requests": 1000, // e.g., per minute for all API calls from an IP
      "window_seconds": 60
    },
    "websocket_rate_limit": { // Applied per IP at connection time
      "max_requests": 60,   // e.g., max 60 connection attempts per minute from an IP
      "window_seconds": 60
    },
    "redis": { // If Redis driver is used for rate limiting
        "prefix": "sockudo_rl:"
        // url_override can also be set here if different from main Redis
    }
  }
}
```

*Client-specific event rate limits are configured per-application (e.g., `max_client_events_per_second`).*

### SSL/TLS

Enable HTTPS and WSS in production by setting `ssl.enabled = true` in `config/config.json` or `SSL_ENABLED=true` (env var) and providing paths to your SSL certificate and key.
Refer to `README-Docker.md` for Docker-specific SSL setup with Nginx or Let's Encrypt.

```json
  "ssl": {
    "enabled": true,
    "cert_path": "/etc/letsencrypt/live/[your-domain.com/fullchain.pem](https://your-domain.com/fullchain.pem)",
    "key_path": "/etc/letsencrypt/live/[your-domain.com/privkey.pem](https://your-domain.com/privkey.pem)",
    "redirect_http": true, // Redirect HTTP traffic to HTTPS
    "http_port": 8080 // Port for HTTP redirect server if Sockudo handles SSL
  }
```

-----

## ๐Ÿ”Œ API Reference

### WebSocket Events (Pusher Protocol)

Sockudo aims for compatibility with the Pusher WebSocket protocol. Key events include:

#### Connection Events

- `pusher:connection_established`: Sent to the client upon successful connection. Contains `socket_id` and `activity_timeout`.
- `pusher:error`: Sent to the client when an error occurs (e.g., auth failure, invalid message). Contains `code` and `message`.
- `pusher:ping`: Sent by the server to check if the client is alive. Client should respond with `pusher:pong`.
- `pusher:pong`: Sent by the client in response to a `pusher:ping`.

#### Subscription Events

- `pusher:subscribe`: Sent by the client to subscribe to a channel. `data` includes `channel` name and optionally `auth` string (for private/presence) and `channel_data` (for presence).
- `pusher:unsubscribe`: Sent by the client to unsubscribe from a channel. `data` includes `channel` name.
- `pusher_internal:subscription_succeeded`: Sent to the client by the server confirming successful subscription to a channel. For presence channels, `data` includes current member list (`presence.hash`, `presence.ids`, `presence.count`).
- `pusher:cache_miss`: Sent to the client if they subscribe to a cache channel and no data is initially cached.

#### Presence Events (Sent by Server to Subscribed Clients)

- `pusher_internal:member_added`: Sent to clients in a presence channel when a new member joins. `data` includes `user_id` and `user_info`.
- `pusher_internal:member_removed`: Sent to clients in a presence channel when a member leaves. `data` includes `user_id`.

#### User Authentication Events

- `pusher:signin`: Sent by the client to authenticate themselves as a user. `data` includes `auth` (app\_key:signature) and `user_data` (JSON string with user details like `id`).
- `pusher:signin_success`: Sent to the client by the server upon successful user sign-in.

#### Client Events (Sent by Client, Broadcast by Server)

- `client-*` (e.g., `client-message`): Custom events sent by one client, broadcast by the server to other clients on the same private or presence channel (excluding the sender by default, if `socket_id` is provided when triggering via HTTP API). The app must have `enable_client_messages: true`.

### REST API Endpoints

All endpoints are prefixed with `/apps/{app_id}/`. Authentication is required for these endpoints.

#### Trigger Event

- **Endpoint**: `POST /apps/{app_id}/events`
- **Description**: Triggers an event on one or more channels.
- **Body**:
  ```json
  {
    "name": "your-event-name",
    "channels": ["channel-1", "channel-2"], // Or "channel": "single-channel"
    "data": {"key": "value"}, // JSON string or object for event data
    "socket_id": "optional_socket_id_to_exclude" // Optional
    // "info": "user_count,subscription_count" // Optional: to request info about channels in response
  }
  ```
- **Response**: `200 OK` with `{"ok":true}` or channel info if requested.

#### Batch Events

- **Endpoint**: `POST /apps/{app_id}/batch_events`
- **Description**: Triggers multiple events in a single request.
- **Body**:
  ```json
  {
    "batch": [
      {"name": "event1", "channel": "channel1", "data": "data1"},
      {"name": "event2", "channels": ["channel2", "channel3"], "data": {"complex":"data2"}}
      // Each item can also have "socket_id" and "info"
    ]
  }
  ```
- **Response**: `200 OK` with `{}` or `{"batch": [...]}` containing info for each event if requested.

#### Get Channel Information

- **Endpoint**: `GET /apps/{app_id}/channels/{channel_name}`
- **Description**: Retrieves information about a specific channel.
- **Query Parameters**:
    * `info`: Comma-separated list of attributes to retrieve (e.g., `user_count`, `subscription_count`, `cache`).
- **Response**: `200 OK` with JSON containing channel status (e.g., `{"occupied": true, "user_count": 5, "subscription_count": 10}`).

#### List Channels

- **Endpoint**: `GET /apps/{app_id}/channels`
- **Description**: Retrieves a list of active channels in an application.
- **Query Parameters**:
    * `filter_by_prefix`: Filters channels by the given prefix.
    * `info`: Comma-separated list of attributes to retrieve for each channel (e.g., `user_count`).
- **Response**: `200 OK` with JSON `{"channels": {"channel_name": {"user_count": 2}, ...}}`.

#### Get Users in a Presence Channel

- **Endpoint**: `GET /apps/{app_id}/channels/{channel_name}/users`
- **Description**: Retrieves the list of users subscribed to a specific presence channel.
- **Response**: `200 OK` with JSON `{"users": [{"id": "user_id_1"}, {"id": "user_id_2"}]}`.

#### Terminate User Connections

- **Endpoint**: `POST /apps/{app_id}/users/{user_id}/terminate_connections`
- **Description**: Forcibly disconnects all WebSocket connections associated with a given `user_id`.
- **Response**: `200 OK` with `{"ok":true}`.

### Other Endpoints

- **Health Check**: `GET /up/{app_id}` - Returns `200 OK` if the app is known (even if no connections).
- **Server Usage**: `GET /usage` - Returns server memory statistics.
- **Prometheus Metrics**: `GET /metrics` (default port 9601) - Exposes metrics in Prometheus format.

-----

## ๐Ÿงช Testing

Sockudo includes a suite of tests to ensure reliability.

### Running Tests

```bash
# Run all unit and integration tests (ensure dependencies like Redis might be needed for some)
cargo test

# For Docker-based testing environment, refer to README-Docker.md
# Example: make ci-test (if Makefile provides this)
```

### Interactive Tester

The project includes an interactive tester in `test/interactive/` which can be used to manually test WebSocket connections, subscriptions, client events, and webhooks against a running Sockudo instance.

### Automated Tests

Automated tests can be found in `test/automated/` which might include scripts for load testing or specific feature validation.

-----

## ๐Ÿค Contributing

We welcome contributions! Please see our [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on how to get started, coding standards, and the PR review process.

-----

## ๐Ÿ“š Documentation

For more in-depth information:

- Explore the `src/` directory for detailed module implementations.
- Configuration options are detailed in `src/options.rs`.
- Docker setup is described in `README-Docker.md`.
- (If a dedicated documentation site exists, link it here, e.g., `https://sockudo.app` as mentioned in the original README).

-----

## ๐Ÿ“„ License

This project is licensed under the **AGPL-3.0 License** - see the [LICENSE](https://www.google.com/search?q=LICENSE) file for details. (Updated based on the badge at the top of your README).

-----

## ๐Ÿ™ Acknowledgments

- Built with [Tokio]https://tokio.rs/ for async runtime.
- Uses [fastwebsockets]https://github.com/denoland/fastwebsockets for WebSocket implementation.
- Inspired by [Pusher]https://pusher.com/ and similar real-time messaging platforms like [Laravel Reverb]https://reverb.laravel.com/.
- Thanks to the Rust community for its robust ecosystem and excellent crates.

-----

## ๐Ÿ“ž Support

### Community Support
- **GitHub Issues**: [Report bugs and request features]https://github.com/RustNSparks/sockudo/issues
- **Crates.io**: [View package details]https://crates.io/crates/sockudo
- **Discord**: [Join the RustNSparks server]https://discord.gg/GUuxFbD8pU
- **X**: [Follow us on X]https://x.com/sockudorealtime

**โญ Star us on GitHub if Sockudo helps your project\! โญ**

[GitHub Repository](https://github.com/RustNSparks/sockudo) โ€ข [Crates.io](https://crates.io/crates/sockudo))