
dSock is a distributed WebSocket broker (in Go, using Redis).
Clients can authenticate & connect, and you can send text/binary message as an API.
Multiple clients per user & authentication
dSock can broadcast a message to all clients for a certain user (identified by user ID and optionally session ID) or a certain connection (by ID). Users can be authenticated using claims or JWTs (see below).
Distributed
dSock can be scaled up easily as it uses Redis as a central database & pub/sub, with clients connecting to worker. It's designed to run on the cloud using scalable platforms such as Kubernetes or Cloud Run.
Text & binary messaging
dSock is designed for text and binary messaging, enabling JSON (UTF-8), Protocol Buffers, or any custom protocol.
Lightweight & fast
dSock utilized Go's concurrency for great performance at scale, with easy distribution and safety. It is available as Docker images for convenience.
Disconnects
Disconnect clients from an external event (logout) from a session ID or for all user connections.
The main use case for dSock is having stateful WebSocket connections act as a stateless API.
This enables you to not worry about your connection handling and simply send messages to all (or some) of a user's clients as any other HTTP API.
Chat service
Clients connect to dSock, and your back-end can broadcast messages to a specific user's clients
More!
Use a client to interact with the dSock API easily. Your language missing? Open a ticket!

dSock is separated into 2 main services:
dSock Worker This is the main server clients connect to. The worker distributed the messages to the clients ("last mile")
dSock API The API receives messages and distributes it to the workers for target clients
This allows the worker (connections) and API (gateway) to scale independently and horizontally.
dSock uses Redis as a backend data store, to store connection locations and claims.
| Word | |
|---|---|
| WebSocket | Sockets "over" HTTP(S) |
| JWT | JSON Web Token |
| Claim | dSock authentication mention using a pre-registered claim ("token") |
| Redis | Open-source in-memory key-value database |
POST /send) with the target (user, session optionally) and the message as bodydSock is published as binaries and as Docker images.
Binaries are available on the releases pages.
You can simply run the binary for your architecture/OS.
You can configure dSock using environment variables or a config (see below).
Docker images are published on Docker Hub:
The images are small (~15MB) and expose on port 80 by default (controllable by setting the PORT environment variable).
It is recommended to use the environment variables to configure dSock instead of a config when using the images.
Configs are still supported (can be mounted to /config.toml or /config.$EXT, see below).
dSock can be configured using a config file or using environment variables.
PORT (port, integer, or DSOCK_PORT environment variable): Port to listen to. Defaults to 6241DSOCK_ADDRESS (address, string, deprecated):: Address to listen to. Defaults to :6241. Uses port if empty.DSOCK_REDIS_HOST (redis_host, string): Redis host. Defaults to localhost:6379DSOCK_REDIS_PASSWORD (redis_password, string): Redis password. Defaults to no passwordDSOCK_REDIS_DB (redis_db, integer): Redis database. Defaults to 0DSOCK_REDIS_MAX_RETRIES (redis_max_retries, integer): Maximum retries before failing Redis connection. Defaults to 10DSOCK_REDIS_TLS (redis_tls, boolean): Whether to enable TLS for Redis. Defaults to falseDSOCK_DEFAULT_CHANNELS (default_channels, comma-delimited string, optional): When set, clients will be automatically subscribed to these channelsDSOCK_TOKEN (token, string): Authentication token to do requests to the APIDSOCK_JWT_SECRET (jwt_secret, string, optional): When set, enables JWT authenticationDSOCK_DEBUG (debug, boolean): Enables debugging, useful for development. Defaults to falseDSOCK_LOG_REQUESTS (log_requests, boolean): Enables request logging. Defaults to falseDSOCK_MESSAGING_METHOD (messaging_method, string): The messages method for communication from API to worker. Can be: redis, direct. Defaults to redisDSOCK_DIRECT_MESSAGE_HOSTNAME (direct_message_hostname, string, worker only): If method_method is set to direct, this is the hostname of the worker accessible from the API. Defaults to first local non-loopback IPv4DSOCK_DIRECT_MESSAGE_PORT (direct_message_port, string, worker only): If method_method is set to direct, this is the port that the worker is listening on. Defaults to portYou can write your config file in TOML (recommended), JSON, YAML, or any format supported by viper
Configs are loaded from (in order):
$PWD/config.$EXT$HOME/.config/dsock/config.$EXT/etc/dsock/config.$EXTA default config will be created at $PWD/config.toml if no config is found.
All API calls will return a success boolean.
If it is false, it will also add error (message) and errorCode (constant from common/errors.go).
All API calls (excluding /connect endpoint) requires authentication with a token query parameter, or set as a Authorization header in the format of: Bearer $TOKEN.
Having an invalid or missing token will result in the INVALID_AUTHORIZATION error code.
Most errors starting with ERROR_ are downstream errors, usually from Redis. Check if your Redis connection is valid!
When targeting, the precedence order is: id, channel, user.
Claims are the recommended way to authenticate with dSock. Before a client connects, they should hit your API (which you can use your usual authentication), and your API requests the dSock API to create a "claim", which you then return to the client.
Once a client has a claim, it can then connect to the worker using the claim query parameter.
You can create them by accessing the API as POST /claim with the following query options:
user (required, string): The user IDsession (optional, string): The session ID (scoped per user)channels (optional, comma-delimited string): Channels to subscribe on join (merged with default_channels)expiration (integer, seconds from epoch): Time the claim expires (takes precedence over duration)duration (integer, seconds): Duration of the claimtoken (required, string): Authorization token for API set in config. Can also be a Authorization Bearer tokenid (optional, string): The claim ID to use. This should not be guessed, so long random string or UUIDv4 is recommended. If not set, it will generate a random string (recommended to let dSock generate the ID)The returned body will contain the following keys:
claim: The claim dataid: The claim IDexpiration: The expiration in seconds from epochuser: The user for the claimsession (if session is provided): The user session for the claimchannels: The channels to subscribe on join (excludes defaults)A claim is single-use, so once a client connects, it will instantly expire.
Create a claim for a user (1) expiring in 10 seconds, with 2 channels:
POST /claim?token=abcxyz&user=1&duration=10&channels=group-1,group-2
Create a claim for a user (1) with a session (a) with a claim ID (a1b2c3) expiring at some time:
POST /claim?user=1&session=a&expiration=1588473164&id=a1b2c3
Authorization: Bearer abcxyz
Creating a claim has the follow possible errors:
USER_ID_REQUIRED: If the user parameter is not setINVALID_EXPIRATION: If the expiration is invalid (not parsable as integer)NEGATIVE_EXPIRATION: If the expiration is negativeINVALID_DURATION: If the duration is invalid (not parsable as integer)NEGATIVE_DURATION: If the duration is negativeERROR_CHECKING_CLAIM: If an error occurred during checking if a claim exist (Redis error)CLAIM_ID_ALREADY_USED: If the claim ID is set and is already usedTo authenticate a client, you can also create a JWT token and deliver it to the client before connecting. To enable this, set the jwt_secret to with your JWT secret (HMAC signature secret)
Payload options:
sub (required, string): The user IDsid (optional, string): The session ID (scoped per user)channels (optional, array of string): Channels to subscribe on join (merged with default_channels)iat (integer, in seconds from epoch): Time the JWT is issued (expires 1 minute after this time)exp (integer, in seconds from epoch): Expiration time for the JWT, takes precedence over iatConnect using a WebSocket to ws://worker/connect with the one of the following query parameter options:
claim: The authentication claim created previously (takes precedence over jwt)jwt: JWT created previouslyYou can load-balance a cluster of workers, as long as the load-balancer supports WebSockets.
The following errors can happen during connection:
ERROR_GETTING_CLAIM: If an error occurred during fetching the claim (Redis error)MISSING_CLAIM: If the claim ID doesn't exists. This can also happen if the claim has expiredINVALID_EXPIRATION: If the claim has an invalid expiration (shouldn't happen unless Redis error)EXPIRED_CLAIM: If the claim has expired, but Redis hasn't expired the claim on it's ownINVALID_JWT: If the JWT is malformed (bad JSON/JWT format) or is not signed with proper keyMISSING_AUTHENTICATION: If no authentication is provided (no claim/JWT)Sending a message is done through the POST /send API endpoint.
Query param options:
user (string): The user ID to targetsession (optional, string, when user is set): The specific session(s) to target from the userid (string UUID): The specific internal connection IDchannel (string): The channel to targettype (required, string): Message (body) type. Can be text (UTF-8 text) or binary. This becomes the WebSocket message type.token (required, string): Authorization token for API set in config. Can also be a Authorization Bearer tokenThe body of the request is used as the message. This can be text/binary, and the Content-Type header is not used internally (only type is used).
Send a JSON message to a user (1)
POST /send?token=abcxyz&user=1&type=text
{"message":"Hello world!","from":"Charles"}
Send a text value to a user (1) with a session (a)
POST /send?user=1&session=a&type=text
Authorization: Bearer abcxyz
<Cretezy> Hey!
Send a binary value to all clients subscribed in a channel:
POST /send?channel=group-1&type=binary
Authorization: Bearer abcxyz
# Binary...
The following errors can happen during sending a message:
INVALID_AUTHORIZATION: Invalid authentication (token). See errors section under usageERROR_GETTING_CONNECTION: If could not fetch connection(s) (Redis error)ERROR_GETTING_USER: If user is set and could not fetch user (Redis error)ERROR_GETTING_CHANNEL: If channel is set and could not fetch channel (Redis error)MISSING_TARGET: If target is not providerINVALID_MESSAGE_TYPE: If the type is invalidERROR_READING_MESSAGE: If an error occurred during reading the request bodyERROR_MARSHALLING_MESSAGE: If an error occurred during preparing to send the message to the workers (shouldn't happen)You can disconnect a client by user (and optionally session) ID.
This is useful when logging out a user, to make sure it also disconnects any connections. Make sure to include a session in your claim/JWT to be able to disconnect only some of a user's connections.
The API endpoint is POST /disconnect, with the following query params:
user (string): The user ID to targetsession (optional, string, when user i$ claude mcp add dSock \
-- python -m otcore.mcp_server <graph>