Configuration
phantom reads TOML, with every option under one [global] section. Two must be set:
[global]
server_name = "phantom.chat"
database_path = "/var/lib/phantom"The one rule
phantom-example.toml is generated — don't edit it. Every cargo build rewrites it from the doc comments on the Config structs in crates/phantom-core/src/runtime/config/. To configure a server, copy it and edit the copy:
cp phantom-example.toml phantom.tomlWhere settings come from
Sources are layered, later ones winning:
- the file named by
$PHANTOM_CONFIG - files passed with
-c - environment variables prefixed
PHANTOM_, with__separating nested keys -O key=valueoverrides on the command line
PHANTOM_CONFIG=/etc/phantom/phantom.toml PHANTOM_PORT=8008 ./phantom-serverAn unknown key is logged as a warning, not rejected, so a typo shows up rather than vanishing.
Validation
Some values are refused at startup: an empty or invalid server_name, no bind address, a malformed log filter, a default_room_version phantom can't run (it supports 1 to 11), tls.certs set without tls.key or the other way round. Risky but legal ones — insecure TLS, deprecated keys — are logged as warnings.
Reloading
With config_reload_signal on (the default), SIGUSR1 re-reads the config from the same files and overrides the server started with; so do the console's reload config action and !admin server reload. Only server_name is fixed: a reload that changes it is refused.
SIGUSR1 also makes the listeners re-read the TLS certificate and key, even with config_reload_signal off, so a renewed certificate takes effect without a restart. They re-read the paths they started with, so moving the files takes a restart, and a certificate that fails to load leaves the old one in use.
Phone numbers
Users can add a phone number to their account, and use it to reset their password, once [global.sms] names a gateway. phantom texts a six-digit code through it, and the client sends the code back to /_phantom/3pid/msisdn/submitToken. well_known_client must be set too, because that URL is built from it.
[global.sms]
gateway_url = "https://sms-relay.example.com/send"
gateway_token = "…" # sent as Authorization: Bearer …
message = "Your {server_name} verification code is {code}"For each code phantom POSTs {"to": "+15551234567", "message": "…"} and treats any 2xx answer as sent. Point it at a provider that accepts that request, or at a small relay that translates it. Codes expire after fifteen minutes, and five wrong guesses end the session.
Useful options
| Option | Covers |
|---|---|
address, port | where it listens (default localhost:8008) |
tls.certs, tls.key | serve HTTPS directly instead of behind a TLS-terminating proxy |
allow_registration, registration_token | who may sign up |
allow_federation, trusted_servers | talking to other servers |
database_backup_path, database_backups_to_keep | backups, which the console can start |
create_admin_room, admin_room_tag, admin_room_notices | the admin room on a fresh database |
default_room_version | the version of rooms created without one, 1–12 (default 11) |
sms.gateway_url, sms.gateway_token, sms.message | texting the codes that verify phone numbers |
log | the log filter, such as info or phantom=debug |
The generated file documents the hundred or so others.
Adding an option
Add a field to the config struct, with its documentation as the doc comment and a /// default: <value> line, then run a real cargo build and commit the regenerated phantom-example.toml. Mark secrets display: sensitive, so the console's settings and the startup summary mask them.