Official Go server SDK for Sockudo — a fast, self-hosted WebSocket server with full Pusher HTTP API compatibility.
- Go 1.21 or greater
- Installation
- Getting Started
- Configuration
- Triggering Events
- Idempotent Publishing
- Authenticating Users
- Authorizing Channels
- Application State
- Webhook Validation
- End-to-End Encryption
- Google App Engine
- Testing
- Pusher SDK Compatibility
go get github.com/sockudo/sockudo-http-go/v5package main
import (
sockudo "github.com/sockudo/sockudo-http-go/v5"
)
func main() {
client := sockudo.Client{
AppID: "your-app-id",
Key: "your-app-key",
Secret: "your-app-secret",
Host: "127.0.0.1",
Port: "6001",
}
data := map[string]string{"message": "hello world"}
err := client.Trigger("my-channel", "my-event", data)
if err != nil {
panic(err)
}
}client := sockudo.Client{
AppID: "your-app-id",
Key: "your-app-key",
Secret: "your-app-secret",
Host: "127.0.0.1",
Port: "6001",
Secure: false,
}client := sockudo.ClientFromURL("http://key:secret@127.0.0.1:6001/apps/app-id")// Reads SOCKUDO_URL, expected format: http://key:secret@host:port/apps/app_id
client := sockudo.ClientFromEnv("SOCKUDO_URL")client.Secure = trueimport "net/http"
import "time"
client.HTTPClient = &http.Client{Timeout: time.Second * 5}If no HTTP client is set, a default is created with a 5-second timeout.
data := map[string]string{"hello": "world"}
err := client.Trigger("my-channel", "my-event", data)socketID := "1234.12"
params := sockudo.TriggerParams{SocketID: &socketID}
channels, err := client.TriggerWithParams("my-channel", "my-event", data, params)err := client.TriggerMulti([]string{"channel-1", "channel-2"}, "my-event", data)batch := []sockudo.Event{
{Channel: "channel-1", Name: "event-1", Data: "hello"},
{Channel: "channel-2", Name: "event-2", Data: "world"},
}
response, err := client.TriggerBatch(batch)The Event struct:
type Event struct {
Channel string
Name string
Data interface{}
SocketID *string
}Pass an IdempotencyKey in TriggerParams to safely retry publishes without causing duplicate deliveries:
key := "order-shipped-order-789"
params := sockudo.TriggerParams{IdempotencyKey: &key}
err := client.TriggerWithParams("my-channel", "my-event", data, params)The server deduplicates publishes with the same key within the configured window.
Use AuthenticateUser to generate an authentication response for user connections. The userData map must contain at least an "id" key.
func sockudoUserAuth(res http.ResponseWriter, req *http.Request) {
params, _ := io.ReadAll(req.Body)
userData := map[string]interface{}{
"id": "user-123",
"name": "Jane Doe",
}
response, err := client.AuthenticateUser(params, userData)
if err != nil {
http.Error(res, "unauthorized", http.StatusUnauthorized)
return
}
fmt.Fprint(res, string(response))
}
func main() {
http.HandleFunc("/sockudo/user-auth", sockudoUserAuth)
http.ListenAndServe(":5000", nil)
}func sockudoAuth(res http.ResponseWriter, req *http.Request) {
params, _ := io.ReadAll(req.Body)
response, err := client.AuthorizePrivateChannel(params)
if err != nil {
http.Error(res, "unauthorized", http.StatusUnauthorized)
return
}
fmt.Fprint(res, string(response))
}
func main() {
http.HandleFunc("/sockudo/auth", sockudoAuth)
http.ListenAndServe(":5000", nil)
}params, _ := io.ReadAll(req.Body)
member := sockudo.MemberData{
UserID: "user-123",
UserInfo: map[string]string{
"name": "Jane Doe",
},
}
response, err := client.AuthorizePresenceChannel(params, member)
if err != nil {
panic(err)
}
fmt.Fprint(res, string(response))The MemberData struct:
type MemberData struct {
UserID string
UserInfo map[string]string
}prefix := "presence-"
attribute := "user_count"
params := sockudo.ChannelsParams{FilterByPrefix: &prefix, Info: &attribute}
channels, err := client.Channels(params)
// channels => &{Channels:map[presence-room:{UserCount:4}]}attribute := "user_count,subscription_count"
params := sockudo.ChannelParams{Info: &attribute}
channel, err := client.Channel("presence-room", params)
// channel => &{Name:presence-room Occupied:true UserCount:42 SubscriptionCount:42}users, err := client.GetChannelUsers("presence-room")
// users => &{List:[{ID:13} {ID:90}]}func sockudoWebhook(res http.ResponseWriter, req *http.Request) {
body, _ := io.ReadAll(req.Body)
webhook, err := client.Webhook(req.Header, body)
if err != nil {
fmt.Println("Invalid webhook:", err)
http.Error(res, "invalid", http.StatusUnauthorized)
return
}
for _, event := range webhook.Events {
fmt.Printf("event: %s, channel: %s\n", event.Name, event.Channel)
}
res.WriteHeader(http.StatusOK)
}The Webhook struct:
type Webhook struct {
TimeMs int
Events []WebhookEvent
}
type WebhookEvent struct {
Name string
Channel string
Event string
Data string
SocketID string
}Sockudo supports end-to-end encryption for private-encrypted- prefixed channels. Only your server and connected clients can read the messages.
-
Generate a 32-byte master key:
openssl rand -base64 32
-
Pass it when constructing the client:
client := sockudo.Client{ AppID: "your-app-id", Key: "your-app-key", Secret: "your-app-secret", Host: "127.0.0.1", Port: "6001", EncryptionMasterKeyBase64: "<output from command above>", }
-
Use channels prefixed with
private-encrypted-.
This library is compatible with Google App Engine's urlfetch library. Pass the urlfetch.Client as the HTTP client:
package main
import (
"appengine"
"appengine/urlfetch"
"fmt"
"net/http"
sockudo "github.com/sockudo/sockudo-http-go/v5"
)
func init() {
http.HandleFunc("/", handler)
}
func handler(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
client := sockudo.Client{
AppID: "your-app-id",
Key: "your-app-key",
Secret: "your-app-secret",
Host: "127.0.0.1",
Port: "6001",
HTTPClient: urlfetch.Client(c),
}
client.Trigger("my-channel", "my-event", map[string]string{"message": "hello"})
fmt.Fprint(w, "OK")
}go test ./...limit := 50
direction := "newest_first"
page, err := client.ChannelHistory("my-channel", sockudo.HistoryParams{
Limit: &limit,
Direction: &direction,
})
cursor := "opaque-cursor-from-previous-page"
nextPage, err := client.ChannelHistory("my-channel", sockudo.HistoryParams{
Cursor: &cursor,
})Sockudo implements the full Pusher HTTP API. If you prefer to use the official github.com/pusher/pusher-http-go/v5 package or are migrating from Pusher, point it at your Sockudo instance:
import pusher "github.com/pusher/pusher-http-go/v5"
client := pusher.Client{
AppID: "your-app-id",
Key: "your-app-key",
Secret: "your-app-secret",
Host: "127.0.0.1",
Port: "6001",
}All standard Pusher SDK calls work against a self-hosted Sockudo server without modification.
MIT