- JavaScript 58.1%
- EJS 25.9%
- CSS 9.3%
- Lua 6.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .vscode | ||
| public | ||
| roblox | ||
| scripts | ||
| src | ||
| test | ||
| views | ||
| .env.example | ||
| .gitignore | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
RoSocket
A Pyramus Studios project. RoSocket gives Roblox game servers WebSockets over plain HTTP.
Roblox game servers can't open WebSockets, so RoSocket opens them for you. The game sends with POST. It receives by long-polling with GET, where the request stays open until something arrives on the socket or the poll times out. Every user is a separate tenant, with their own API tokens, sockets and audit log.
Running
npm install
cp .env.example .env # set SESSION_SECRET for production
npm start # or: npm run dev
npm test
Open http://localhost:3000 and sign up. Create an API token on the dashboard. The full API and Luau reference are at /docs.
Admins
There are two roles: admin and user. The first account to sign up becomes an admin automatically. Existing databases are migrated so that their oldest account becomes the admin.
Admins get a Manage Users page (/admin/users) where they can:
- create users (leave the password blank to generate one, which is shown once)
- edit a user's email and admin flag
- reset passwords (this signs the user out everywhere)
- delete users (type the email to confirm). Deleting a user closes their sockets and removes their tokens and audit log.
The dashboard and audit log also have a Show all users checkbox, available to admins only. It lists every tenant's tokens, open sockets and audit entries. Revoked tokens are hidden on the dashboard by default for everyone; tick Show revoked tokens to include them. Admins who tick both see every user's revoked tokens. Admins can revoke any token or close any socket. These actions are recorded in the owner's audit log with "by": "<admin email>", and all admin.* actions are also logged under the acting admin. Admins can't demote or delete themselves, so at least one admin always exists.
To grant or remove admin from the command line (for example, to recover access):
npm run make-admin -- someone@example.com
npm run make-admin -- someone@example.com --revoke
It uses DATABASE_PATH from the environment or .env.
Roblox
- Turn on Game Settings → Security → Allow HTTP Requests.
- Download
/RoSocket.luafrom your server (roblox/RoSocket.lua). Add it toServerScriptServiceas a ModuleScript namedRoSocket. - Store your token as an experience secret, or use a plain string while testing.
local RoSocket = require(game.ServerScriptService.RoSocket)
RoSocket.configure({
BaseUrl = "https://rosocket.example.com",
Token = game:GetService("HttpService"):GetSecret("rosocket_token"),
})
local socket = RoSocket.connect("wss://echo.websocket.org")
socket:OnMessage(function(data, isBinary) print(data) end)
socket:Send("hello")
Each socket owns two hidden (unparented) BindableEvents:
socket.Inboundfires with one of:("open"),("message", data, isBinary),("error", msg),("close", code, reason).socket.Outbound: call:Fire(data, isBinary?)to send. You can hand this event to other scripts.
How it works
src/bridge.jskeeps the outbound WebSockets open. It turns frames and state changes into sequenced events and answers long-polls. Clients acknowledge events withack=<seq>, and anything not yet acknowledged is sent again. A socket that isn't polled forSOCKET_IDLE_TIMEOUT_SECONDSis closed.src/targets.jsonly allowsws:///wss://targets that resolve to public addresses (SSRF protection). For local development, setALLOW_PRIVATE_TARGETS=true.- The audit log records signup/login, token create/revoke, and socket open/send/close/expire. Each entry includes the token, IP and Roblox Place ID (from the
Roblox-Idheader). Message contents are never stored. - Sessions and data are stored in SQLite (better-sqlite3). Passwords are hashed with bcrypt. API tokens are stored as SHA-256 hashes. Portal forms are protected by CSRF tokens.
Sockets live in memory, so a server restart closes them and RoSocket runs as a single process. See .env.example for all settings.