The database

"Everything anyone ever built here is in one file. Mind the step." — Archivist Yenna Tal

Every account, character, Force rank and skill on your server lives in a single SQLite file called storyforce.db.

Treat that file and its backups as sensitive. Passwords are stored as salted hashes rather than plain text, but a copy of the database is still a copy of everyone's credentials.


What it does

The game module carries its own copy of SQLite. There is no database server to install, no port to open and no separate service to keep running. The file is created on first boot and opened again on every map load.

That also means the file is the whole of your server's state. Lose it and every character is gone. Copy it and you have taken everything with you, which is why the file matters more than any other on the machine.


Where it lives

The database sits in your server's writable folder:

<fs_homepath>/StoryForce/db/storyforce.db

Under Docker that resolves to /opt/openjk/homepath/StoryForce/db/storyforce.db, inside the volume you mapped, so it survives an image update.

The db folder does not exist in a fresh install. The game module creates it on first boot by writing a marker file into it. SQLite will create a database file but never the folder holding it. If that folder cannot be created, the account system disables itself and says so in the console.

Backups land in db/backups/ alongside it. See backups and D1 sync.


What is in it

2 tables, plus one index.

sf_accounts is the login. One row per account, holding the username, the salted password hash, the membership state and the admin level. 3 more columns record when the account was made, when it last logged in, and which character it selects by default. Usernames are unique and matched without regard to case, so Rhen and rhen cannot both exist.

The row ids matter more than usual. Row 1 is the seed account, and after the first boot the server finds its leadership account that way rather than by name. If row 1 is missing from a hand-edited database, the server prints a warning and quietly turns off both seed account protection and community characters.

sf_characters is everything a player made. One row per character, keyed on the character id and pointing back at the account that owns it. Roughly 50 columns cover 4 kinds of thing:

  • identity, including the name, listed name, pronouns, status, faction and rank
  • progression, including XP, Force powers, powers bought and skills
  • appearance, including model, scale, saber setup and the 4 attachment slots
  • state, including whether the character is alive, their spawn loadout and their comlink setting

Force powers and skills are stored as fixed-length digit strings, one digit per power, rather than as separate columns. Reading them by eye is possible but not pleasant.

A third table, d1_sync_state, appears only if you turn on the Cloudflare mirror. It holds fingerprints of already-pushed rows and no player data.


The second file next to it

The database runs in write-ahead logging mode, so you will usually see a storyforce.db-wal file beside it. That sidecar holds recent writes that have not yet been folded back into the main file.

The server folds it back and truncates it every time the database closes, which happens at every map change, so it should stay small. A large or growing sidecar means the server has not shut down cleanly in a while.

Copy both files together, or better, do not copy them by hand at all. The backup system snapshots a running database safely and gives you one consistent file.


Schema changes

There is no migration system. Every table is created with a check for whether it already exists, and that check is the only thing that runs on an existing database.

The practical consequence is that a StoryForce update adding a column to the schema does not add that column to a database that already exists. New installs get the new shape. Old installs keep the old one, and any feature depending on the new column fails until somebody alters the table by hand.

Take a backup before updating, and read the release notes for any note about the schema.


Things to note

  • Deleting the database resets the server to a fresh install, including seeding a new leadership account from the seed settings.
  • The seed account cannot be removed through any in-game command, and the server expects it at row 1.
  • Accounts are never hard-deleted, only disabled, which is what keeps row ids stable.
  • Hand-editing the file is possible but risky. Stop the server first, because a live server holds it open with pending writes in the sidecar.
  • Every map change opens and closes the database. A file lock held by another program can stop the server loading the map.
  • The file is only as protected as the machine. Anyone who can read the folder can take the whole database, so restrict access to the server's writable folder.
  • A console recovery command exists for a locked-out leadership account. It is reachable from the server console and over rcon, which is one more reason to set an rcon password.

Next