{"id":15678150,"url":"https://github.com/hacksore/github-websocket-api","last_synced_at":"2025-05-07T02:26:31.152Z","repository":{"id":145783936,"uuid":"559240548","full_name":"Hacksore/github-websocket-api","owner":"Hacksore","description":"Reverse engineering the Github websocket API - alive.github.com","archived":false,"fork":false,"pushed_at":"2023-04-19T02:40:49.000Z","size":29,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-25T20:39:11.192Z","etag":null,"topics":["github","github-hacking","github-reverse-engineering","realtime","reverse-engineering"],"latest_commit_sha":null,"homepage":"","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/Hacksore.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}},"created_at":"2022-10-29T13:55:15.000Z","updated_at":"2024-10-31T12:19:03.000Z","dependencies_parsed_at":"2023-07-15T17:15:42.783Z","dependency_job_id":null,"html_url":"https://github.com/Hacksore/github-websocket-api","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hacksore%2Fgithub-websocket-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hacksore%2Fgithub-websocket-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hacksore%2Fgithub-websocket-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hacksore%2Fgithub-websocket-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hacksore","download_url":"https://codeload.github.com/Hacksore/github-websocket-api/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242884332,"owners_count":20201108,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["github","github-hacking","github-reverse-engineering","realtime","reverse-engineering"],"created_at":"2024-10-03T16:17:43.820Z","updated_at":"2025-03-10T16:31:41.970Z","avatar_url":"https://github.com/Hacksore.png","language":"TypeScript","readme":"# github-websocket-pipelines-api\n\nGithub has an undocumented websocket API hosted on `alive.github.com` for some realtime interactions.\n\n# 🟡 Mildly Blocked\n\nAble to get data if I reuse the payload that is sent from a real client to subscribe. Can't figure out how to craft the packet fully yet.\n\n### Events\n\n- Workflow Run\n- Notification Changed\n- Check Suites\n\n### New intel\n\nIt seems the `session` is in two parts, one a base64 encoded string with the payload seen below.\n\nSchema:\n`\u003cbase64String\u003e--\u003csessionIdentifierFromDOM\u003e`\n\nThis is what the github webpage has as the first part `session` field in the websocket query string.\n\n```json\n{\n  \"v\": \"V3\",\n  \"u\": \"\u003cuserId\u003e\",\n  \"s\": \"\u003cnumber\u003e\",\n  \"c\": \"\u003cnumber\u003e\",\n  \"t\": \"\u003cnumber\u003e\"\n}\n```\n\nanother piece of intel is how you subscribe to events, this is the decoded part of what the client will send as the first part.\n\n```json\n{ \"c\": \"notification-changed:\u003cuserId\u003e\", \"t\": 1667188552 }\n```\n\ncheck suites payload to send as the first part.\n\n```json\n{ \"c\": \"check_suites:\u003cjobId\u003e\", \"t\": 1667192074 }\n```\n\n### Auth\n\nIf use a valid `user_session` cookie you can request github and extract the websocket URL from the page.\n\nThe script is doing this automatically now.\n\n### How to run\n\n1. Create an `.env` file, copy the example. Source both your user id and a user session\n1. run `yarn` to install the deps\n1. run `yarn start` to start the client\n\n# Payloads/Events\n\nI've attempted to give a good description to each of these payloads below.\n\n## ack\n\nThis is seems to be returned not matter what you send to the API.\n\n```json\n{\n  \"e\": \"ack\",\n  \"off\": \"1667055219967-0\",\n  \"health\": true\n}\n```\n\n## subscribe\n\nThis is what the client will send when you visit a github page. It looks to be asking github\n\n\u003e please give me events for `\u003csessionId\u003e`\n\n```json\n{\n  \"subscribe\": {\n    \"\u003csessionId\u003e\": \"\"\n  }\n}\n```\n\n## unsubscribe\n\nThis is what the client will send when you navigating to a new page on github. It looks to be tell github saying\n\n\u003e please unsubscribe me for all previous `\u003csessionId\u003e` event subscriptions\n\n```json\n{\n  \"unsubscribe\": [\"\u003csessionId\u003e\"]\n}\n```\n\n## notification-changed\n\nThis is emitted whenever the indicator needs to update. Anything that will leave items in your notifications inbox will trigger this.\n\n```json\n{\n  \"e\": \"msg\",\n  \"ch\": \"notification-changed:\u003cuserId\u003e\",\n  \"off\": \"1667056418489-0\",\n  \"data\": { \"indicator_mode\": \"none\", \"wait\": 371.57200000000006 }\n}\n```\n\n## workflow_run\n\nThis is emitted whenever a workflow run starts.\n\n```json\n{\n  \"e\": \"msg\",\n  \"ch\": \"workflow_run:\u003crunId\u003e:execution\",\n  \"off\": \"1667059757243-0\",\n  \"data\": {\n    \"timestamp\": \"2022-10-29T16:09:15.000Z\",\n    \"wait\": 372.463,\n    \"reason\": \"Execution created\"\n  }\n}\n```\n\n## check_suites\n\nThis is emitted whenever a workflow job updates and has two states in the `reason` field. It can either be `in_progress` or `completed`.\n\n```json\n{\n  \"e\": \"msg\",\n  \"ch\": \"check_suites:\u003cjobId\u003e\",\n  \"off\": \"1667056504426-0\",\n  \"data\": {\n    \"timestamp\": \"2022-10-29T15:15:04.000Z\",\n    \"wait\": 396.523,\n    \"reason\": \"check_suite #\u003cjobId\u003e updated: \u003cstate\u003e\",\n    \"log_archive\": false\n  }\n}\n```\n\n### Example\n\nSee demo in `client.ts`\n\n### Investigating \n\n- How do I get a `jobId` that is used\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhacksore%2Fgithub-websocket-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhacksore%2Fgithub-websocket-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhacksore%2Fgithub-websocket-api/lists"}