{"id":36799707,"url":"https://github.com/kamalshkeir/ksbus","last_synced_at":"2026-01-12T13:33:03.653Z","repository":{"id":62971647,"uuid":"559679509","full_name":"kamalshkeir/ksbus","owner":"kamalshkeir","description":"KSBus is a zero-configuration event bus written in Go, designed to facilitate real-time data sharing and synchronization between Go servers, JavaScript clients, and Python. It's particularly useful for building applications that require real-time communication, such as chat applications or live updates.","archived":false,"fork":false,"pushed_at":"2025-01-06T19:03:01.000Z","size":200,"stargazers_count":39,"open_issues_count":1,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-09-18T21:49:53.948Z","etag":null,"topics":["bus","channels","client","client-client","eventbus","generics","golang","pure-javascript","realtime","server-client","server-server","sync","synchronization","websockets","zero-configuration"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kamalshkeir.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-10-30T20:59:22.000Z","updated_at":"2025-04-11T05:25:48.000Z","dependencies_parsed_at":"2024-04-06T03:24:04.845Z","dependency_job_id":"e0ab2f7e-20fc-484d-a2f1-51515953b7f4","html_url":"https://github.com/kamalshkeir/ksbus","commit_stats":{"total_commits":100,"total_committers":1,"mean_commits":100.0,"dds":0.0,"last_synced_commit":"f199c1e6826a72096dd91beeafc49ec714b0e35a"},"previous_names":["kamalshkeir/kbus"],"tags_count":90,"template":false,"template_full_name":null,"purl":"pkg:github/kamalshkeir/ksbus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamalshkeir%2Fksbus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamalshkeir%2Fksbus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamalshkeir%2Fksbus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamalshkeir%2Fksbus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kamalshkeir","download_url":"https://codeload.github.com/kamalshkeir/ksbus/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamalshkeir%2Fksbus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28339175,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T12:22:26.515Z","status":"ssl_error","status_checked_at":"2026-01-12T12:22:10.856Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bus","channels","client","client-client","eventbus","generics","golang","pure-javascript","realtime","server-client","server-server","sync","synchronization","websockets","zero-configuration"],"created_at":"2026-01-12T13:33:03.517Z","updated_at":"2026-01-12T13:33:03.606Z","avatar_url":"https://github.com/kamalshkeir.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# KSBus\n\nKSBus is a zero-configuration event bus written in Go, designed to facilitate real-time data sharing and synchronization between Go servers, JavaScript clients, and Python. It's particularly useful for building applications that require real-time communication, such as chat applications or live updates.\n\n## Features\n\n- **Zero Configuration**: Easy to set up and use without the need for complex configurations.\n- **Cross-Language Support**: Supports communication between Go servers and clients, JavaScript clients, and Python clients.\n- **Real-Time Data Sharing**: Enables real-time data synchronization and broadcasting.\n- **Auto TLS**: Automatically generates and renews Let's Encrypt certificates for secure communication, simplifying the setup of HTTPS servers.\n\n## Installation\n\nTo install KSBus, use the following command:\n\n```sh\ngo get github.com/kamalshkeir/ksbus@v1.4.6\n```\n\n## Usage\n\n### Server Setup\n\nTo set up a KSBus server, you can use the `NewServer` function. Here's a basic example:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/kamalshkeir/lg\"\n\t\"github.com/kamalshkeir/ksbus\"\n\t\"github.com/kamalshkeir/ksmux\"\n\t\"github.com/kamalshkeir/ksmux/ws\"\n)\n\nfunc main() {\n\tbus := ksbus.NewServer(ksbus.ServerOpts{\n\t\tAddress: \":9313\",\n\t\t// OnDataWS: func(data map[string]any, conn *ws.Conn, originalRequest *http.Request) error {\n\t\t// \tfmt.Println(\"srv OnDataWS:\", data)\n\t\t// \treturn nil\n\t\t// },\n\t})\n    // bus.Id = \"server-9313\"\n    fmt.Println(\"connected as\",bus.Id)\n\t\n    app := bus.App // get router\n\tapp.LocalStatics(\"JS\", \"/js\") //server static dir\n\tlg.CheckError(app.LocalTemplates(\"examples/client-js\")) // handle templates dir\n\n\tbus.OnDataWs(func(data map[string]any, conn *ws.Conn, originalRequest *http.Request) error {\n\t\tfmt.Println(\"srv OnDataWS:\", data)\n\t\treturn nil // if error, it will be returned to the client\n\t})\n\n\tbus.OnId(func(data map[string]any) { //recv on bus.Id from PublishToID\n\t\tfmt.Println(\"srv OnId:\", data)\n\t})\n\n\tunsub := bus.Subscribe(\"server1\", func(data map[string]any, unsub ksbus.Unsub) {\n\t\tfmt.Println(data)\n\t\t// unsub.Unsubscribe()\n\t})\n\n    // unsub.Unsubscribe()\n\n\tapp.Get(\"/\", func(c *ksmux.Context) {\n\t\tc.Html(\"index.html\", nil)\n\t})\n\n\tapp.Get(\"/pp\", func(c *ksmux.Context) {\n\t\tbus.Bus.Publish(\"server1\", map[string]any{\n\t\t\t\"data\": \"hello from INTERNAL\",\n\t\t})\n\t\tc.Text(\"ok\")\n\t})\n\n\tfmt.Println(\"server1 connected as\", bus.ID)\n\tbus.Run()\n}\n```\n\n\n\n### Client Setup (JavaScript)\n\nFor JavaScript clients, you can use the provided `Bus.js` file. Here's a basic example of how to use it:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\n\u003chead\u003e\n    \u003cmeta charset=\"UTF-8\"\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\n    \u003ctitle\u003eIndex\u003c/title\u003e\n\u003c/head\u003e\n\n\u003cbody\u003e\n    \u003ch1\u003eIndex\u003c/h1\u003e\n    \u003cinput type=\"text\" id=\"inpp\"\u003e\n    \u003cbutton type=\"button\" id=\"btn\"\u003eSend\u003c/button\u003e\n    \u003cscript src=\"/js/Bus.js\"\u003e\u003c/script\u003e\n    \u003cscript\u003e\n        let bus = new Bus({ Id: \"browser\" });\n        bus.OnId = (d) =\u003e {\n            console.log(\"recv OnId\", d)\n        }\n        bus.OnDataWS = (d, wsConn) =\u003e {\n            console.log(\"recv OnData\", d)\n        }\n        bus.OnOpen = () =\u003e {\n            console.log(\"connected\", bus.Id)\n\n            unsub = bus.Subscribe(\"index.html\",(data,unsub) =\u003e { // receive once, then unsub\n                console.log(data);\n                unsub.Unsubscribe();\n            })\n\n            // or you can unsub from the outside using returned unsub from bus.Subscribe\n\n            btn.addEventListener(\"click\", (e) =\u003e {\n                e.preventDefault();\n                // publish and wait for recv\n                bus.PublishToIDWaitRecv(inpp.value, { \"cxwcwxcc\": \"hi from browser\" }, (data) =\u003e {\n                    console.log(\"onRecv:\", data)\n                }, (eventID, id) =\u003e {\n                    console.log(`${id} did not receive ${eventID}`);\n                })\n            })\n        }\n    \u003c/script\u003e\n\u003c/body\u003e\n\n\u003c/html\u003e\n```\n\n\n### Client Setup (Go)\n\nFor Go clients, you can use the `NewClient` function. Here's a basic example:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/kamalshkeir/lg\"\n\t\"github.com/kamalshkeir/ksbus\"\n\t\"github.com/kamalshkeir/ksmux/ws\"\n)\n\nfunc main() {\n\tclient, err := ksbus.NewClient(ksbus.ClientConnectOptions{\n\t\tId:      \"go-client\",\n\t\tAddress: \"localhost:9313\",\n        Secure: false,\n\t\tOnDataWs: func(data map[string]any, conn *ws.Conn) error {\n\t\t\tfmt.Println(\"ON OnDataWs:\", data)\n\t\t\treturn nil\n\t\t},\n\t\tOnId: func(data map[string]any, subs ksbus.Unsub) {\n\t\t\tfmt.Println(\"ON OnId:\", data)\n\t\t},\n\t})\n\tif lg.CheckError(err) {\n\t\treturn\n\t}\n\n\tfmt.Println(\"CLIENT connected as\", client.Id)\n\n\tclient.Subscribe(\"go-client\", func(data map[string]any, sub ksbus.Unsub) {\n\t\tfmt.Println(\"ON sub go-client:\", data)\n\t})\n\n\tclient.Publish(\"server1\", map[string]any{\n\t\t\"data\": \"hello from go client\",\n\t})\n\n\tclient.PublishToIDWaitRecv(\"browser\", map[string]any{\n\t\t\"data\": \"hello from go client\",\n\t}, func(data map[string]any) {\n\t\tfmt.Println(\"onRecv:\", data)\n\t}, func(eventId, id string) {\n\t\tfmt.Println(\"not received:\", eventId, id)\n\t})\n\tclient.Run()\n}\n```\n\n\n### Client Setup (Python)\n\nFor Python clients, you can install pkg ksbus\n\n```sh\npip install ksbus==1.3.0\n```\n\n\n```py\nfrom ksbus import Bus\n\n\ndef OnId(data):\n    print(\"OnId:\",data)\n\ndef OnOpen(bus):\n    print(\"connected as \",bus.Id)\n    bus.PublishToIDWaitRecv(\"browser\",{\n        \"data\":\"hi from pure python\"\n    },lambda data:print(\"OnRecv:\",data),lambda event_id:print(\"OnFail:\",event_id))\n\nif __name__ == '__main__':\n    Bus({\n        'Id': 'py',\n        'Address': 'localhost:9313',\n        'OnId': OnId,\n        'OnOpen':OnOpen},\n        block=True)\n```\n\n\n##### you can handle authentication or access to certain topics using to [Global Handlers](#global-handlers)\n\n\n# API\n\n### Internal Bus (No websockets, use channels to handle topic communications)\n\n```go\nfunc New() *Bus\n\nfunc (b *Bus) Subscribe(topic string, fn func(data map[string]any, unsub Unsub), onData ...func(data map[string]any)) Unsub\n\nfunc (b *Bus) Unsubscribe(topic string)\n\nfunc (b *Bus) Publish(topic string, data map[string]any)\n\nfunc (b *Bus) PublishToID(id string, data map[string]any)\n\nfunc (b *Bus) PublishWaitRecv(topic string, data map[string]any, onRecv func(data map[string]any), onExpire func(eventId string, topic string)) error\n\nfunc (b *Bus) PublishToIDWaitRecv(id string, data map[string]any, onRecv func(data map[string]any), onExpire func(eventId string, id string)) error\n\nfunc (b *Bus) RemoveTopic(topic string)\n```\n\n### Server Bus (use the internal bus and a websocket server)\n```go\nfunc NewServer(bus ...*Bus) *Server\n\nfunc (s *Server) OnServerData(fn func(data any, conn *ws.Conn)) // used when receiving data from other ksbus server (not client) using server.PublishToServer(addr string, data map[string]any, secure ...bool) error\n\nfunc (s *Server) OnDataWs(fn func(data map[string]any, conn *ws.Conn, originalRequest *http.Request) error) // triggered after upgrading connection to websockets and each time on recv new message \n\nfunc (s *Server) OnId(fn func(data map[string]any)) // used when server bus receive data on his ID 'server.Id'\n\nfunc (s *Server) WithPprof(path ...string) // enable std library pprof endpoints /debug/pprof/...\n\nfunc (s *Server) WithMetrics(httpHandler http.Handler, path ...string) // take prometheus handler as input and serve /metrics if no path specified\n\nfunc (s *Server) Subscribe(topic string, fn func(data map[string]any, unsub Unsub)) (unsub Unsub)\n\nfunc (s *Server) Unsubscribe(topic string)\n\nfunc (srv *Server) Publish(topic string, data map[string]any)\n\nfunc (s *Server) PublishToID(id string, data map[string]any) // every connection ws (go client, python client, js client and the server), all except the internal bus, each connection should have a unique id that you can send data specificaly to it (not all subscribers on a topic, only a specific connection)\n\nfunc (s *Server) PublishWaitRecv(topic string, data map[string]any, onRecv func(data map[string]any), onExpire func(eventId string, topic string))\n\nfunc (s *Server) PublishToIDWaitRecv(id string, data map[string]any, onRecv func(data map[string]any), onExpire func(eventId string, toID string))\n\nfunc (s *Server) RemoveTopic(topic string)\n\nfunc (s *Server) PublishToServer(addr string, data map[string]any, secure ...bool) error // send to another ksbus server\n\n\nfunc (s *Server) Run() // Run without TLS\nfunc (s *Server) RunTLS(cert string, certKey string) // RunTLS with TLS from cert files\nfunc (s *Server) RunAutoTLS(subDomains ...string) // RunAutoTLS generate letsencrypt certificates for server.Address and subDomains and renew them automaticaly before expire, so you only need to provide a domainName(server.Address). \n```\n\n### Client Bus GO\n\n```go\ntype ClientConnectOptions struct {\n\tId           string\n\tAddress      string\n\tSecure       bool\n\tPath         string // default ksbus.ServerPath\n\tAutorestart  bool\n\tRestartEvery time.Duration\n\tOnDataWs     func(data map[string]any, conn *ws.Conn) error // before data is distributed on different topics\n\tOnId         func(data map[string]any, unsub Unsub) // used when client bus receive data on his ID 'client.Id'\n}\n\nfunc NewClient(opts ClientConnectOptions) (*Client, error)\n\nfunc (client *Client) Subscribe(topic string, handler func(data map[string]any, unsub Unsub)) Unsub\n\nfunc (client *Client) Unsubscribe(topic string)\n\nfunc (client *Client) Publish(topic string, data map[string]any)\n\nfunc (client *Client) PublishToServer(addr string, data map[string]any, secure ...bool)\n\nfunc (client *Client) PublishToID(id string, data map[string]any)\n\nfunc (client *Client) PublishWaitRecv(topic string, data map[string]any, onRecv func(data map[string]any), onExpire func(eventId string, topic string))\n\nfunc (client *Client) PublishToIDWaitRecv(id string, data map[string]any, onRecv func(data map[string]any), onExpire func(eventId string, id string))\n\nfunc (client *Client) RemoveTopic(topic string)\n\nfunc (client *Client) Close() error\n\nfunc (client *Client) Run()\n\n```\n\n### Client Bus JS\n\n```js\nclass Bus {\n    /**\n     * Bus can be initialized without any param 'let bus = new Bus()'\n     * @param {object} options \"default: {...}\"\n     * @param {string} options.Id \"default: uuid\"\n     * @param {string} options.Address \"default: window.location.host\"\n     * @param {string} options.Path \"default: /ws/bus\"\n     * @param {boolean} options.Secure \"default: false\"\n     * @param {boolean} options.AutoRestart \"default: false\"\n     * @param {number} options.RestartEvery \"default: 10\"\n     */\n\nSubscribe(topic, handler)\nUnsubscribe(topic)\nPublish(topic, data)\nPublishWaitRecv(topic, data, onRecv, onExpire)\nPublishToIDWaitRecv(id, data, onRecv, onExpire)\nPublishToServer(addr, data, secure)\nPublishToID(id, data)\nRemoveTopic(topic)\n```\n\n### Client Bus PY\n\n```py\nclass Bus:\n    def __init__(self, options, block=False):\n        self.Address = options.get('Address', 'localhost')\n        self.Path = options.get('Path', '/ws/bus')\n        self.scheme = 'ws://'\n        if options.get('Secure', False):\n            self.scheme = 'wss://'\n        self.full_address = self.scheme + self.addr + self.path\n        self.conn = None\n        self.topic_handlers = {}\n        self.AutoRestart = options.get('AutoRestart', False)\n        self.RestartEvery = options.get('restartEvery', 5)\n        self.OnOpen = options.get('OnOpen', lambda bus: None)\n        self.OnClose = options.get('OnClose', lambda: None)\n        self.OnDataWs = options.get('OnDataWs', None)\n        self.OnId = options.get('OnId', lambda data: None)\n        self.Id = options.get('Id') or self.makeId(12)\n\ndef Subscribe(self, topic, handler)\ndef Unsubscribe(self, topic)\ndef Publish(self, topic, data)\ndef PublishToID(self, id, data)\ndef RemoveTopic(self, topic)\ndef PublishWaitRecv(self, topic, data, onRecv, onExpire)\ndef PublishToIDWaitRecv(self, id, data, onRecv, onExpire)\ndef PublishToServer(self, addr, data, secure)\n```\n\n## Global Handlers \n```go\nOnUpgradeWS   = func(r *http.Request) bool { return true }\n// also server.OnDataWs can be used for auth\n```\n\n\n## Example Python Client\n```sh\npip install ksbus==1.2.8\n```\n\nWith FastApi example:\n```py\nfrom typing import Union\n\nfrom fastapi import FastAPI\n# ksbus import\nfrom ksbus import Bus\nfrom pydantic import BaseSettings\n\n\nclass Settings(BaseSettings):\n    bus: Bus = None\n\nsettings= Settings()\napp = FastAPI()\n\n\ndef initBus():\n    Bus({\n        'Id': 'py',\n        'Address': 'localhost:9313',\n        'OnId': OnId,\n        'OnOpen':onOpen})\n\ndef onOpen(bus):\n    print(\"connected\")\n    settings.bus=bus\n    print(settings.bus)\n    bus.Publish(\"server\",{\n        \"data\":\"hi i am python\"\n    })\n    bus.Subscribe(\"client\",lambda data,subs : print(data))\n\n\n\n@app.get(\"/\")\nasync def read_root():\n    if settings.bus == None:\n        initBus()\n    return {\"Hello\": \"World\"}\n\n\n@app.get(\"/items/{item_id}\")\nasync def read_item(item_id: int, q: Union[str, None] = None):\n    return {\"item_id\": item_id, \"q\": q}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkamalshkeir%2Fksbus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkamalshkeir%2Fksbus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkamalshkeir%2Fksbus/lists"}