phantom
Documentation menu

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.toml

Where settings come from

Sources are layered, later ones winning:

  1. the file named by $PHANTOM_CONFIG
  2. files passed with -c
  3. environment variables prefixed PHANTOM_, with __ separating nested keys
  4. -O key=value overrides on the command line
PHANTOM_CONFIG=/etc/phantom/phantom.toml PHANTOM_PORT=8008 ./phantom-server

An 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

OptionCovers
address, portwhere it listens (default localhost:8008)
tls.certs, tls.keyserve HTTPS directly instead of behind a TLS-terminating proxy
allow_registration, registration_tokenwho may sign up
allow_federation, trusted_serverstalking to other servers
database_backup_path, database_backups_to_keepbackups, which the console can start
create_admin_room, admin_room_tag, admin_room_noticesthe admin room on a fresh database
default_room_versionthe version of rooms created without one, 1–12 (default 11)
sms.gateway_url, sms.gateway_token, sms.messagetexting the codes that verify phone numbers
logthe 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.