Steam Rich Presence on PC Without Leaking Build IDs - A Unity and Godot Safe Wiring Pass
Steam Rich Presence is one of those features that looks tiny on paper and very real in production. Players see useful status text, friends get clearer join context, and your game feels less "opaque lobby software."
But small teams often wire it straight from internal state and accidentally publish strings that should stay private: branch labels, build IDs, debug map names, or temporary test mode flags.
This guide is a practical pass you can apply in one afternoon for Unity or Godot. It focuses on clean mapping, safe keys, and a QA checklist that catches leaks before release.
If you are also polishing input and Steam Deck behavior, pair this with How to Add Steam Input Correctly in Unity 6 and this troubleshooting reference for Steam Deck controller glyph issues in Unity Input System.

What Steam Rich Presence should expose and what it should never expose
Your public Rich Presence text should answer two player questions:
- What is my friend doing right now?
- Can I join them now or not?
That usually means player-safe values such as:
- mode: campaign, skirmish, ranked, custom
- phase: in menu, in match, post-match
- join state: joinable or not joinable
- optional coarse progress: chapter 2, act 3, biome name
It should never include:
- raw build IDs
- branch names like
staging-hotfix-2 - internal environment labels like
qa-west-03 - private room tokens
- machine or account identifiers
Steam's official Rich Presence docs are still the source of truth for key/value behavior and localization format: Steamworks - Rich Presence
The safe pattern - map game state to a public dictionary
The key principle is simple: do not send raw runtime strings. Send only values from a strict allowlist.
Instead of this risky flow:
- scene name -> directly copied to presence
- branch env var -> directly copied to presence
Use this safer flow:
- runtime state -> internal enum -> sanitized public token -> Steam key/value
That one indirection layer prevents accidental leaks during crunch when someone renames a scene to bossArena_perf_test_v47.
Unity implementation pattern
In Unity, many teams already have an app state machine. Add a RichPresenceMapper class that consumes state transitions and emits only approved tokens.
Recommended structure:
GamePublicActivityenumDictionary<GamePublicActivity, string>for display tokens- single
ApplyRichPresence(GamePublicActivity activity, bool isJoinable)function - branch guard that disables presence in non-production builds
Example mapping:
public enum GamePublicActivity {
InMainMenu,
InMatchmaking,
InMatch,
InPostMatch
}
private static readonly Dictionary<GamePublicActivity, string> PresenceMap = new() {
{ GamePublicActivity.InMainMenu, "menu" },
{ GamePublicActivity.InMatchmaking, "matchmaking" },
{ GamePublicActivity.InMatch, "in_match" },
{ GamePublicActivity.InPostMatch, "post_match" }
};
Then feed Steam only those mapped values, never the source scene/build text.
For Unity teams shipping frequent updates, this same "public mapping layer" approach also helps with release reliability in your content pipeline. If that is currently painful, the checklist in Unity 6 Addressables release workflow is worth reviewing after this pass.
Godot implementation pattern
In Godot 4, the risk is similar if you pipe node paths or debug labels directly from scripts. Keep the same architecture:
- map gameplay states to a compact public enum
- keep a dictionary of allowed steam values
- update Rich Presence only through one script entry point
For example, in GDScript:
enum PublicPresence {
MENU,
MATCHMAKING,
IN_MATCH,
POST_MATCH
}
var presence_map = {
PublicPresence.MENU: "menu",
PublicPresence.MATCHMAKING: "matchmaking",
PublicPresence.IN_MATCH: "in_match",
PublicPresence.POST_MATCH: "post_match"
}
The idea is not engine-specific. It is a discipline: "public strings are curated assets," not ad-hoc debug output.
Build and branch guards that prevent embarrassing leaks
Most leaks happen in two situations:
- A QA-only string path reaches production by accident.
- A dev turns on verbose state output for debugging and forgets to revert.
Use these safety rails:
- compile-time or environment guard to disable unsafe rich presence paths outside production
- unit test or static check that rejects disallowed patterns (
qa,staging,build_, long numeric IDs) - release checklist item that captures live Steam profile output from a release candidate build
If your team has multiple depots or experimental branches, make "presence sanity" part of your pre-release validation, right next to save compatibility and input checks.
Localization strategy for Rich Presence
If you support multiple languages, keep Rich Presence tokens stable and localize display text through your Steam localization setup, not by sending translated free-text from runtime.
Why this matters:
- stable tokens are easier to test
- translators work in one known place
- you avoid per-build text drift
A common robust set:
status=menustatus=matchmakingstatus=in_matchstatus=post_matchjoin=availableorjoin=unavailable
This also keeps your telemetry cleaner when you compare player activity states across patches.
QA checklist before shipping
Run this once for each release candidate:
- Launch production candidate build on a clean Steam account.
- Observe profile Rich Presence from a second account/friend view.
- Move through menu, queue, match, and post-match.
- Confirm no internal terms appear.
- Confirm joinability flips correctly.
- Repeat on at least one non-English locale if supported.
- Repeat after reconnect/network interruption to catch stale states.
For small teams, this test is cheap and catches exactly the kind of mistakes that cause trust damage disproportionate to the feature size.
Common mistakes to avoid
1) Sending scene names directly
Scene names are for your team, not for players. They drift, include debug tags, and sometimes include sensitive labels.
2) Forgetting to clear stale keys
If a key is no longer valid in current state and you never clear/update it, players can see misleading old status.
3) Mixing debug and public pathways
Keep debug overlays and logs separate from public rich presence update calls.
4) Skipping post-hotfix validation
Hotfixes often touch state transitions. Re-run Rich Presence QA after every multiplayer hotfix, not just major releases.
Minimal rollout plan for solo devs
If you are shipping alone and want low overhead:
- implement 3 states first: menu, in_match, post_match
- ship without fine-grained map/chapter info
- add joinability only after basic state accuracy is stable
Simple and correct beats feature-rich and leaky.
FAQ
Can Rich Presence include match IDs for direct joining?
It can, but treat identifiers carefully. Use join tokens designed for exposure, not internal server/build IDs.
Should I disable Rich Presence for unreleased playtests?
Yes, if your text pipeline is not fully sanitized. Private branches are where accidental leaks usually start.
Is this only a multiplayer concern?
No. Single-player games still benefit from clear status text and can still leak internal labels if mapping is sloppy.
Do Unity and Godot require different safety rules?
No. The same rule applies: runtime internals must pass through a strict public mapping layer before calling Steam APIs.
How often should we review Rich Presence strings?
At least once per release cycle and after every change to game state architecture or matchmaking flow.
Steam Rich Presence is a trust feature disguised as a tiny UX detail. Keep the output boring, consistent, and player-safe, and it will quietly improve discoverability and join flow without creating support headaches later.