AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Websocket

skill-jkaninda-okapi-skills-websocket · by jkaninda

A Claude skill from jkaninda/okapi-skills.

No reviews yet
0 installs
25 views
0.0% view→install

Install

$ agentstack add skill-jkaninda-okapi-skills-websocket

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-jkaninda-okapi-skills-websocket)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Websocket? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Okapi WebSocket (okapiws)

WebSocket support ships as a separate, framework-agnostic package. It works with Okapi handlers and with plain net/http.

Installation

The repository is jkaninda/okapi-ws, but the module path is github.com/jkaninda/okapiws — use that in go get and in imports:

go get github.com/jkaninda/okapiws
import okapiws "github.com/jkaninda/okapiws"

Built on github.com/gorilla/websocket.

Upgrading Inside an Okapi Handler

c.Response() implements Hijack, so the upgrader works directly:

func WebSocket(config *okapiws.WSConfig, c *okapi.Context) (*okapiws.WSConnection, error) {
    upgrader := okapiws.NewWSUpgrader(config) // nil config = defaults
    return upgrader.Upgrade(c.Response(), c.Request(), nil)
}

func WebSocketWithHeaders(config *okapiws.WSConfig, headers http.Header, c *okapi.Context) (*okapiws.WSConnection, error) {
    upgrader := okapiws.NewWSUpgrader(config)
    return upgrader.Upgrade(c.Response(), c.Request(), headers)
}

Or upgrade with defaults in one call: okapiws.Default(c.Response(), c.Request(), nil).

Echo Server

func main() {
    app := okapi.Default()

    app.Get("/", func(c *okapi.Context) error {
        return c.OK(okapi.M{"message": "Hello from Okapi!"})
    })

    app.Get("/ws", handleWebSocket)

    if err := app.Start(); err != nil {
        panic(err)
    }
}

func handleWebSocket(c *okapi.Context) error {
    ws, err := WebSocket(nil, c)
    if err != nil {
        return err
    }
    defer func() {
        if err := ws.Close(); err != nil {
            log.Printf("error closing WebSocket: %v", err)
        }
    }()

    ws.OnMessage(func(msg *okapiws.WSMessage) {
        log.Printf("[%d] %s", msg.Type, msg.Data)
        _ = ws.Send(msg.Data) // echo back
    })

    ws.OnError(func(err error) { log.Printf("WebSocket error: %v", err) })
    ws.OnClose(func() { log.Println("client disconnected") })

    ws.Start()            // start the read/write pumps

    <-ws.Context().Done() // block until the connection closes
    return nil
}

With Plain net/http

http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
    upgrader := okapiws.NewWSUpgrader(nil)
    ws, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        http.Error(w, "WebSocket upgrade failed", http.StatusBadRequest)
        return
    }
    defer ws.Close()

    ws.OnMessage(func(msg *okapiws.WSMessage) { _ = ws.Send(msg.Data) })
    ws.OnError(func(err error) { log.Printf("WebSocket error: %v", err) })
    ws.Start()

    <-ws.Context().Done()
})

log.Fatal(http.ListenAndServe(":8080", nil))

Server API

okapiws.NewWSUpgrader(config *WSConfig) *WSUpgrader
upgrader.Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*WSConnection, error)
okapiws.Default(w, r, responseHeader) (*WSConnection, error)  // upgrade with default settings
okapiws.DefaultWSConfig() *WSConfig

*WSConnection:

ws.OnMessage(func(*okapiws.WSMessage))  // message callback
ws.OnError(func(error))                 // error callback
ws.OnClose(func())                      // close callback
ws.Send(data []byte) error              // text frame (non-blocking)
ws.SendText(text string) error
ws.SendBinary(data []byte) error
ws.SendJSON(v any) error
ws.SendEvent(event string, data any) error // {event, data} envelope
ws.Start()                              // start read/write goroutines
ws.Close() error                        // graceful close
ws.IsClosed() bool
ws.Context() context.Context            // cancelled when the connection closes

WSMessage: Type int, Data []byte, Event string, Error error.

WSConfig

| Field | Default | |-------|---------| | ReadBufferSize / WriteBufferSize | 1024 | | HandshakeTimeout | 10s | | CheckOrigin func(*http.Request) bool | func(*http.Request) bool { return true } — allows any origin; override in production | | Subprotocols | none | | EnableCompression | false | | PingInterval | 54s | | PongWait | 60s | | WriteWait | 10s | | MaxMessageSize | 512 KB |

cfg := &okapiws.WSConfig{
    CheckOrigin:    func(r *http.Request) bool { return r.Header.Get("Origin") == "https://app.example.com" },
    PingInterval:   30 * time.Second,
    PongWait:       40 * time.Second,
    MaxMessageSize: 1 << 20,
}
ws, err := okapiws.NewWSUpgrader(cfg).Upgrade(c.Response(), c.Request(), nil)

Ping/pong keep-alive is handled for you from PingInterval / PongWait.

Client

The package also ships a client with optional auto-reconnect:

client := okapiws.NewWSClient("wss://api.example.com/ws",
    okapiws.WithConfig(&okapiws.WSClientConfig{
        AutoReconnect:    true,
        ReconnectInitial: time.Second,
        ReconnectMax:     30 * time.Second,
        MaxRetries:       0, // unlimited
        Headers:          http.Header{"Authorization": {"Bearer " + token}},
    }))

client.OnConnect(func() { log.Println("connected") })       // fires on connect and each reconnect
client.OnMessage(func(msg *okapiws.WSMessage) { log.Printf("%s", msg.Data) })
client.OnError(func(err error) { log.Println(err) })
client.OnClose(func() { log.Println("closed") })

if err := client.Connect(ctx); err != nil {
    return err
}
defer client.Close()

_ = client.SendJSON(map[string]any{"type": "subscribe", "topic": "prices"})
<-client.Context().Done()

WSClientConfig adds TLSConfig, Subprotocols, EnableCompression, and the reconnect knobs to the shared buffer/timeout fields. okapiws.DefaultWSClient() returns the defaults.

Detecting an Upgrade Request

if c.IsWebSocketUpgrade() {
    // Connection: Upgrade + Upgrade: websocket present
}

okapi.LoggerMiddleware skips WebSocket upgrades, so a long-lived connection does not sit in the access log.

Tips

  • Always defer ws.Close() after a successful upgrade.
  • Block on <-ws.Context().Done() to keep the handler alive for the connection's lifetime.
  • Keep per-connection state in your own struct — *okapi.Context is per-request, not per-connection.
  • Set CheckOrigin explicitly — the default accepts every origin, which is fine for development only.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.