docs/subsystems/settings.md
The page documents the Zulip settings system, and hopefully should help you decide how to correctly implement new settings you're adding to Zulip.
We have two types of administrative settings in Zulip:
Philosophically, the goals of the settings system are to make it convenient for:
Zulip uses the Django settings
system, which
means that the settings files are Python programs that set a lot of
variables with all-capital names like EMAIL_GATEWAY_PATTERN. You can
access these anywhere in the Zulip Django code using, for example:
from django.conf import settings
print(settings.EMAIL_GATEWAY_PATTERN)
Additionally, if you need to access a Django setting in a shell script (or just on the command line for debugging), you can use, for example:
$ ./scripts/get-django-setting EMAIL_GATEWAY_PATTERN
%s@localhost:9991
The DJANGO_SETTINGS_MODULE environment variable is set to
zproject.settings, which is what tells Django to effectively import
zproject.settings.* into django.conf.settings to make them
accessible there.
However, Zulip has separated those settings that we expect a system administrator to change (with nice documentation) from the ~1000 lines of settings needed by the Zulip Django app. As a result, there are a few files involved in the Zulip settings for server administrators. In a production environment, we have:
/etc/zulip/settings.py (the template is in the Zulip repo at
zproject/prod_settings_template.py) is the main system
administrator-facing settings file for Zulip. It contains all the
server-specific settings, such as how to send outgoing email, the
hostname of the PostgreSQL database, etc., but does not contain any
secrets (e.g., passwords, secret API keys, cryptographic keys, etc.).
The way we generally do settings that can be controlled with shell
access to a Zulip server is to put a default in
zproject/default_settings.py, and then override it here. As this
is the main documentation for Zulip settings, we recommend that
production installations carefully update /etc/zulip/settings.py
every major
release
to pick up new inline documentation.
/etc/zulip/zulip-secrets.conf (generated by
scripts/setup/generate_secrets.py as part of installation)
contains secrets used by the Zulip installation. These are read
using the standard Python
RawConfigParser,
and accessed in zproject/computed_settings.py by the get_secret
or get_mandatory_secret function defined in zproject/config.py.
All secrets/API keys/etc. used by the Zulip Django application should
be stored here. The secrets mandatory to start the Zulip Django app
are read using get_mandatory_secret and the others are read with
get_secret. If any of the mandatory secrets is missing, a
ZulipSettingsError is raised.
zproject/settings.py is the main Django settings file for Zulip.
It imports everything from zproject/configured_settings.py and
zproject/computed_settings.py.
zproject/configured_settings.py imports everything from
zproject/default_settings.py, then in a prod environment imports
/etc/zulip/settings.py via a symlink.
zproject/default_settings.py has the default values for the settings the
user would set in /etc/zulip/settings.py.
zproject/computed_settings.py contains all the settings that are
constant for all Zulip installations or computed as a function of
zproject/configured_settings.py (e.g., configuration for logging,
static assets, middleware, etc.).
In a development environment, we have zproject/settings.py, and
additionally:
zproject/dev_settings.py has the custom settings for the Zulip development
environment; these are set after importing prod_settings_template.py.
zproject/dev-secrets.conf replaces
/etc/zulip/zulip-secrets.conf, and is not tracked by Git. This
allows you to configure your development environment to support
features like authentication
options that require secrets to
work. It is also used to set certain settings that in production
belong in /etc/zulip/settings.py, e.g., SOCIAL_AUTH_GITHUB_KEY.
You can see a full list with git grep development_only=True, or
add additional settings of this form if needed.
If you need to override a setting in your development environment,
you can do so by creating a zproject/custom_dev_settings.py
setting the values you'd like to override (the test suites ignore
this file). This optional file is processed just after
dev_settings.py in configured_settings.py, so
zproject/computed_settings.py will correctly use your custom
settings when calculating any computed settings that depend on them.
zproject/test_settings.py imports everything from
zproject/settings.py and zproject/test_extra_settings.py.
zproject/test_extra_settings.py has the (default) settings used
for the Zulip tests (both backend and Puppeteer), which are applied on
top of the development environment settings.
When adding a new server setting to Zulip, you will typically add it in two or three places:
zproject/default_settings.py, with a default value
for production environments.
If the settings has a secret key,
you'll add a get_secret or get_mandatory_secret call in
zproject/computed_settings.py (and the
user will add the value when they configure the feature).
In an appropriate section of zproject/prod_settings_template.py,
with documentation in the comments explaining the setting's
purpose and effect.
Possibly also zproject/dev_settings.py and/or
zproject/test_settings.py, if the desired value of the setting for
Zulip development and/or test environments is different from the
default for production.
Most settings should be enabled in the development environment, to maximize convenience of testing all of Zulip's features; they should be enabled by default in production if we expect most Zulip sites to want those settings.
You can write tests for settings using, for example,
with self.settings(TERMS_OF_SERVICE=None). However, this only works
for settings which are checked at runtime, not settings which are only
accessed in initialization of Django (or Zulip) internals
(e.g., DATABASES). See the Django docs on overriding settings in
tests for more details.
Realm settings are preferred for any configuration that is a matter of organizational policy (as opposed to technical capabilities of the server). As a result, configuration options for user-facing functionality is almost always added as a new realm setting, not a server setting. The new feature tutorial documents the process for adding a new realm setting to Zulip.
So for example, the following server settings will eventually be replaced with realm settings:
NAME_CHANGES_DISABLEDINLINE_IMAGE_PREVIEWENABLE_GRAVATAR