Files
ps5-payload-manager/DEVELOPMENT.md
T

5.2 KiB

Development

This document describes how to build and deploy the Payload Manager.

How to Build

1. Build the Frontend

You must build the React UI first. This converts the JSX into the dist/index.html file that gets embedded.

make frontend-build

2. Build the SDK Docker Image

If you haven't already, build the SDK environment:

docker build -t ps5-payload-sdk -f Dockerfile.sdk .

3. Build the ELF

Use the Docker container to compile the Payload Manager. It is recommended to run make clean if you updated the frontend.

docker run --rm -v $(pwd):/src -w /src ps5-payload-sdk make clean all

The resulting pldmgr.elf will be created in the root directory.

Automated Deploy

For a quick build & deploy cycle, use the deploy.sh script. It will automatically call /shutdown on the PS5, run the docker build, and send the new ELF via socat.

./deploy.sh [PS5_IP]

(Requires PS5 IP as the first argument).

Manual Deploy

  1. Upload pldmgr.elf to your PS5 (e.g., via port 9021).
  2. The menu will be available at http://[PS5_IP]:8084.

Automated Verification

The HTTP response suite compiles the actual src/http_server.c with stubbed PS5 services and real libmicrohttpd response objects. It injects response-buffer, response-creation, CORS-header, queue, reallocation, and file I/O failures, then sends 10,000 requests through a real loopback HTTP server. It also checks disconnected/chunked requests, concurrent uploads, and server-stop cleanup in thread-per-connection mode. ASan (including leak detection) and UBSan are enabled by default. Test files, storage paths, and binaries are temporary; the suite does not connect to a console or launch payloads.

On a host with Clang, its sanitizer runtime, Python 3, pkg-config, and libmicrohttpd/libcurl development files installed:

make test-host
make test-frontend

The container option builds libmicrohttpd 1.0.1, matching build_deps.sh:

docker build -f tests/Dockerfile -t ps5-payload-manager-tests .
docker run --rm --network=none -v "$(pwd):/src:ro" ps5-payload-manager-tests make test-host

The build downloads test dependencies; the test container itself has external networking disabled. Linux sanitizer support is included in the image. The optional --no-sanitize argument is for hosts without sanitizer support; use the default sanitizer run for verification.

The JSON encoder suite compiles actual src/utils.c and checks its output with Python's independent JSON and UTF-8 decoders. It covers Unicode, all non-NUL control bytes, invalid UTF-8 replacement, every small escape/scalar boundary, truncation status, and guarded destination bounds, with ASan/UBSan enabled. C strings cannot represent embedded NUL. The decoder rejects decoded NUL in the requested value, while accepting valid escaped NUL in unrelated fields.

The decoder suite passes exact, non-NUL-terminated buffers to actual src/json_helpers.c; it checks bounds, direct-member selection, all escapes, UTF-8, surrogate pairs, malformed input, and strict/prefix overflow contracts. Objects are validated completely with a nesting limit of 64. Strict extraction clears the result on overflow; descriptions/release notes explicitly opt into UTF-8-safe prefixes. The backend suite uses real repository, source save/load, and slot metadata read/write code inside temporary storage. It parses their results independently with Python. Unused network/PS5 code is discarded at link time, and no download or payload launch occurs. make test-host runs all four host suites with sanitizers.

.github/workflows/verify.yml runs all host suites, existing frontend tests, and frontend build without publishing or deploying. Frontend lint still reports existing errors. The host suite tests response ownership/status handling, request cleanup, and autoload-list bounds. Storage transactions, autoload worker concurrency, and PS5 firmware behavior remain separate validation scopes. Backend storage/service calls in the HTTP tests are stubbed; successful mock imports move actual files inside temporary fixtures.

The HTTP server accepts config/source bodies up to 1 MiB and streamed uploads up to 512 MiB. Larger declared or accumulated bodies return HTTP 413; empty request bodies return HTTP 400. Upload staging files use private, unique .upload-* names and are removed by request teardown if uncommitted. This does not purge older orphan files or guarantee cleanup after power loss/SIGKILL. Autoload CSV responses currently allow 4,095 bytes; larger/unreadable lists return HTTP 500 with JSON rather than a truncated list.

Native compilation remains a separate gate. To preserve the previous ELF, build a candidate under scratch/ rather than run make clean:

mkdir -p scratch
docker run --rm --network=none -v "$(pwd):/src" -w /src \
  ps5-payload-sdk-pldmgr make ELF=scratch/pldmgr-candidate.elf

This command assumes the SDK image, frontend distribution, and embedded CA header already exist. For a new checkout, follow the normal build setup first. The ELF target currently does not track ordinary header dependencies; use a new candidate filename or force the ELF target to rebuild after header-only edits.