Browser-based real-time modding tool for offline single-player games on a jailbroken PS5, served by a payload on the console. - Value search, memory and code views, find what writes to a value - Cheats as patches, held values, injected code, calls and scripts - .cspector cheat files, saved on the PS5 or on your device - Community tables in JSON, SHN and MC4 from three repositories - Game library with names, logos and sizes; start a game from the browser - MCP server for assistants
On-console payload
cheatspecter.elf runs on the PS5 and removes the computer from the picture.
It serves the web app and bridges it to the debugger, so any phone or laptop
on the network just opens http://<PS5 IP>:8744.
Status: builds and passes its tests on a computer; not yet run on a real
PS5. The same source is compiled natively and exercised against the mock
console (test/payload.test.mjs): serving the app, the WebSocket handshake,
large transfers both ways, several connections at once, a missing debugger,
and cross-site rejection. What that cannot show is how it behaves under the
console's own loader and network stack.
Use it
Send cheatspecter-v<version>.elf if you use a payload manager: it reads the
version from the file name and shows it next to the payload. cheatspecter.elf
is the same build under a fixed name, for scripts.
-
Load ps5debug-NG on the console as usual.
-
Send
payload/cheatspecter.elfto your ELF loader, the same way you load any other payload. From this folder:npm run payload:send -- 192.168.1.20 # loader port 9021 by default -
A notification on the TV shows the address to open.
It has to be sent again after each reboot, like any payload. Sending it twice is harmless: the second copy sees the port is taken, says so, and exits.
What it does
| Request | Behaviour |
|---|---|
GET /, /assets/*, /ghost.svg |
The web app, embedded in the ELF and served gzip-compressed. |
GET /api/info |
{"mode":"console","version":"..."}. The app sees this and skips the "find your PS5" screen. |
WS /ws |
Opens a TCP connection to 127.0.0.1:744 and relays binary frames both ways, unchanged. One WebSocket is one TCP connection. |
GET /api/library |
Installed titles: {"titles":[{"id","meta":"json"|"sfo","size","path"}]}. meta is which metadata file the title has (param.json = PS5, param.sfo = PS4). |
GET /api/appmeta/<title id>/<param.json|param.sfo|icon0.png> |
The system's own metadata file for a title, untouched. The web app reads the name from it. |
POST /api/launch/<title id> |
Closes the running game, if any, and starts this one. |
GET /api/tables/<title id> |
Which game versions have a saved cheat file. |
GET, PUT /api/tables/<title id>/<version> |
The user's cheats for that game version, stored as /data/cheatspecter/cheats/<title id>-<version>.cspecter. |
WS /events |
Debugger stop reports. The payload listens on 127.0.0.1:755, where the debugger connects when something attaches, and forwards what arrives. |
It is deliberately thin. All protocol knowledge and file parsing stay in the web app, and ps5debug-NG keeps doing the kernel work.
WebSocket upgrades, launches and file saves are refused when the Origin host
differs from Host, so another web page cannot drive the console through a
visitor's browser. Title IDs and versions are checked against a strict pattern
before they are used in a path.
The library is built from the system's metadata folders
(/system_data/priv/appmeta, /user/appmeta). Sizes are the files found under
/user/app, /user/patch, the same folders on /mnt/ext0 and /mnt/ext1, and
/system_ex/app, where game folders kept elsewhere are mounted. A title with
none of those is listed without a size; one with more than 200,000 files is
marked partial.
Build it
npm run payload:host # for this computer; then: CS_PORT=8745 CS_DEBUGGER_PORT=7440 payload/build/cheatspecter-host
PS5_PAYLOAD_SDK=/opt/ps5-payload-sdk npm run payload # cheatspecter.elf
Both embed whatever npm run build produced, so the ELF needs rebuilding
after any change to the web app. The PS5 build needs
ps5-payload-sdk with clang and lld.
src/main.c is the whole thing: an HTTP server, a WebSocket relay with one
thread per direction, and SHA-1 and Base64 for the handshake.