rlru
Rust-first Rocket League replay uploader.
rlru uses strict TOML configuration, explicit local state paths, testable auth/upload boundaries, and a Dioxus client scaffold.
Crates
This repository is a Cargo workspace. The reusable pieces are published to crates.io as their own crates:
| Crate | Description | crates.io | docs.rs |
|---|---|---|---|
rlru |
CLI + library for uploading Rocket League replays | ||
psynet |
Standalone client for Psyonix's PsyNet RPC backend (Rocket League online services) |
crates/psynet (README) is independent of the rest of
rlru and can be depended on directly. The rlru-dioxus desktop client also lives
in this workspace but is not published. All published crates currently share a
single version number.
Library usage
rlru is a library as well as a CLI — the binary is a thin wrapper over the
public API, so you can drive config, syncing, and uploads from your own Rust
code.
[]
= "0.1"
= { = "1", = ["macros", "rt-multi-thread"] }
use Config;
use AppPaths;
use SyncService;
async
Key building blocks:
rlru::Config— strict TOML config, accounts, and upload destinations (Config::load,validate, helpers likeUploadDestinationConfig::ballchasing()).rlru::sync::SyncService— the sync engine (run_once,run_once_with_options,current_history).rlru::upload— the upload destination abstraction.rlru::psynet— the PsyNet client is re-exported asrlru::psynet, sorlruusers don't need a separate dependency to talk to PsyNet directly.
Full API docs are on docs.rs/rlru and docs.rs/psynet.
Screenshots




Development
Releases
Releases are driven entirely by the vX.Y.Z tag that matches the rlru Cargo
package version. All published crates (rlru, psynet) share that single
version number.
Fully automatic (recommended): bump the version in Cargo.toml,
crates/psynet/Cargo.toml, and crates/rlru-dioxus/Cargo.toml and merge to
main. The auto-tag-release workflow
notices the new version and pushes the matching vX.Y.Z tag for you, which kicks
off the release pipeline.
For the auto-created tag to trigger the downstream publish jobs, add a Personal Access Token (with
contents: write) as theRELEASE_PATrepository secret — a tag pushed with the defaultGITHUB_TOKENwill not start new workflow runs. Without it, the tag is still created; just re-push it manually.
Manual: cut the tag yourself from a clean main checkout:
Either way, the tagged run:
- uploads downloadable assets to the GitHub Releases page:
rlru-cli-linux-x86_64.tar.gzrlru-cli-windows-x86_64.ziprlru-dioxus-linux-x86_64.AppImage
- publishes
psynetthenrlruto crates.io, when theCARGO_REGISTRY_TOKENrepository secret is configured (GitHub release assets are still created when that secret is absent).
Windows Builds From Linux
The dev shell includes the Fenix x86_64-pc-windows-gnu Rust target and the
MinGW linker toolchain. Build Windows executables from Linux with:
The CLI executable lands under
target/x86_64-pc-windows-gnu/release/rlru.exe. The Dioxus desktop executable
lands under target/x86_64-pc-windows-gnu/release/rlru-dioxus.exe, with
WebView2Loader.dll copied beside it.
Configuration
Print the effective default configuration:
Validate a configuration file:
Tokens are stored separately from TOML config under the XDG config directory.
Accounts and Epic auth
Add and authenticate an Epic Games account with the device-code flow:
The command writes the account to config.toml, selects it, opens Epic's login
page, prints the device code, waits for approval, and stores the refresh token
under the account id in the local token directory.
In the Dioxus desktop app, use the Accounts screen with Epic Auth checked, or
click Authenticate on an existing Epic account.
If the account is already in the config, authenticate it separately:
Useful account commands:
The default upload destinations include Rocky, Ballchasing, and Rocket Sense at
https://rocket-sense.duckdns.org/api/v1. For Rocket Sense uploads, set
ROCKET_SENSE_TOKEN to a Rocket Sense bearer token before running rlru sync,
or configure a command that prints the token to stdout:
[]
= "bearer_command"
= ["pass", "show", "rocket-sense/token"]
Upload names
Replays are uploaded with a templated filename, which most destinations show as
the replay's name. The default produces names like
2024-01-15.14.30 SaltySphinx Ranked Doubles Win. Customize it in [behavior]:
[]
= "{YEAR}-{MONTH}-{DAY}.{HOUR}.{MIN} {PLAYER} {MODE} {WINLOSS}"
{PLAYER} and {WINLOSS} are from the synced account's perspective. Available
placeholders:
| Token | Meaning |
|---|---|
{YEAR} {MONTH} {DAY} |
Match date (local time, zero-padded) |
{HOUR} {MIN} {SEC} |
Match time (local time, zero-padded) |
{PLAYER} |
Synced account's in-match name (falls back to the account name) |
{MODE} |
Playlist name (e.g. Ranked Doubles, Tournament) |
{MAP} |
Map name (e.g. DFH Stadium) |
{WINLOSS} |
Win / Loss / Draw for the synced account |
{SCORE} |
Final score as team0-team1 |
{MATCH_ID} |
PsyNet match GUID |
The rendered name is sanitized for use as a filename and gets a .replay
extension automatically. Set upload_name_template = "" to keep the legacy
match-id filename.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.