# orbisRPC Architecture
Discord Rich Presence for jailbroken PS4. No PC at runtime: the daemon
talks to the Discord gateway directly.
## Binaries
| Binary | Built by | Runs as |
|---|---|---|
| `orbisrpc.elf` | `scripts/build.sh` (OpenOrbis) | BinLoader / Payload Guest payload |
| `orbisrpc_sdk.elf` | `scripts/build_sdk.sh` (ps4-payload-sdk) | PKG `assets/daemon.elf`, copied to `/data/payloads/orbisrpc.bin` |
| Installer PKG (`ORPC00001`) | `installer/Makefile` | Setup app: SDL2 UI, token entry, file staging |
| GoldHEN plugin | frozen | not shipped; payload is the supported path |
Both payload builds share `orbisrpc/*.c`. `ORBISRPC_SDK_PAYLOAD`
selects toolchain/libc differences only — detection strategy is
identical (probe-first, see below).
## Detection cascade (ordered, first signal wins)
1. **msgbuf events** (`focus.c`): `AppFocusChanged [...]` in
kern.msgbuf. Event-driven, sees system screens (settings/browser),
resolves multi-app ambiguity. Unreadable buffer = skip.
2. **ShellCoreUtil dlopen** (`detect.c` `scu_init`): `sceShellCoreUtilIsAppLaunched`.
Firmware-independent (libkernel exports dlopen/dlsym). Fail = skip.
3. **sysctl eboot scan** with the verified kinfo table (`fw.c`):
exact offset only on firmwares in the table (currently 9.00);
everywhere else a bounded in-record scan. Never reads past recsz,
never assumes.
4. **Sandbox/save fallbacks**: `/mnt/sandbox/
_000`, freshest
savedata / app dir / `app.pkg` atime.
New title needs 2 consecutive polls (~2s debounce). Same title
returning within 10 min resumes its timer; cross-restart resume seeds
from `session.json` (validated: 9-char id, sane epoch).
## Gateway
Explicit states, logged on transition: `DOWN -> CONNECTING ->
HELLO_WAIT -> IDENTIFYING -> READY`. Fresh IDENTIFY every connect
(no RESUME). Close 4004 (bad token) is a warning + backoff + config
reload — never an exit. Reconnect: exponential backoff with
deterministic jitter, 60s cap for 10 fails, then 10-min cadence.
Presence re-posts after every (re)connect plus a 15-min reconcile.
## No-exit policy
The daemon never exits on transient failure: no token (waits, FTP
edit lands without reboot), network drops, corrupt session file,
detection misses. Exits only on: stop request, lock held by a peer
(stands down), superseded generation, fatal `/data` unwritable.
Crash safety: consecutive unclean boots (never marked healthy after
`HEALTH_STABLE_SECS`) enter safe mode. Single-writer lock
(`/data/orbisRPC/lock`, O_EXCL + pid liveness). `daemon.gen`
generation protocol: installer bumps, older daemons exit cleanly.
## On-disk state (`/data/orbisRPC/`)
| File | What |
|---|---|
| `config.json` | token, prefs, learned titles. Atomic save, `0600` |
| `session.json` | live session for timer resume. Validated on load |
| `status.json` | heartbeat (state/title/version/ts), every 60s |
| `diag.json` | boot census: fw, kinfo-table hit, msgbuf/sandbox/shellcore reachability |
| `daemon.gen` | install generation for supersede |
| `install.log` | installer stage log (errno + sizes per step) |
| `log.txt` | daemon log, mirrored to klog |
| `playtime.log` | append-only ledger, readers total it |
## Installer
Thin layer: preflight assets, `copy_file` with FNV read-back proof
(no stat — sandbox lies), config preserved on reinstall, 3-try IME
token entry with FTP fallback, gen bump, done screen reports live
daemon state from `status.json`. Every step logs to `install.log`;
crash guard turns faults into log lines. UI is custom SDL2
(`installer/ui.c`, same `ui.h` API); font is build-time rasterized
(`scripts/genfont.py` -> `installer/font.h`).
## Compatibility
Verified baseline: **9.00**. All other firmwares: probe-and-degrade
by design, `diag.json` proves per-box signal availability. The
firmware matrix fills in from user-submitted `diag.json` files —
see `SUPPORT.md` and `docs/`.