{"id":23134913,"url":"https://github.com/4players/odin-gateway","last_synced_at":"2026-04-26T23:31:46.365Z","repository":{"id":113887205,"uuid":"573908401","full_name":"4Players/odin-gateway","owner":"4Players","description":"Gateway to regulate access by spreading clients over available ODIN servers based on current and future usage","archived":false,"fork":false,"pushed_at":"2025-10-29T15:52:46.000Z","size":98,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-29T17:43:26.318Z","etag":null,"topics":["deno","gateway","infrastructure","jwt","licensing","odin","rpc","voip"],"latest_commit_sha":null,"homepage":"https://www.4players.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/4Players.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-12-03T20:13:30.000Z","updated_at":"2025-10-29T15:52:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"2857143a-a284-4f5b-981f-463d499dcddd","html_url":"https://github.com/4Players/odin-gateway","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/4Players/odin-gateway","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4Players%2Fodin-gateway","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4Players%2Fodin-gateway/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4Players%2Fodin-gateway/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4Players%2Fodin-gateway/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/4Players","download_url":"https://codeload.github.com/4Players/odin-gateway/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4Players%2Fodin-gateway/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32317163,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"ssl_error","status_checked_at":"2026-04-26T23:26:25.802Z","response_time":129,"last_error":"SSL_read: 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":["deno","gateway","infrastructure","jwt","licensing","odin","rpc","voip"],"created_at":"2024-12-17T12:13:39.592Z","updated_at":"2026-04-26T23:31:46.357Z","avatar_url":"https://github.com/4Players.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ODIN Gateway\n\n![v0.18.0](https://img.shields.io/badge/version-0.18.0-blue?style=for-the-badge)\n\nThis is a simple gateway server written in Deno. It regulates access by\nspreading ODIN Voice clients over available ODIN Voice servers based on current and future\nusage. It also performs basic checks on customer license conditions. \n\n[ODIN Voice](https://odin.4players.io/voice-chat/) is a full-service voice chat solution by [4Players GmbH](https://www.4players.io/company/about_us/). Check out our [full online documentation](https://docs.4players.io/voice/)!\n\n```mermaid\nsequenceDiagram\n\nparticipant Client\nparticipant Gateway\nparticipant Server\n\nServer -\u003e\u003e Gateway: sfu.GridConfig RPC\nactivate Gateway\nGateway --\u003e\u003e Server: Receive recommended settings\ndeactivate Gateway\n\nloop every n seconds\nServer -\u003e\u003e Gateway: sfu.Report RPC\nactivate Gateway\nGateway --\u003e\u003e Server: Receive enqueued tasks\ndeactivate Gateway\nend\n\nautonumber 1\n\nClient -\u003e\u003e Gateway: Connect RPC\nactivate Gateway\nGateway --\u003e\u003e Client: Receive signed room token \ndeactivate Gateway\nClient -\u003e\u003e Server: Join Room\n```\n\n## Setup Development Environment\n\nThe following prerequisites are necessary to start development:\n\n- [Deno](https://deno.land/#installation)\n\n## How to Start\n\n```shell\n# start a HTTP server on port 7000\ndeno task run\n\n# start a HTTPS server on port 7000\ndeno task run --ssl\n```\n\n**Note:** If you don't want to install Deno on the target system, you can also\ncompile the scripts into a self-containing executable. Click\n[here](https://deno.land/manual/tools/compiler) for details.\n\n## Configuration\n\nThe gateway is configured using a TypeScript configuration file\n([config.ts](https://github.com/4Players/odin-gateway/blob/main/config.ts)). In\naddition, the following optional command-line arguments are available to enforce\nspecific options:\n\n| Option          | Description                      |\n| :-------------- | :------------------------------- |\n| `--host`        | The IP address to listen on      |\n| `--port`        | The TCP port number to listen on |\n| `--ssl`         | Enable HTTPS                     |\n| `--certificate` | The certificate file to use      |\n| `--privatekey`  | The private key file to use      |\n\n**Note:** For local testing, using\n[mkcert](https://github.com/FiloSottile/mkcert) is recommended.\n\n### Importing Customer Keys\n\nInstead of providing a static list of customer keys in the gateway configuration\nfile, you can also write your own custom import function, which needs to return\na `Promise\u003cCustomer[]\u003e` value.\n\n```typescript\ncustomerApi: {\n  updateFunction: myFunction, // async function returning a list of customers\n  updateInterval: 300,        // interval in seconds\n},\n```\n\nThis function can be used to either fetch your customer keys from a RESTful API\nor a reload file on disk. Here's an example:\n\n```typescript\nasync function myFunction(): Promise\u003cCustomer[]\u003e {\n  try {\n    const response = await fetch(\"https://domain.tld/api/customer-keys\");\n    if (response.status !== 200) {\n      throw \"something went wrong\";\n    }\n    return await response.json();\n  } catch (_e) {\n    // handle error\n  }\n  return [];\n}\n```\n\n## Public API\n\n### Authentication\n\nAuthentication for the public API is done using a `Bearer` token signed with an\nODIN access key. When specified, the\n[audience](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3) claim (aud) in\nthe token must be set to `gateway`. The payload of a decoded JWT looks like\nthis:\n\n```json\n{\n  \"rid\": \"foo\",\n  \"uid\": \"bar\",\n  \"aud\": \"gateway\",\n  \"sub\": \"connect\",\n  \"exp\": 1669852860,\n  \"nbf\": 1669852800\n}\n```\n\nFor details on generating tokens, please refer to\n[@4players/odin-tokens](https://github.com/4Players/odin-tokens).\n\n### RPC Methods\n\nTo use any of the following methods, the JWT used to authorize must have its\nname listed in the\n[subject](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2) claim (sub).\n\n#### Connect\n\nAuthorizes ODIN clients to access the room specified in the `Bearer` token and\nresponds with a new JWT signed with the gateway master key, which can be used to\njoin a room on an attached ODIN server.\n\n```shell\ncurl --request POST \\\n  --url http://localhost:7000/ \\\n  --header 'Authorization: Bearer ${JWT}' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n    \"jsonrpc\": \"2.0\",\n    \"method\": \"Connect\",\n    \"params\": {},\n    \"id\": 1\n  }'\n```\n\n#### RoomClose\n\nCloses the specified room (if available) and prevents it from being joined again\nuntil `ban_time` is expired.\n\n```shell\ncurl --request POST \\\n  --url http://localhost:7000/ \\\n  --header 'Authorization: Bearer ${JWT}' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n    \"jsonrpc\": \"2.0\",\n    \"method\": \"RoomClose\",\n    \"params\": {\n      \"room_id\": \"${ROOM_ID}\",\n      \"ban_time\": ${OPTIONAL_TIME_IN_SECONDS},\n    },\n    \"id\": 1\n  }'\n```\n\n#### RoomUpdate\n\nUpdates the global user data of the specified room using the given `user_data`\nbyte array.\n\n```shell\ncurl --request POST \\\n  --url https://localhost:7000/ \\\n  --header 'Authorization: Bearer ${JWT}' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n    \"jsonrpc\": \"2.0\",\n    \"method\": \"RoomUpdate\",\n    \"params\": {\n      \"room_id\": \"${ROOM_ID}\",\n      \"user_data\": ${ARRAY_OF_BYTES}\n    },\n    \"id\": 1\n  }'\n```\n\n#### RoomBanClient\n\nKicks all clients matching the specified `user_id` from the specified room and\nprevents them from joining again until `ban_time` is expired.\n\n```shell\ncurl --request POST \\\n  --url http://localhost:7000/ \\\n  --header 'Authorization: Bearer ${JWT}' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n    \"jsonrpc\": \"2.0\",\n    \"method\": \"RoomBanClient\",\n    \"params\": {\n      \"room_id\": \"${ROOM_ID}\",\n      \"user_id\": \"${PEER_USER_ID}\",\n      \"ban_time\": ${OPTIONAL_TIME_IN_SECONDS},\n    },\n    \"id\": 1\n  }'\n```\n\n#### RoomSendMessage\n\nSends arbitrary data to all clients matching the specified `user_id` in the\nspecified room.\n\n```shell\ncurl --request POST \\\n  --url https://localhost:7000/ \\\n  --header 'Authorization: Bearer ${JWT}' \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n    \"jsonrpc\": \"2.0\",\n    \"method\": \"RoomSendMessage\",\n    \"params\": {\n      \"room_id\": \"${ROOM_ID}\",\n      \"user_id\": \"${PEER_USER_ID}\",\n      \"message\": ${ARRAY_OF_BYTES}\n    },\n    \"id\": 1\n  }'\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4players%2Fodin-gateway","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F4players%2Fodin-gateway","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4players%2Fodin-gateway/lists"}