SilentWolf Shut Down: Move Your Godot Leaderboards to Crux in 15 Minutes
SilentWolf, the most-recommended free Godot backend, went dark in late 2025 with no warning and broke already-shipped games. Godot has no first-party backend. Here's a concrete, copy-paste migration of your leaderboards to Crux using the real API - submit and fetch, verified against the live contract.
SilentWolf is gone. The free backend that half of Godot's leaderboard tutorials pointed at - the one you added in an afternoon because it just worked and cost nothing - went dark in late 2025, around November. For a lot of developers there was little or no warning, and because SilentWolf served live leaderboards inside games that had already shipped, those online features simply stopped responding one day.
If that is you, this guide gets your leaderboards working again in about 15 minutes. It is a concrete migration to Crux, not a sales pitch: real GDScript, real endpoints, verified against the live API contract. Copy it and it runs.
Why this keeps happening to Godot specifically
Unity has Unity Gaming Services. Unreal has Epic Online Services. Godot has no first-party backend at all. There is no Godot-branded auth, cloud save, or leaderboard service, and there never has been. So the community did the sensible thing and standardised on the best free third-party option - SilentWolf - and a very large number of shipped and in-development Godot games ended up depending on one small, free, single-maintainer service.
That is a great deal right up until the day it isn't. A free backend with no contract can pause, get abandoned, or be switched off with no notice, and everything pointed at it breaks at once. The lesson is not "never use a free backend" - it's "use one you can leave." (More on that below: the whole point of picking a replacement is that your data stays yours.)
What you're replacing
A typical SilentWolf leaderboard integration is tiny - configure once, persist a score, fetch the top N. Exact call names vary by SilentWolf version, but it looks about like this:
# BEFORE - SilentWolf
func _ready() -> void:
SilentWolf.configure({
"api_key": "YOUR_SW_KEY",
"game_id": "my-game",
})
func submit(player_name: String, score: int) -> void:
SilentWolf.Scores.persist_score(player_name, score)
func load_top() -> void:
SilentWolf.Scores.get_high_scores(10)
# results arrive on the sw_scores_received signal
That's the shape we're going to reproduce against Crux: an API key, submit a name + score, fetch the top N. Crux's leaderboard model maps onto it almost one-to-one, which is what makes this a 15-minute job instead of a rewrite.
Step 1 - create a project and a leaderboard (3 minutes)
- Sign up for Crux (free - 10,000 MAU, no credit card). A project and a
devenvironment are created for you. - In the dashboard, copy three values: your project ID and environment ID (both are UUIDs), and a new API key (a long secret string, shown once at creation - copy it right away).
- Create a leaderboard (give it a key like
high_scores, sort orderdesc) and copy its ID.
Those are the same four things SilentWolf needed (a key and a board), just named explicitly. Like SilentWolf, this flow embeds an API key in the client - fine for casual boards; see the anti-cheat note at the end for competitive ones.
Step 2 - drop in a GDScript autoload (5 minutes)
Add this as an autoload named CruxLeaderboard (Project → Project Settings → Autoload). It talks to the real Crux HTTP API with Godot's built-in HTTPRequest - no addon required. Submitting a score does not require the player to log in: the leaderboard entry's player_id is just a string you choose, exactly like a SilentWolf player name.
extends Node
## CruxLeaderboard - a drop-in SilentWolf replacement backed by Crux.
const BASE := "https://crux.supercraft.host"
const PROJECT := "YOUR_PROJECT_ID" # a UUID from the dashboard
const ENV := "YOUR_ENV_ID" # a UUID from the dashboard
const API_KEY := "YOUR_API_KEY" # the bare secret string from the dashboard
const BOARD := "YOUR_LEADERBOARD_ID"
signal scores_received(scores) # Array of { player_id, score, rank }
signal score_submitted(ok)
func _entries(suffix: String) -> String:
return "%s/v1/projects/%s/environments/%s/leaderboards/%s/entries%s" % [BASE, PROJECT, ENV, BOARD, suffix]
func _headers() -> PackedStringArray:
return PackedStringArray([
"Authorization: ApiKey " + API_KEY,
"Content-Type: application/json",
])
## Equivalent of SilentWolf.Scores.persist_score(player_name, score)
func submit_score(player_name: String, score: float) -> void:
var http := HTTPRequest.new()
add_child(http)
http.request_completed.connect(func(_result, code, _headers, _body):
http.queue_free()
emit_signal("score_submitted", code < 400)
if code >= 400:
push_error("Crux submit_score failed: HTTP %d" % code)
)
var payload := JSON.stringify({ "player_id": player_name, "score": score })
http.request(_entries("/"), _headers(), HTTPClient.METHOD_POST, payload)
## Equivalent of SilentWolf.Scores.get_high_scores(limit)
func get_top(limit: int = 10) -> void:
var http := HTTPRequest.new()
add_child(http)
http.request_completed.connect(func(_result, code, _headers, body):
http.queue_free()
if code >= 400:
push_error("Crux get_top failed: HTTP %d" % code)
emit_signal("scores_received", [])
return
# The /entries/top response is a plain JSON array.
var scores = JSON.parse_string(body.get_string_from_utf8())
emit_signal("scores_received", scores if scores is Array else [])
)
http.request(_entries("/top?limit=%d" % limit), _headers(), HTTPClient.METHOD_GET)
Step 3 - swap your calls (2 minutes)
Replace the SilentWolf calls with the autoload. The shape is deliberately the same - a fire-and-forget submit and a signal that delivers the top scores:
# AFTER - Crux
func submit(player_name: String, score: int) -> void:
CruxLeaderboard.submit_score(player_name, score)
func load_top() -> void:
CruxLeaderboard.scores_received.connect(_on_scores)
CruxLeaderboard.get_top(10)
func _on_scores(scores: Array) -> void:
for row in scores:
print("#%d %s %d" % [row.rank, row.player_id, int(row.score)])
Each entry in the array has player_id, score, and rank - so a leaderboard UI that iterated SilentWolf's player_name / score needs only a field rename.
Direct call mapping
| SilentWolf | Crux equivalent |
|---|---|
SilentWolf.configure({api_key, game_id}) | The BASE / PROJECT / ENV / API_KEY / BOARD constants |
SilentWolf.Scores.persist_score(name, score) | CruxLeaderboard.submit_score(name, score) → POST .../leaderboards/{id}/entries/ |
SilentWolf.Scores.get_high_scores(n) | CruxLeaderboard.get_top(n) → GET .../leaderboards/{id}/entries/top?limit=n |
sw_scores_received signal | scores_received signal (Array of { player_id, score, rank }) |
Optional: the Crux Godot SDK, auth, and cloud saves
The raw HTTPRequest above is intentionally dependency-free and matches SilentWolf's "just an API key" model. If you also want player accounts and cloud saves - the other things SilentWolf offered - Crux ships a Godot 4 SDK addon (addons/crux) that wraps the same API, plus player auth (email, OAuth, or guest) and player-scoped JSON documents for save data. The full engine walkthrough is in the SDK quickstart, and there's broader context in what Godot indie devs actually use for multiplayer backends in 2026.
A word on cheating (and why it matters more now)
Like SilentWolf, the client-side flow above embeds an API key in your game, so a determined player can extract it and post fake scores. That is acceptable for casual and friends-list boards. For anything competitive, submit scores from a trusted place instead - a dedicated server or your own backend using a server token - and validate the run server-side. Crux supports exactly that split (server tokens and an admin auth mode), and the pattern is covered in stopping leaderboard cheating with server-side validation.
The real lesson: pick a backend you can leave
SilentWolf didn't break because it was free. It broke because when it went away, there was no export path and no notice - your data and your game's online features left with it. Whatever you migrate to, that is the property to insist on. With Crux, every score, player, and document is readable through the same open API you write with, so you can pull all of it out any time, and there's a written commitment to advance notice and an export window if the service ever winds down. The SDKs and API are open source too. Use a backend you could walk away from - that's the whole point of not getting caught twice.
Related reading
- What Godot indie devs actually use for multiplayer backends in 2026
- The best Godot backend in 2026: free options compared
- Will your game backend still be alive next year?
- Wiring a Godot 4 dedicated server to a real backend
- The 2026 game-backend shutdowns: who died and where studios went
- Crux SDK quickstart - curl, JavaScript, Godot, and Unity