Skip to content

Releases

v0.50.0-rc3

Choose a tag to compare

@github-actions github-actions released this 07 Nov 16:21
Immutable release. Only release title and notes can be modified.
f7a9c03

Added

  • Discord RPC OAuth, optional and experimental. Add optional browser sign-in, the adsum oauth commands, optional refresh tokens, GET_USER, GET_GUILDS, limits, and timeouts.
  • OAuth setup in the installer. install.sh can write the OAuth redirect and secret settings for the user service.
  • Cross-client state snapshots. With --state-file, Adsum can create arrpc-state-N links in client runtime folders and use ADSUM_STATE_PREFIX to choose the file prefix.
  • Compatibility detection. Adsum now recognizes games started by Box64, FEX, QEMU, Wine/Proton, and Steam reaper, including Wine games on macOS, plus Minecraft and compatible loaders on Linux.
  • Pre-release tags. Alpha, beta, and release-candidate tags are now published as GitHub pre-releases with notes from this changelog. Stable tags still need a numbered changelog section.

Changed

  • Lower memory use at startup. The bundled game database is stored in a smaller form, so the measured startup peak fell from about 57 MB to about 28 MB in local testing.
  • More exact game matching. Game arguments keep their original boundaries, including empty arguments, so programs opened through a wrapper are matched by the real game.
  • Clearer IPC overload response. When the queue is full, publishing now reports
    5000 Activity queue is full; retry later instead of appearing successful.
    A rejected publication still does not gain ownership.
  • Pre-release binary versions. Build tags are stamped into the workspace, so the binary reports the exact tag version. The self-updater still uses GitHub's stable release only.

Fixed

  • Cleaner shutdown. The scanner is stopped before its threads are joined, so shutdown does not wait beyond the shared time limit for stuck workers.
  • State aliases. Existing aliases now follow only trusted paths, and a missing alias is not recreated from an old snapshot.
  • OAuth expiry. The first command that finds the expired session now reports InvalidAccessToken, not NotAuthenticated.
  • Non-Linux builds. Windows, macOS, and other non-Linux targets now compile.

Security

  • OAuth tokens. Responses contain identity and scopes, never the access token. Logs and debug output do not show it.
  • OAuth settings. Refresh and restore require ADSUM_OAUTH_CLIENT_SECRET; without it, the client secret error is returned instead of a misleading invalid-argument error.
  • Paths and IPC. Paths reached through a symlink are checked across the full path, not only the last directory.
  • Windows IPC pipe. The named pipe now has an explicit access policy instead of receiving default permissions.

v0.40.0

Choose a tag to compare

@github-actions github-actions released this 29 Oct 14:05
Immutable release. Only release title and notes can be modified.
936e485

Performance

  • Incremental process table (Linux): a per-pid classification cache
    (crates/adsum-detect/src/cache/procs.rs) replaces the full re-read of
    every process each tick. A tick now enumerates /proc for liveness,
    sweeps the dead, revalidates cached hits (one cmdline read per detected
    game; the matcher re-runs if the argv fingerprint drifts) and classifies
    only new pids. Cached misses are not re-read; a miss that becomes a hit
    surfaces as an EXEC event or a watcher-loss flush.
    • Steady state: zero cmdline reads, automaton walks and allocations.
      On the bundled database shape (1062 processes): 5.05 → 0.38 ms/tick
      (−92%), 3152 → 3 allocs/tick, 40.3 KB → 54 B/tick of churn.
    • Every watcher loss channel (cn_proc gaps, backlog sheds, socket
      overruns, watcher death/resubscribe) forces one full-read tick; bundle
      swaps invalidate through an epoch in the generation Arc.
    • Watcher disabled/dead, other OSes and one-shot diagnostics keep the
      classic full sweep, byte-for-byte.
  • Zero-allocation /proc enumeration: raw getdents64 into a reused,
    aligned arena replaces std::fs::read_dir. ~3 allocations per entry →
    0 (~29 MB/h of churn removed at 1000 processes), which also keeps the
    glibc arenas and the RSS floor low.
  • Allocation-free snapshot lane: ScannedEntry.id/.name and
    ProcessDetectedEvent::Removed now use AppId/Arc<str>, and the scan
    loop's snapshot Vec is reused across ticks. Per tick: zero
    allocations; the daemon pump's ScannedGame construction is two
    refcount bumps instead of two deep copies.
  • Aux-table arena: the four SortedIndex tables (steam/epic/name/
    nodot) pack keys into one contiguous per-generation arena. Miss-heavy
    lookups are 13–39% faster and per-key malloc headers are gone. Build
    order, first-wins ties and lookups are unchanged (pinned by tests).
  • Steam install-tail matching: match_install_tail_into binary-searches
    a precomputed sorted tail index (rebuilt on dirs mutation) instead of
    walking every install prefix per probe, so large libraries no longer
    multiply each sweep or EXEC storm. Longest-wins and ambiguity refusal
    are preserved; a parity oracle test compares it with the old linear
    walk. refresh_if_stale now fingerprints over the map in place.
  • Borrow pass: five places that copied where a borrow suffices:
    • matching: the command-line AppId fallback no longer wraps the borrowed
      &str in a fresh AppId per process per tick.
    • cache/appid: one AppId shared by refcount instead of two copies.
    • runtime: first-sighting inserts are refcount bumps, not to_string().
    • types/cmd: ActivityCmd.cmd is Cow<'static, str>, so synthesized
      commands borrow their verb (wire format unchanged).
    • bridge/handoff: is_process_alive formats /proc/<pid> into a stack
      buffer.
  • Hoisted locks and buffers: the exclusions RwLock is read once per
    tick/event instead of per process; the dead-pid sweep, is_suspended
    and read_cwd_into reuse caller-owned sets and buffers.
  • Windows/macOS: the snapshot enumerator folds \→/ at fill time so
    match_process's Cow stays Borrowed; sysinfo slots are refilled in
    place and gain compact_slots; process_start_ms refreshes with
    ProcessRefreshKind::nothing() instead of everything().
  • Minor: Exclusions.executables is an FxHashSet; the persist gate
    swaps its dirty flag with AcqRel (was SeqCst); broadcast_raw
    answers both encoding questions in one pass.

Changed

  • File organization: one concern per module. The near-twin module
    pairs are gone — scan.rs (classification) is now matching/ with
    the pipeline in mod.rs and its rule families in normalize (the one
    name-folding rule), paths (what it means for a declared name to name
    a path), args (borrowed command-line token scans) and tiers (the
    aux match tiers + shared hit tail); the tick orchestration moved from
    scanner.rs to sweep.rs; database.rs (bundle swaps) became
    generations.rs, leaving db.rs as the single wire-model module.
    runtime.rs split into runtime/{mod,workers,lanes,cadence,loops} —
    start() reads as lifecycle and the two 150+ line thread closures are
    named bodies (run_scan_loop, run_refresh_loop). The reuse-buffer
    family moved to a top-level scratch.rs with a new
    EnumerateScratch group (pid list, getdents arena, liveness/work
    lists) so the enumeration state no longer rides inside the per-pid
    reader scratch, and the two per-pid caches grouped under cache/
    (appid + procs) with their shared trust model documented once.
    os_matches moved beside OsName in types.rs; first_sightings /
    apply_ignore_list moved beside the lanes that consume them. Public
    paths that embedders use are preserved (adsum_detect::proc_start,
    runtime::ScanGuard/idle_wait, the matching:: re-exports); the
    test tree mirrors the source (tests/unit/matching/,
    tests/unit/cache/, tests/unit/platform/). No behavior change: the
    full suite passes untouched, rustdoc is warning-free.
  • Platform backends behind one seam: the OS-specific code that was
    scattered as #[cfg] islands inside shared files (six reader pairs in
    scan.rs, two enumerations in scanner.rs, the netlink wiring in
    runtime.rs, four arena-release arms in server.rs) now lives under
    crates/adsum-detect/src/platform/ — linux/ (/proc readers,
    getdents64 enumerator, netlink dispatch/watcher), snapshot/ (the
    sysinfo backend for every non-Linux target), macos/ (the .app
    bundle-interior shape — deliberately ungated: the tier is data-driven
    and must run, and be tested, everywhere), windows/ (documented
    empty: nothing Windows-only is left in the crate), plus unified
    memory.rs (arena release) and proc_start.rs (public path
    adsum_detect::proc_start unchanged). The seam is a static-method
    ProcessEnumerator trait (traits/enumerator.rs: begin_tick +
    read_one), so the tick orchestration — BOTH the full sweep and the
    incremental cache — is now 100% cfg-free and runs on every target;
    the incremental mode is gated only by cache_mode_live (watcher
    enabled AND live), which today can only hold on Linux. No behavior
    change: the full test suite passes untouched, and the parse_proc_pid
    suite moved with its function.
  • Watcher opt-in under systemd: the user-unit guide now shows a drop-in
    that resets the namespace-creating sandbox directives so cn_proc can
    deliver, with the filesystem and device isolation loss stated explicitly.
    The default unit keeps its sandbox and polling.

Added

  • SteamAppId tier on Windows and macOS: read_steam_app_id was a
    None stub off Linux, leaving the 57% of database entries with no
    declared executables without their authoritative tier on those
    platforms. sysinfo 0.39 reads the environ block on both (PEB through
    the process handle on Windows, the sysctl arg/env route on macOS); the
    reader uses a System scoped to the call — the shared snapshot never
    accumulates environ copies — and the existing per-pid memo
    (Present/Absent/Stale + invalidation sequence) keeps it to one
    read per process lifetime. Permission-gated processes memoize
    Absent, exactly like an unreadable /proc/<pid>/environ.
  • Steam and Heroic discovery off Linux: fallback Steam roots now
    include the registry's SteamPath (HKCU\Software\Valve\Steam via
    reg query — dependency-free; discovery runs at startup plus every
    120th tick) and the Program Files (x86)/Program Files defaults on
    Windows, and ~/Library/Application Support/Steam on macOS; Heroic's
    cached library is found under %APPDATA%\heroic (Windows) and
    ~/Library/Application Support/heroic (macOS). The
    ADSUM_STEAM_ROOT/ADSUM_HEROIC_ROOT overrides stay exclusive.
    Without these, the Steam-library and -epicapp tiers were silently
    dead off Linux ($HOME/XDG shapes do not exist on Windows).
  • DetectablesBundle carries a monotonic epoch (stamped inside the
    generation Arc, so a reader can never pair it with a foreign
    bundle): the cache-invalidation signal for refreshes and override
    edits, costing one full-read tick per swap.
  • watch() takes an invalidate flag it sets on every loss channel it
    can observe from inside (sequence gap, shed, ENOBUFS); the retry
    loop sets it on both edges of the watch.

Fixed

  • Proc-events watcher: a non-initial user or PID namespace now turns
    the watcher off with one warning instead of retrying a self-test the
    kernel can never satisfy. watcher_live is set only after the self-test
    parses an event and cleared on every exit. A refusal is recognized both
    as a nonzero NLMSG_ERROR and as a PROC_EVENT_NONE ack with an error
    in the event body, and retries back off from 5 seconds to a 5-minute cap
    instead of waiting a fixed 5 minutes. A watch that proved delivery resets
    that backoff, so a late failure retries soon instead of inheriting a stale
    cap.
  • Proc-events listener leak: every exit after a successful subscription
    now sends PROC_CN_MCAST_IGNORE before closing the netlink socket. The
    kernel decrements its global listener count only on that message, so a
    stopped watcher, a failed self-test or a lost receiver no longer leaves
    the count positive. A leaked count let sandboxed sockets receive events
    they were never granted.
  • PID namespace gate: the watcher also reads NSpid from
    /proc/self/status and stays off when it sees several values, which
    means a nested PID namespace. A procfs mounted inside that namespace
    collapses the line to one value, so the self-test remains the final word.
  • Steam demos whose executable is not in the database (VEIN Demo is the
    motivating case) are now detected when the process runs under the AppId's
    own install dir (steamapps/common/<installdir>/). T...
Read more

v0.39.0

Choose a tag to compare

@github-actions github-actions released this 21 Oct 13:21
Immutable release. Only release title and notes can be modified.
fa4f091

Breaking

  • Renamed the project from rsRPC to Adsum. The binary is now adsum, the
    systemd service is adsum.service, environment variables use the
    ADSUM_* prefix, and config/cache moved to ~/.config/adsum and
    ~/.cache/adsum. Existing 0.38.1 installations need a one-time manual
    migration: the old updater only looks for rsrpc-cli-* assets and
    cannot update across the rename.

Added

  • Heroic/Epic sandbox-id detection: Proton games launched by Heroic carry
    -epicsandboxid=<id> on the command line, and the service sandbox hides
    the environ — so the cmdline is the only distributor id a service can
    see. GTA V Enhanced is the motivating case.
  • Epic -epicapp fallback: older Legendary releases (before 0.20.25) omit
    the sandbox id and pass only the app name. Heroic's cached
    store_cache/legendary_library.json maps it to the same epic
    namespace the database stores; the sandbox id remains the first choice.
    The config root can be overridden exclusively with ADSUM_HEROIC_ROOT.

Fixed

  • IPC SET_ACTIVITY publications with a missing or zero PID are now
    rejected before any state change, instead of leaving an orphan
    presence downstream.
  • An absent activity field in an activity update is now rejected
    instead of being treated as a clear; only an explicit null clears the
    presence.

Security

  • Bumped rustls-webpki to 0.103.16 and webpki-roots to 1.0.10 in all
    three lockfiles; RUSTSEC-2026-0382 (distrusted intermediates accepted)
    and RUSTSEC-2026-0381 (distrusted emSign roots bundled) are fixed in
    those versions. Both crates are reached only through ureq/rustls,
    the HTTPS client.

What's Changed

  • Activity: a missing activity field is not a request to clear by @yiesko in #30
  • Detect: the Epic sandbox id names the game when the environment is hidden by @yiesko in #31
  • Detect: the Epic app name identifies the game when the sandbox id is missing by @yiesko in #32

Full Changelog: v0.38.1...v0.39.0

v0.38.1

Choose a tag to compare

@github-actions github-actions released this 15 Oct 16:07
Immutable release. Only release title and notes can be modified.
77e7abc

Security

  • Bumped rustls to 0.23.46 in all three workspaces; RUSTSEC-2026-0346,
    -0347, -0348 and -0349 were published on 2026-10-14 and are fixed in
    0.23.46 and later. The crate is reached only through ureq, the HTTPS
    client, and the gate went red on a push that had not touched it.

v0.38.0 - 91db30a03ae3993807a31f097fb764bf2a919610

Choose a tag to compare

@github-actions github-actions released this 11 Oct 14:34
Immutable release. Only release title and notes can be modified.
91db30a

Added

  • Detection traceability: every first sighting logs which matcher won
    (automaton, cwd-joined, proton-automaton, steam-app-id,
    steam-library, exe-stem, folder) plus process age in ms at
    classification; every clear carries its reason (sdk-clear,
    abrupt-close, process-vanished, scan-absent, yielded); SDK takeovers and
    generic resumes name both sides of the slot. Snapshots gain additive
    detectionSource / detectLatencyMs fields per card ("sdk" for
    client-published cards, absent on old ones). Tracing only, on
    lifecycle transitions — no hot-path cost, no retained state. Guide:
    docs/detection-traceability.md.

Security

  • Bumped rustls to 0.23.45 in the fuzz and tools/updater
    workspaces (RUSTSEC-2026-0285, TLS 1.3 handshake); the main lock was
    already clean. All three locks are now audited in CI.
  • The bridge now validates the WebSocket Origin header like the game
    transport already did: absent Origin (native clients) and Discord's
    own pages pass, anything else is closed at connect before READY unless
    listed in --bridge-allowed-origins / RSRPC_BRIDGE_ALLOWED_ORIGINS.
    A malicious page can no longer read local activities or send bridge
    commands through ws://127.0.0.1:1337; frames pipelined past the
    close are dropped as unregistered. Client ids are now unique
    process-wide across both bridge servers (per-server counters could
    alias registrations), and messages from unregistered ids never reach
    control handling.
  • The installer is fail-closed: a missing signature manifest or a
    missing minisign binary aborts the install instead of continuing on
    SHA256 alone (explicit --binary files stay exempt). The systemd unit
    ships as a signed release asset covered by SHA256SUMS.txt and is
    verified the same way before anything is installed from it.
  • Presence snapshots are owner-only: the temp file is created with an
    explicit 0600 mode on Unix (the rename carries it to the final
    path), so app ids, pids and ports stay private under a permissive
    umask. Every write uses its own temp, created exclusively without
    following symlinks, so a planted link is refused and concurrent
    daemons sharing a slot still write apart. Slot selection stays
    best-effort — concurrent daemons share slots, last writer wins —
    and stale crash temps are swept under the same staleness rule.

Performance

  • The per-tick, per-publish and per-connection allocations left behind by
    the earlier memory fix (#2) are gone, under one rule: one owned
    materialization at the boundary, zero deep copies after it
    (61 commits,
    59 files, +6394/-1349). Same games detected, same order, same log lines.
  • Ids (AppId, SocketId) are Arc<str> newtypes with Deref<str>, so
    cloning one is a refcount bump: every path that copied a client id,
    socket id or app id now shares it.
  • Payloads that reach more than one consumer cross as
    Bytes/Utf8Bytes/Arc<T> and are encoded once; the MessagePack
    derivation is lazy and shared instead of rebuilt per consumer.
  • Scratch buffers (ExecScratch, MatchScratch, the /proc stat and
    environ paths) are owned by the loop and reused across ticks, which is
    where roughly 400 allocations every 5 s per process came from. The scan
    tick classifies every process on the machine, so this is the path that
    decides the resident set, not a hot path in the usual sense.
  • Collections that outlive their build are Box<[T]> and shrunk;
    transient ones deliberately are not. Entries carry packed [u32;2]
    index pairs, and the automaton is built in a single pass with exact
    reservations.
  • Structural caps on the fetched database, measured rather than guessed:
    the bundled snapshot is 24,299 entries, and the maxima within an entry are
    16 executables, 8 aliases, 30 Steam SKUs and 204-character names. Every
    cap sits an order of magnitude above that. A body decoding past one is
    rejected as DatabaseTooLarge,
    distinct from a parse failure, and the caller keeps the database it
    already holds. The 64 MiB body limit bounded the bytes; it never
    bounded what they decode into — one entry with tens of thousands of
    executables is a megabyte of JSON becoming one automaton pattern and
    one index pair each.
  • The census now reports the resident high-water mark beside the current
    RSS and the live/free heap split from the allocator, so a growth can be
    told from a floor that held while the peak climbed. Both were verified
    against the kernel before being written: the kB unit in
    /proc/self/status is really KiB, confirmed by comparing resident pages
    against VmRSS.
  • Two deliberate deviations. The SET_ACTIVITY echo emits data
    keys in declaration order instead of the BTreeMap's alphabetical
    order (the echo contract already allows key-order variance and every
    test compares parsed values), and the IPC close-clear nonce now carries
    the value the client sent instead of its JSON text — that one is a
    fix, see below.

Changed

  • Folded rsrpc-state into rsrpc-bridge (12 crates down to 11): the
    snapshot slots had exactly one consumer, so the crate boundary only
    added hops. rsrpc-telemetry stays separate: four crates share it,
    and folding it into the bridge would point their dependencies the
    wrong way. BindPaths=/tmp in the unit stays as is: tightening was
    investigated, but BindPaths takes no globs, the sockets only exist at
    runtime, and the fan-out symlinks need the host path writable.
  • Regression nets now pin the memory bounds: replay-cache pruning and
    broadcast fan-out never exceed MAX_CACHED_ACTIVITIES, and the
    flood-guard table stays within its cap.
  • Shared package metadata and dependency pins now live in the workspace
    root ([workspace.package] + [workspace.dependencies]): edition,
    MSRV, license, repository, authors and 15 dependency versions change
    once instead of per crate. Cargo.lock is byte-identical, and per-crate
    versions are untouched. authors names the historical author and the
    current maintainer (spikehd, yieskoW). tokio-util stays pinned
    per crate on purpose: inheritance cannot override default-features.
  • CI now runs the test suite on Windows and macOS as well as Linux,
    audits all three lockfiles, and validates non-Rust artifacts
    (node --check on the plugin, systemd-analyze verify on the unit,
    typecheck of the fuzz workspace). A new fuzz workflow smoke-checks
    targets on PRs and runs the engine nightly (10 minutes per target:
    ipc_frame, activity_full, trim_db, db_parse), with corpus
    caching between runs. New ipc_frame fuzz target drives the IPC
    reader (handle_stream with a mock facilitator and a split
    byte-duplex), asserting every response byte belongs to a complete
    frame (708k runs clean locally).
  • All GitHub Actions references are pinned to commit SHAs (tags kept as
    comments), closing the mutable-tag supply-chain window on the release
    pipeline especially.
  • Unix IPC pumps are capped at 16 concurrent peers like Windows (which
    keeps 32): connection floods past the cap drop peers at accept instead
    of parking blocking threads without bound, leaving room in the
    32-thread blocking pool for unrelated work.
  • Split the two biggest modules internally, same crates and same
    behavior: crates/rsrpc-detect/src/server.rs is now foundation plus
    runtime (threads, shutdown, join), database (bundle ownership and
    swaps), scanner (enumeration and classification), steam
    (library roots) alongside the existing bundle/db/refresh/scan
    modules; crates/rsrpc-bridge/src/bridge.rs is now construction plus pumps
    alongside consumer (fan-out and pruning), router (activity
    decisions), replay (cache and ghost-reap pid resolution), snapshot
    (persistence) and control (SET_USER/RESET_USER). Shared fields
    are crate-visible for the new units; wire protocols unchanged. One
    breaking change for library callers: rsrpc_proc_events::watch now
    takes a shutdown flag (watch(events, stop)), so the watcher thread
    joins on directed shutdown instead of blocking on netlink.
  • CodeQL now scans the workspace as one job with the v4 action inputs, and
    re-scans whenever any workflow changes rather than only its own.
  • Dependabot maintains the three cargo workspaces and the pinned actions;
    CI declares the least permissions each workflow needs, and the runners
    are pinned by image label instead of floating on ubuntu-latest.
  • The three workspaces share one grouped cargo entry in CI instead of
    three near-duplicates.

Fixed

  • Hostile timestamps saturate instead of overflowing: normalize_timestamp
    uses saturating_mul, so extreme inputs floor at i64::MIN in both
    debug and release builds.
  • Payload builders (empty_cached, generic_payload) return None on
    encode failure instead of shipping empty frames: callers log, count a
    dropped broadcast and skip. The hand-written clear JSON is gone — clears
    serialize from the same struct as everything else — and the emergency
    error frames escape the command name instead of interpolating it raw.
  • The flood-guard key builds once per call instead of twice (an AppId
    key would allocate identically, since callers hold &str).
  • Scanner threads now belong to the daemon lifecycle: ProcessServer
    gained directed shutdown() + join() (plus rsrpc-* thread names),
    every long sleep became an interruptible park, the netlink watcher
    honors a stop flag (including its self-test), and Daemon::run_until
    shuts down, joins and drops the scanner on success and bind failures.
    Future abandonment (dropping the future via select!/timeout/abort)
    signals shutdown through a cancellation guard instead, so threads exit
    on their own within ab...
Read more

v0.36.1 - 676fedc1fb9dba2f0465e5d19c3e17bbdd071c43

Choose a tag to compare

@github-actions github-actions released this 25 Sep 00:41
Immutable release. Only release title and notes can be modified.
676fedc

Commits in v0.36.1 (v0.36.0..HEAD)

  • docs: credit SpikeHD as the original rsRPC author (a4de519)
  • chore: rename repository urls to yiesko/discord-rich-presence (437b3d0)
  • fix(cli): stop --rollback from re-execing into a swap loop (e9b4d85)
  • feat(cli): wire --state-file and RSRPC_STATE_FILE into the bridge (15b06d8)
  • fix(unit): drop dead RSRPC_LOGS_ENABLED and allow the self-update swap (7c2d5ae)
  • docs: rewrite the README, split guides into docs/, add screenshots (e2422ea)
  • docs: changelog the rollback, state-file and unit fixes (6e68663)
  • docs: release 0.36.1 changelog (15944cd)
  • style(cli): wrap the release api url for rustfmt (b64fee6)
  • chore: release v0.36.1 (676fedc)

Full Changelog: v0.36.0...v0.36.1

v0.36.0 - 203d0dab72f6ef5023421fc64dd847ca2dce1864

Choose a tag to compare

@github-actions github-actions released this 23 Sep 18:34
Immutable release. Only release title and notes can be modified.
203d0da

Commits in v0.36.0 (HEAD)

  • feat(ws): rsrpc-ws skeleton with config, error and message types (6d9d4f4)
  • feat(ws): async server with bounded queues, keepalive and shutdown (6d06911)
  • feat(types): rsrpc-types with Arc ids, cmd and user modules (20ee90f)
  • feat(protocol): rsrpc-protocol with typed errors and Arc-shared payloads (89f6942)
  • feat(types): Box conversions and TimeoutValue accessor (9ce7aaa)
  • refactor(lib): re-export types/protocol with Arc payloads and typed errors (ee9fd86)
  • feat(transport-ws): game WS transport over rsrpc-ws without map lock contention (1d18abd)
  • feat(transport-ipc): async IPC transport with JoinSet lifecycle (ab7248b)
  • feat(state): rsrpc-state snapshot slots with caller version (65cfd1e)
  • refactor(protocol): canonical query_params shared by transports (a90d444)
  • feat(bridge): async activity bridge with dirty-gated snapshots (31b1cac)
  • feat(telemetry): rsrpc-telemetry gauges, RSS and census rendering (f61e859)
  • feat(proc-events): rsrpc-proc-events watcher with shared gauge sink (fff1fc0)
  • feat(steam): rsrpc-steam VDF provider with hermetic tests (69eadde)
  • feat(detect): rsrpc-detect database module with bundled snapshot (95ff1f1)
  • feat(detect): process scanner split with lock-free generation swaps (d9c1c63)
  • feat(core): rsrpc-core daemon wiring with run_until shutdown (4643436)
  • feat(cli): thin async CLI over Daemon with tokio signal handling (30aafd6)
  • refactor!: remove legacy lib, wire workspace lints and CI paths (43262b4)
  • fix(core): move database into scanner instead of cloning and retaining it (8e5139c)
  • perf: drop chrono, lazy fallback, inlines, reused IPC frame buffers (ba2daea)
  • perf: snapshot slots without deep clone, borrow args map (9e65707)
  • perf: carry is_clear on payloads instead of re-parsing (bcbb3b8)
  • perf: FxHashMap on u64-keyed hot maps with bench proof (b173331)
  • perf: refcounted text payloads, slim subscriber, tuned runtime (8a70ee8)
  • fix(ipc): clear presence on protocol-violation close (6bc8dfb)
  • fix(bridge): reap dead-pid ghosts from replay on null scan (2d42c76)
  • fix(transports): clear every published pid on disconnect, bounded history (4ca6ba9)
  • docs: fix private intra-doc link from public cache_entry_pid (9496c98)
  • docs: point Unreleased entries at workspace paths, document modularization (ffa2039)
  • fix(bridge): resolve replay-cache owner from body pid first (75b8db2)
  • fix(bridge): register consumers under resolved protocol (7a8dff6)
  • fix: close four reviewer-found correctness gaps (c683bbd)
  • fix(detect,steam): guard empty exclusion fetch, decode mount UTF-8 (58b3777)
  • fix(ipc): socket-link ownership, reliable presence clears (22ed44b)
  • fix(ws,ipc): reap JoinSets, enforce idle on its own deadline (429ba03)
  • fix(ipc): unblock connection pumps on unix shutdown (fc4bd06)
  • fix: process parity and review nits (9ead738)
  • refactor(bridge): split handle_set_activity into focused helpers (127e867)
  • refactor(ipc): split handle_stream per-packet arms (2c55e02)
  • refactor(bridge): single user-mutex section in handle_bridge_control (599563c)
  • refactor(cli): split async_main into focused helpers (1cc2d9e)
  • feat: wire resource census hourly and session-boundary emission (9dee156)
  • fix: restore cross-platform builds and lint gates (9b9cfd5)
  • fix(detect): share one detection generation across server clones (04cd02f)
  • fix(ipc): probe errs toward alive after a successful connect (638bd3e)
  • fix(transport-ws): bound reply waits so a stalled client cannot freeze the pump (7b603d9)
  • fix(ipc): route clean-close clear through the retrying clear path (f6ab963)
  • fix(proc-events): count only delivered events as forwarded (8610bea)
  • docs(ipc): document the Windows shutdown drain limitation (3a3f7e2)
  • docs(readme): make the library examples compile (01a9317)
  • fix(bridge): return the unix liveness probe directly (04e627a)
  • fix(proc-events): gate netlink-only items to Linux (cff8fa0)
  • fix(detect): serialize detection-bundle writers under one lock (58e94e2)
  • fix(transport-ws): record publications even when the reply fails (0b4e7ed)
  • docs(readme): align the runtime description with current behavior (e2c5591)
  • chore(core): drop the unused tokio-util dependency (31e7e49)
  • docs(core): correct the scanner channel-bound comment (14cb146)
  • docs(detect): finish the ac_probe rustdoc sentence (1679600)
  • refactor(steam): remove unused test seams (948230c)
  • refactor(protocol)!: decouple the HTTP error from the client (b0369b2)
  • docs(readme): drop removed-behavior bullets, complete bench paths (e554719)
  • perf(publish): fingerprint first, drop floods before building (cee0efd)
  • fix(protocol): stamp application id before fingerprinting (b257006)
  • docs(readme): drop the unsupported RSRPC_STATE_FILE bullet (9003659)
  • fix(transport-ws): clear evicted pids instead of leaking them (28a111a)
  • fix(transport-ws): refuse disallowed origins at connect (bbbc3f8)
  • docs(protocol): scope the dependency claim to workspace-internal (34f93a9)
  • docs: cover undocumented functions touched by the PR (e430d02)
  • fix(transport-ws): retain evicted clears when enqueue fails (0e0f0bd)
  • fix(detect,bridge,core): per-slot clears, scan shutdown exit, ignored EXEC hits (a402725)
  • fix(detect): allow dead exec_hit_ignored off Linux (cd6e958)
  • docs: document every function touched by the PR diff (8454abc)
  • fix(transport-ws,ws): cap publications at tracking bound; validate bind bounds (3618b0d)
  • fix(detect,bridge): review follow-ups; validators after swap, watcher-gated backoff, single-lock removal (8dd860b)
  • fix(ws): bound over-limit rejection backlog; register tasks for shutdown (8cafbb3)
  • fix(core): map each connector flag to its command class (142b16f)
  • chore(msrv): raise minimum toolchain to 1.95 (4ff4b8a)
  • docs(ws): restore slow-consumer doc displaced by bind test (491d71b)
  • chore(ci): pin toolchain action to immutable SHA (bb82013)
  • fix(ws,transport-ws): reject zero keepalive at bind; drain residue on shutdown (b989297)
  • fix(detect): reject empty exe names; unpark scan on watcher failure (7f64ef4)
  • fix(ws): cancel handshakes on shutdown; reject zero keepalive at bind (265a9cc)
  • fix(bridge): fallible pid_t narrowing; deterministic barrier test (af26859)
  • fix(steam): hermetic cache seam for refresh tests (def2702)
  • fix(ws): reject zero idle_timeout at bind (d349efe)
  • fix(ipc): malformed activity disturbs no presence (414cd58)
  • fix(ipc): bind falls through candidate dirs (1529a25)
  • docs: precedence wording; symbol citation over line number (fbccb1c)
  • fix(bridge): count pruned consumers in raw broadcasts (5253767)
  • fix(ipc): drop closes live sockets; cancel before abort (5feba05)
  • fix(telemetry,proc-events): bounded watcher backlog with CAS reservation (1fba9bd)
  • fix(transport-ws): ordered pending-clear queue with per-clear deadlines (dfb6949)
  • fix(detect): normalized match-all guard; unpark dead-code allowance (5157fb3)
  • fix(ipc): error reply on malformed activity; disturb no presence (9259930)
  • fix(bridge): deterministic raw-broadcast counting test (8c867fc)
  • fix(bridge): prune consumer protocol entries with dead consumers (0271714)
  • fix(ipc): back off on repeated accept errors (4a77f49)
  • fix(bridge): prune consumer protocol entries with dead consumers (741fab2)
  • fix(ws,ipc): back off on persistent accept errors (309e764)
  • fix(detect,bench): poison-tolerant sweep; pinned flood-drop timestamp (476d289)
  • docs: mutable daemon in staged API example (4f8ebe7)
  • fix(core): hermetic override paths via explicit environment source (7bb631e)
  • fix(ws): server Drop; hub close timeout; zero idle validation (d1ef8da)
  • fix(transport-ws): per-clear deadlines; ordered staging; retry worker (cdae401)
  • fix(detect): normalized match-all guard; unpark allowance (1f5e6d5)
  • fix(ipc): malformed error reply; candidate-dir fallback; minimal tokio-util (1503ef6)
  • fix(core,protocol,cli): scan interval validation; WsExhausted wording; misc (86ec39f)
  • ci: fix msrv toolchain input; drop Node audit; pin ubuntu 22.04 (22bfa68)
  • Merge staging: full workspace modularization + ghost-presence and memory fixes (e12182a)
  • fix(detect): deferred post-swap trim and allocator telemetry (97185db)
  • perf(detect): reuse per-tick buffers in scan and match paths (78a59d2)
  • perf(detect): single-pass automaton build with sized vectors (b7f9d08)
  • chore(deps): trim tokio-util to default features (0840f49)
  • fix(detect,ws): non-Linux builds and review feedback (d6cab8c)
  • test(detect): capacity-gated backing assert; check normalized / (62fe758)
  • fix(detect): gate native_at helper to linux test path (29f0f6e)
  • style(detect): rustfmt native_at closure onto one line (eec9d73)
  • perf(detect): retain high-water Exec slots across smaller sweeps (61aa308)
  • Merge PR #2: Fix hourly memory ratchet + per-tick churn (54a85cd)
  • docs: release 0.36.0 changelog (21fceba)
  • chore: release v0.36.0 (203d0da)

What's Changed

  • Full workspace modularization + ghost-presence and memory fixes by @yiesko in #1
  • Fix hourly memory ratchet + per-tick churn (measured, no behavior change) by @yiesko in #2

New Contributors

  • @yiesko made their first contribution in #1

Full Changelog: https://github.com/yiesko/discord-rich-presence/commits/v0.36.0