Bezgelor is free software (GNU AGPL v3), and anyone can run a copy: for themselves, for friends on a home network, or as a public server. This page is the shortest path from a fresh copy of the code to logging in with the game client and making yourself an administrator. Admin commands, the next page, lists everything an administrator can type in the game.
Bezgelor is a work in progress. Before you start, see Where we stand, which lists what works today, zone by zone.
The steps below come from the repository's own README, AGENTS.md and the deployment notes, which have more detail.
What you need
- Your own WildStar client, the final live build (16042). Bezgelor ships no client files and no game data.
-
Erlang 28 and Elixir 1.19. The exact versions are pinned in
mise.toml;mise installsets them up. -
PostgreSQL 14 or newer.
docker compose up -dstarts one on port 5433; any other works through thePOSTGRES_*settings below. - Python 3.8 or newer, standard library only, for the extractors.
- Memory: the whole server uses about 1.8 GB idle with the open-world zones started; players and dungeon instances add to that. A public server wants 8 GB or more.
1. Get the code
git clone https://github.com/Bezgelor/bezgelor.git
cd bezgelor
mise install
2. Extract the game data from your client
The public repository carries the extractors, not the client's data. The creatures, items, spells, zones and text the server needs are read out of your own installation with one command (a few minutes):
python3 -I tools/extract/extract_all.py --client-dir "/path/to/WildStar/Patch" --out ~/.local/share/bezgelor/data
The server reads that folder by default; set BEZGELOR_DATA_DIR to use another. The result is the game's own
data, so keep it to yourself as you keep the client. Terrain heights are optional and extracted separately
(--terrain, read from BEZGELOR_TERRAIN_DIR); without them the server still runs, with no ground heights to
check positions against. tools/extract explains every option.
3. Configure
Settings come from environment variables. The ones that matter for a server of your own:
| Variable | Default | What |
|---|---|---|
WORLD_PUBLIC_ADDRESS |
127.0.0.1 | The IPv4 address players' clients reach the world server on (an address, not a name) |
POSTGRES_HOST, POSTGRES_PORT |
localhost, 5433 | Where the database is |
POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB |
bezgelor, bezgelor_dev, bezgelor_dev | The database's user, password and name |
BEZGELOR_DATA_DIR |
~/.local/share/bezgelor/data |
The data you extracted |
BEZGELOR_TERRAIN_DIR |
~/.local/share/bezgelor/terrain |
The terrain heights, if you made them |
REALM_NAME |
Bezgelor | The realm's name in the client's realm list (used by a release's seeding) |
AUTH_PORT, REALM_PORT, WORLD_PORT |
6600, 23115, 24000 | The game servers' ports |
STOREFRONT_MODE |
paid |
free shows every store price as 0 |
DISCORD_INVITE_URL |
unset | A Discord invite shown on the website; unset hides the Discord links |
WRITE_BEHIND, WRITE_BEHIND_ASYNC_COMMIT |
on, on | How player progress is saved; see Stopping the server |
The world's public address. The client first logs in on port 6600, then the realm server tells it which
address to find the world server on. That address is WORLD_PUBLIC_ADDRESS, and it is written into the realm
list when the database is seeded. If the client runs on the same machine, the default is right. Otherwise set it
to the server's address before step 4. If you change it later, seed again:
cd apps/bezgelor_db && mix run priv/repo/seeds.exs.
Ports.
| Port | What | Open it to |
|---|---|---|
| 6600 | Login server | Players (TCP) |
| 23115 | Realm server | Players (TCP) |
| 24000 | World server | Players (TCP) |
| 4000 | The website: accounts, the admin pages | Players, or put a web server in front of it |
| 5433 | PostgreSQL | Nobody: keep it on the machine |
4. Create the database and start the servers
./scripts/setup.sh # PostgreSQL in Docker, then mix deps.get and mix ecto.setup
./scripts/start.sh # every server and the website, with an interactive shell
With your own PostgreSQL instead of Docker, set the POSTGRES_* variables, then run
mix deps.get && mix ecto.setup, and start with iex -S mix phx.server. mix ecto.setup creates the database,
runs the migrations and seeds the permissions, the roles and the realm list. The development seeds add a second
realm, Mikros, for testing; nothing answers on it, so pick Bezgelor.
./scripts/stop.sh stops the servers, and the log is in logs/dev.log.
5. Make an account
Open http://<server>:4000/register and sign up with an email address and a password. In the development
setup no email is sent; the verification message waits at http://<server>:4000/dev/mailbox. The link in it
starts with http://localhost:4000; from another machine, put your server's address in its place.
You do not have to verify the email to play. Only the website asks for it; the game login (STS), the realm
server and the world server never look at it, so an account that has not been verified can log in with the client
and make characters. If you run a release without working mail delivery, people can play all the same; to mark an
account verified by hand, run UPDATE accounts SET email_verified_at = now() WHERE email = '...'; in psql.
6. Point the client at your server
On Windows, run this command on the machine with the client, from its Client64 folder, with your server's
address in place of 127.0.0.1 when it runs elsewhere. On Linux, Steam Deck or macOS, the same command line is
started through Proton or Wine: see the Linux, Steam Deck and
macOS client guides. Their launch scripts take the server's address as their argument.
WildStar64.exe /auth 127.0.0.1 /authNc 127.0.0.1 /lang en /patcher 127.0.0.1 /SettingsKey WildStar /realmDataCenterId 9
Log in with the email and password of your account, pick the realm and make a character.
7. Make yourself an administrator
In the shell that ./scripts/start.sh opened, with your account's email:
BezgelorDb.Release.grant_role("you@example.com", "Super Admin")
That gives your account every permission: every in-game admin command, every admin page, and giving roles to
other people. The website's admin pages (/admin) also ask you to turn on 2-Step Verification first (Settings).
For the people who help you run the server, pick a smaller role: Moderator (chat, mutes, kicks, bans and
reports), Game Master (helps players: teleports, levels, quests) or Senior GM (also currency, items and
events), or make your own. Roles and permissions explains the roles,
how to give one to someone else, how to make your own, and the development switch that makes everyone an admin.
The Field Kit add-on (optional)
The Bezgelor Field Kit is an add-on for the client: a bug report wizard for every player, and a quest setup
and quick action panel for testers. Your server already speaks to it; there is nothing to configure. Your
website serves it at /downloads/field-kit.zip, zipped from addons/BezgelorFieldKit when the portal is
built, and the Field Kit guide tells Windows players how to install it (unzip into
%APPDATA%\NCSOFT\WildStar\Addons; the add-on has not yet been tested on Linux, Steam Deck or macOS). To hand the zip out another way, mix bezgelor.field_kit.zip --out dist/
writes it to a folder. The client loads it with no setting, and /fk opens it. Players may ask why the Addons
entry of the options menu is greyed out at the login screen: the stock client enables it only in the world (Esc
there), whatever the server.
It talks to the server over the client's own add-on channels (ICComm), not chat, so nothing shows in anyone's
chat tabs. It is optional: every request it sends stands for a /c command, needs that command's permission
(admin commands) and writes the same audit log entry. A player without the add-on types
the commands instead. The design notes describe the protocol.
Development or a release
./scripts/start.sh runs the development setup: the code reloads as it changes, errors show their details in
the browser, and every account can use the admin commands (gm_commands_for_all: true in config/dev.exs).
That is right for trying Bezgelor yourself and wrong for a server strangers play on.
For a server other people use, build a release (mix release, production settings). In a release the admin
switch is off, and the world server refuses to start if it is turned on (a private test server can set
BEZGELOR_ALLOW_GM_FOR_ALL=1 to allow it; see admin commands); it reads its secrets from the environment and refuses to start without the ones it needs: DATABASE_URL,
SECRET_KEY_BASE, CLOAK_KEY, PHX_HOST, WORLD_PUBLIC_ADDRESS and RESEND_API_KEY.
deploy/env.example lists every setting. The release carries no client data: copy
your extracted folder to the server and point BEZGELOR_DATA_DIR at it. The
deployment notes describe the scripts we use for our own server (one Ubuntu 24.04
machine, Caddy in front of the website, nightly database dumps) and, at the end, how to build and run a release
anywhere else.
Two things are not finished for other hosts yet. A release sends its account emails through Resend only, and needs a Resend key even if you would rather use another mail service. The deployment scripts assume Ubuntu 24.04; elsewhere you build and run the release yourself.
Stopping the server and what a crash costs
Loot, experience, credits and quest progress reach the player first and are saved to the database a moment later, by one writer per character: a slow disk then slows the saving, not the game. Until a change is saved it waits in a queue in the server's memory. A clean stop saves the whole queue; killing the program loses it.
Stop it properly. With our deployment scripts, systemctl stop bezgelor@<instance> (or restart). For a release you
run yourself, bin/bezgelor stop. In development, ./scripts/stop.sh, or System.stop() in the server's
shell. Each of these logs every player out and saves everything queued before the database connection closes.
It usually takes a few seconds; with a slow disk it can take longer, and systemd waits up to three minutes
(TimeoutStopSec), which covers the slowest case. Let it finish.
Never kill -9 the server, and don't end the development shell with Ctrl+C twice or Ctrl+C then a:
those end the program at once, without saving the queue. A normal reboot is fine: systemd stops the service
first and waits for it.
What a crash costs players. If the program dies anyway (killed, out of memory, the machine loses power),
players lose what was still queued: normally less than a second of loot, experience and quest progress. If
PostgreSQL or the machine itself crashes, up to about the last half second of saved progress can go too, because
those saves don't wait for the disk (WRITE_BEHIND_ASYNC_COMMIT); the database is never corrupted by it. Trades,
mail, the marketplace, vendors, crafting and quest turn-ins always save first and wait for the disk, so a crash
can roll a player back a little but never duplicates an item or credits.
Watching it. The admin metrics page (/admin/metrics, permission server.view_metrics) has a "Saving
player progress" card: how many saves are queued and for which characters, how old the oldest is, how far
behind the saving was over the last five minutes, retries, and dropped saves. The health check
(http://127.0.0.1:4002/health) reports the same in its write_behind section, and the server is reported
"degraded" while the saving lags more than five seconds or a save was dropped in the last ten minutes.
A dropped save is one that failed five times in a row and was given up: the player was told about a change
(an item, experience, a quest step) that the database never got, and it will be missing at their next login.
The log line starts with [WriteBehind] and says what it was, for which character, and why (most often the
database was unreachable or the disk full). Fix the cause; if it matters to the player, give the item or
progress back with an admin command.
Settings (in /etc/bezgelor/<instance>.env, or the environment):
| Variable | Default | What |
|---|---|---|
WRITE_BEHIND |
on |
off saves each change before the player is told, as older versions did: nothing waits in a queue, but a slow disk makes loot and experience slow to arrive |
WRITE_BEHIND_ASYNC_COMMIT |
on |
off makes the queued saves wait for the disk: a crash of PostgreSQL or the machine then loses none of them, at some cost in speed |
When something goes wrong
Type /c bug in the game, or open an issue with what you did and
what happened. The server's log (logs/dev.log in development, journalctl -u bezgelor@<instance> for our deployment
scripts) usually says why a client was turned away.