DeadAPI is a work-in-progress API for Valve's game Deadlock.
This repository is a Rust workspace. The workspace contains deployable services and shared components.
Use this command when you run a service directly on the host:
docker run --detach \
--name deadapi-postgres \
--restart unless-stopped \
--env POSTGRES_USER=deadapi \
--env POSTGRES_PASSWORD=deadapi_dev \
--env POSTGRES_DB=deadapi \
--publish 127.0.0.1:5432:5432 \
--volume deadapi-postgres-data:/var/lib/postgresql/data \
docker.io/library/postgres:17-alpineThe development stack contains PostgreSQL, the ingest API, the metadata worker, the artifact downloader, and the stats parser.
Create the local configuration files before you start the stack:
install -m 600 configs/development/ingest.example.json configs/development/ingest.json
install -m 600 configs/development/metadata.example.json configs/development/metadata.json
install -m 600 configs/development/demo-downloader.example.json configs/development/demo-downloader.json
install -m 600 configs/development/stats-parser.json configs/development/stats-parser.jsonOpen configs/development/metadata.json. Set account_name to your Steam login name. Replace the
refresh-token placeholder with your Steam refresh token.
Open configs/development/demo-downloader.json. Replace the R2 endpoint, access key, and secret
key placeholders with credentials scoped to the configured metadata and demo buckets.
Open configs/development/stats-parser.json. Configure the same metadata bucket with read access.
Run these commands from the repository root:
docker compose up --build --detach
docker compose ps
docker compose logs --follow ingest metadata demo-downloader stats-parserYou can use the same Compose file with Podman. Replace docker compose with podman compose.
Install a Compose provider before you use Podman Compose. The Z mount option applies a private
SELinux label to each bind mount on Fedora.
PostgreSQL runs each file in migrations/ when it creates the data volume. PostgreSQL does not run
these files again on an existing volume.
If you change a migration, delete the development volume. Then, start the stack again:
docker compose down --volumes
docker compose up --build --detachUse a valid Deadlock match ID to add a job:
curl --request POST http://127.0.0.1:8000/ingest \
--header 'content-type: application/json' \
--data '{"match_id": 123456789}'
curl http://127.0.0.1:8000/ingest/123456789Open http://127.0.0.1:8000 to view the Apalis Board dashboard. The dashboard displays queue metrics, worker status, and task states.
Open http://127.0.0.1:8000/docs to use the OpenAPI interface.
After artifact download, the stats parser downloads the compressed metadata, decodes Valve's
match and player analytics, persists them in PostgreSQL, and marks the ingestion job completed.
Both workers process up to four matches concurrently by default. Set
worker.max_concurrent_downloads in its configuration to change that limit. Set
worker.download_demo to false to persist only the metadata artifact; metadata-only jobs still
advance to stats parsing. Set worker.max_concurrent_jobs in the stats parser configuration to
change its concurrency.
The pipeline uses three typed Apalis queues: metadata resolution, artifact download, and stats
parsing. Apalis owns task claiming, worker heartbeats, retry backoff, concurrency, graceful
shutdown, and recovery of orphaned tasks. You can monitor each queue in the Apalis Board dashboard.
The ingestion_jobs table remains the public pipeline state read by the ingest status endpoint.
Each successful stage updates that row and enqueues the next typed task in one PostgreSQL
transaction.
Use this command to stop the stack and keep the PostgreSQL data:
docker compose downCreate a configuration file for each service that you want to run:
cp configs/ingest.example.json configs/ingest.json
cp configs/metadata.example.json configs/metadata.json
cp configs/demo-downloader.example.json configs/demo-downloader.json
cp configs/stats-parser.json configs/stats-parser.jsonReplace all placeholder credentials. Each binary reads its configuration file only during startup.
Run the applicable command from the repository root:
cargo run --package ingest
cargo run --package ingest -- --config /absolute/path/to/ingest.json
cargo run --package metadata
cargo run --package demo-downloader
cargo run --package stats-parserThe --config option is not necessary when you use configs/<service>.json.
The ingest service listens on 127.0.0.1:8000. The root path / serves the Apalis Board
dashboard. The /docs route serves the OpenAPI interface. The /api/v1 route exposes the Apalis
Board REST API.
The metadata service opens one Game Coordinator connection for each configured Steam account. PostgreSQL stores account leases and the request cooldown. The default request cooldown is 1,728 seconds. Multiple metadata replicas can use the same mounted configuration file.
Use a trusted computer to create the token. Run these commands:
cd scripts/steam-refresh-token
bun install
bun run get-tokenUse Steam Guard to scan the displayed QR code. Approve the login in Steam Guard. The script saves
the token in steam-refresh-token.txt. File permissions give access only to the file owner.
Set account_name to the Steam login name that the script displays. Copy the token to the
applicable steam.accounts[].access_token field. Delete steam-refresh-token.txt after the
metadata worker connects. The Steam account must have Deadlock in its library.
Mount the configuration file as a read-only file at /etc/deadapi/config.json:
docker run --mount type=bind,src=/secure/ingest.json,dst=/etc/deadapi/config.json,readonly \
<ingest-image> --config /etc/deadapi/config.jsonDo not add a production configuration file to the container image. Give read permission only to the runtime service user. Use the secret-file mount that your deployment platform supplies.
DATABASE_URL=postgres://deadapi:deadapi_dev@127.0.0.1:5432/deadapi \
cargo test --workspaceCargo reads each package name from the name field in the applicable Cargo.toml file.