diff --git a/changelog.d/13615.feature b/changelog.d/13615.feature
new file mode 100644
index 0000000000..c2c568f1eb
--- /dev/null
+++ b/changelog.d/13615.feature
@@ -0,0 +1 @@
+Change the default startup behaviour so that any missing "additional" configuration files (signing key, etc) are generated automatically.
diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md
index e9ab58854e..4c59e3dcf2 100644
--- a/docs/usage/configuration/config_documentation.md
+++ b/docs/usage/configuration/config_documentation.md
@@ -2139,6 +2139,9 @@ allows the shared secret to be specified in an external file.
The file should be a plain text file, containing only the shared secret.
+If this file does not exist, Synapse will create a new signing
+key on startup and store it in this file.
+
Example configuration:
```yaml
registration_shared_secret_file: /path/to/secrets/file
@@ -2555,7 +2558,10 @@ Config options relating to signing keys
---
### `signing_key_path`
-Path to the signing key to sign messages with.
+Path to the signing key to sign events and federation requests with.
+
+*New in Synapse 1.67*: If this file does not exist, Synapse will create a new signing
+key on startup and store it in this file.
Example configuration:
```yaml
diff --git a/synapse/config/_base.py b/synapse/config/_base.py
index 7c9cf403ef..1f6362aedd 100644
--- a/synapse/config/_base.py
+++ b/synapse/config/_base.py
@@ -20,6 +20,7 @@ import logging
import os
import re
from collections import OrderedDict
+from enum import Enum, auto
from hashlib import sha256
from textwrap import dedent
from typing import (
@@ -603,18 +604,44 @@ class RootConfig:
" may specify directories containing *.yaml files.",
)
- generate_group = parser.add_argument_group("Config generation")
- generate_group.add_argument(
+ # we nest the mutually-exclusive group inside another group so that the help
+ # text shows them in their own group.
+ generate_mode_group = parser.add_argument_group(
+ "Config generation mode",
+ )
+ generate_mode_exclusive = generate_mode_group.add_mutually_exclusive_group()
+ generate_mode_exclusive.add_argument(
+ # hidden option to make the type and default work
+ "--generate-mode",
+ help=argparse.SUPPRESS,
+ type=_ConfigGenerateMode,
+ default=_ConfigGenerateMode.GENERATE_MISSING_AND_RUN,
+ )
+ generate_mode_exclusive.add_argument(
"--generate-config",
- action="store_true",
help="Generate a config file, then exit.",
+ action="store_const",
+ const=_ConfigGenerateMode.GENERATE_EVERYTHING_AND_EXIT,
+ dest="generate_mode",
)
- generate_group.add_argument(
+ generate_mode_exclusive.add_argument(
"--generate-missing-configs",
"--generate-keys",
- action="store_true",
help="Generate any missing additional config files, then exit.",
+ action="store_const",
+ const=_ConfigGenerateMode.GENERATE_MISSING_AND_EXIT,
+ dest="generate_mode",
)
+ generate_mode_exclusive.add_argument(
+ "--generate-missing-and-run",
+ help="Generate any missing additional config files, then run. This is the "
+ "default behaviour.",
+ action="store_const",
+ const=_ConfigGenerateMode.GENERATE_MISSING_AND_RUN,
+ dest="generate_mode",
+ )
+
+ generate_group = parser.add_argument_group("Details for --generate-config")
generate_group.add_argument(
"-H", "--server-name", help="The server name to generate a config file for."
)
@@ -670,11 +697,12 @@ class RootConfig:
config_dir_path = os.path.abspath(config_dir_path)
data_dir_path = os.getcwd()
- generate_missing_configs = config_args.generate_missing_configs
-
obj = cls(config_files)
- if config_args.generate_config:
+ if (
+ config_args.generate_mode
+ == _ConfigGenerateMode.GENERATE_EVERYTHING_AND_EXIT
+ ):
if config_args.report_stats is None:
parser.error(
"Please specify either --report-stats=yes or --report-stats=no\n\n"
@@ -732,11 +760,14 @@ class RootConfig:
)
% (config_path,)
)
- generate_missing_configs = True
config_dict = read_config_files(config_files)
- if generate_missing_configs:
- obj.generate_missing_files(config_dict, config_dir_path)
+ obj.generate_missing_files(config_dict, config_dir_path)
+
+ if config_args.generate_mode in (
+ _ConfigGenerateMode.GENERATE_EVERYTHING_AND_EXIT,
+ _ConfigGenerateMode.GENERATE_MISSING_AND_EXIT,
+ ):
return None
obj.parse_config_dict(
@@ -965,6 +996,12 @@ def read_file(file_path: Any, config_path: Iterable[str]) -> str:
raise ConfigError("Error accessing file %r" % (file_path,), config_path) from e
+class _ConfigGenerateMode(Enum):
+ GENERATE_MISSING_AND_RUN = auto()
+ GENERATE_MISSING_AND_EXIT = auto()
+ GENERATE_EVERYTHING_AND_EXIT = auto()
+
+
__all__ = [
"Config",
"RootConfig",
|