Sockudo
Server SDKs

Ruby

Use the Ruby server SDK for Rails and Rack apps, including push notification workflows.

Install

gem "sockudo", "~> 2.0"
bundle install

For one-off scripts, install the gem directly:

gem install sockudo -v "~> 2.0"

Configure

require "sockudo"

sockudo = Sockudo::Client.new(
  app_id: "app-id",
  key: "app-key",
  secret: "app-secret",
  host: "127.0.0.1",
  port: 6001,
  use_tls: false
)

Create one client in an initializer and reuse it. use_tls defaults to true; a custom port takes precedence. You can also load SOCKUDO_URL=http://KEY:SECRET@HOST:PORT/apps/APP_ID with Sockudo::Client.from_env, but treat the full URL as a secret and never log it.

For applications behind an outbound proxy, configure Sockudo.http_proxy. Keep the SDK request timeout below the Rails request or job deadline.

Publish

sockudo.trigger(
  "orders",
  "order.created",
  { id: "ord_123" },
  { idempotency_key: "order-created-ord_123" }
)

sockudo.trigger_batch([
  { channel: "orders", name: "order.created", data: { id: "ord_124" } },
  { channel: "orders", name: "order.paid", data: { id: "ord_124" } },
])

Publish the same event to several channels with an array and exclude an originating connection when it already applied the update:

sockudo.trigger(
  ["tenant-42:orders", "user-7:orders"],
  "order.updated",
  { id: "ord_123", status: "paid" },
  {
    socket_id: "123.456",
    idempotency_key: "order-paid:ord_123:v3"
  }
)

Multi-channel and batch calls accept up to 10 entries. Use a stable idempotency key for every publish that Active Job or another worker may retry.

Auth

private_auth = sockudo.authenticate("private-orders", params[:socket_id])

presence_auth = sockudo.authenticate(
  "presence-lobby",
  params[:socket_id],
  user_id: "user-42",
  user_info: { name: "Ada" }
)

Signing is the last step of authorization. Authenticate the application session, validate the exact requested channel, and derive user_id from trusted server-side state before calling authenticate.

Use authenticate_user for user-targeted events. When access changes, terminate_user_connections disconnects a user immediately and force_reconnect_user closes sockets with a reconnect instruction.

Webhooks

webhook = sockudo.webhook(request)

if webhook.valid?
  webhook.events.each { |event| handle_event(event) }
  head :ok
else
  head :unauthorized
end

Build the webhook from the original Rack::Request so validation sees the raw body and headers. Validate before processing, enqueue the events, and return quickly. Do not log the request body or signature.

State and history

channels = sockudo.channels(filter_by_prefix: "presence-")
info = sockudo.channel_info("presence-lobby", info: "user_count")
users = sockudo.channel_users("presence-lobby")

page = sockudo.channel_history(
  "orders",
  limit: 50,
  direction: "newest_first"
)
next_page = sockudo.channel_history("orders", cursor: page[:next_cursor])

presence_page = sockudo.channel_presence_history(
  "presence-lobby",
  limit: 50
)
snapshot = sockudo.channel_presence_snapshot(
  "presence-lobby",
  at_serial: 4
)

State is an operational snapshot. Keep history pages bounded and pass opaque cursors back unchanged.

Async requests

trigger_async and get_async use EventMachine when its reactor is active and otherwise return a threaded HTTPClient::Connection immediately:

sockudo.trigger_async(
  "orders",
  "order.created",
  { id: "ord_123" },
  { idempotency_key: "order-created:ord_123" }
).callback do
  Rails.logger.info("sockudo publish accepted")
end.errback do |error|
  retry_publish_later(error)
end

Use background jobs for work that must outlive a web request; an async method alone is not a durable queue.

Push notifications

sockudo.activate_device(
  deviceId: "ios-device-1",
  clientId: "user-42",
  platform: "apns",
  providerToken: "provider-token"
)

sockudo.upsert_channel_push_subscription(
  deviceId: "ios-device-1",
  clientId: "user-42",
  channel: "orders"
)

accepted = sockudo.publish_push(
  recipients: [{ type: "channel", channel: "orders" }],
  payload: {
    title: "Order updated",
    body: "Order ord_123 is packed"
  },
  idempotency_key: "push-order-ord_123-packed"
)

status = sockudo.get_publish_status(accepted[:publish_id])

Use Rails jobs for push fanout workflows that also touch your product database.

Errors, logging, and retries

All errors inherit from Sockudo::Error:

begin
  sockudo.trigger(
    "orders",
    "order.created",
    payload,
    idempotency_key: operation_id
  )
rescue Sockudo::AuthenticationError
  # Fix credentials; do not retry unchanged.
rescue Sockudo::HTTPError => error
  retry_publish_later(error)
rescue Sockudo::Error => error
  report_sdk_error(error)
end

Set Sockudo.logger = Rails.logger to use the application logger, but redact credentials, signed URLs, provider tokens, and event content. Retry only transient network failures, 429, and suitable 5xx responses with bounded backoff and the original idempotency key.

  • Reuse the client from a Rails initializer.
  • Keep request deadlines shorter than the web or job timeout.
  • Authorize and rate-limit auth/history endpoints.
  • Use jobs for fanout and push workflows.
  • Test duplicate delivery, credential rotation, webhook retries, and worker shutdown.

On this page