Skip to main content

Configuration Interfaces

Four things configure synapse, and they are not interchangeable:

  1. config.yaml - the full schema, documented on Configuration.
  2. Command-line flags - a subset of the schema, plus a handful of one-shot commands.
  3. Environment variables - a different subset, plus variables that exist nowhere in the YAML.
  4. The package you installed - not every build contains every subsystem. A key for a subsystem your package does not carry still parses, and then does nothing.

This page covers how the first three combine, lists all of them, and ends with what each package actually contains.

Verified against 0.8.8

Every flag, variable, default and behaviour here was read out of the v0.8.8 source. The counts are exact for that release: 84 flags, of which 63 write a configuration key; 129 environment variable names in the merge path, of which 116 are canonical and 13 are fallbacks; and 51 further variables read at runtime outside the merge. The four per-signal rows under OpenTelemetry stand for three names each.

Which source wins​

Configuration is assembled in three steps, in this order:

  1. the YAML file is parsed (--config, or built-in defaults if no file is given);
  2. command-line flags are merged over it;
  3. environment variables are applied over that.

Step 3 assigns unconditionally. So the order of precedence is:

environment variable → command-line flag → config.yaml → built-in default.

The environment beats the command line

This is the opposite of the usual convention, and the opposite of what synapse's own in-repo docs/CONFIGURATION.md says. If a flag appears to be ignored, look for the environment variable that writes the same key - a stray FIREWALL_MODE in a unit file or a container spec silently wins over --firewall-mode on the command line.

There is no diagnostic for this. Nothing logs that an environment variable replaced a flag's value.

Two settings do not follow that order.

Exception 1: the log level​

The log level is resolved separately, and the flag wins:

--log-level → logging.level (YAML, or LOGGING_LEVEL over it) → RUST_LOG → info.

RUST_LOG is only consulted when neither the flag nor logging.level is set, and only a bare level is accepted: error, warn, info, debug, trace, off. A per-module directive such as RUST_LOG=synapse=debug,hyper=warn is not passed through - it does not match one of the six names, so the level degrades to info with no warning.

Synapse then overwrites RUST_LOG in its own environment with the level it resolved, so anything reading the variable later sees the resolved value rather than what you set.

Exception 2: the event and control sockets​

daemon.event_socket and daemon.control_socket in the YAML win over --event-socket and --control-socket in the process that serves those sockets - the daemon. The flags only apply there when the YAML leaves the key unset, and neither key has an environment variable.

The client side does not consult the YAML at all. synapse --terminal connects to whatever --event-socket says, default included, so moving the socket with daemon.event_socket alone leaves the terminal looking at the old path. Pass --event-socket to the terminal as well, or keep the socket where the default puts it.

Three flags overwrite the YAML even when you do not pass them​

Most flags are merged only when you actually pass them. These three are assigned unconditionally, so the flag's own default lands on top of whatever the YAML said:

FlagKey it overwritesValue written when the flag is absent
--arxignis-include-response-bodyplatform.include_response_bodyfalse
--arxignis-max-body-sizeplatform.max_body_size1048576
--daemon-chown-pid-filedaemon.chown_pid_filetrue

Setting any of these three in config.yaml has no effect. Use the flag or, where one exists, the environment variable.

proxy.redis.prefix cannot be set from the YAML​

Seven flags are merged only when their value differs from a sentinel, which is how a flag with a built-in default avoids clobbering the YAML. For six of them the sentinel is the flag's own default and the mechanism works. For --redis-prefix the two disagree: the flag defaults to g0s:synapse and the comparison is against ax:synapse, so the test always passes and proxy.redis.prefix from the YAML is always replaced by g0s:synapse.

To use a different prefix, pass --redis-prefix or set REDIS_PREFIX. Both work.

What a bad value does​

An environment variable that cannot be parsed into the key's type is handled in one of four ways. All four are silent for all but three variables:

BehaviourWhat happensVariables
used as giventhe string is stored unvalidated55
built-in defaultthe key is reset to the code default, discarding the YAML value35
ignoredthe assignment is skipped and the YAML value survives20
clearedthe key is set to "unset", discarding the YAML value6

IDS_MAX_FLOWS=lots resets ids.max_flows to the code default even if the YAML set it; CAPTURE_ENABLED=yes is ignored entirely, because only true and false parse as a boolean; GEOIP_COUNTRY_HEADERS=junk clears platform.geoip.country.headers, so a YAML header map is dropped rather than kept. The Unparseable value column in the tables below says which applies to each variable.

Only three variables log anything when their value does not parse, and the tables mark them "and it warns": CAPTURE_MODE and IDS_CAPTURE_MODE, which warn and then use the built-in default, and NETWORK_IP_VERSION, which warns and then changes nothing. Its message claims it is "using default 'both'", but the code makes no assignment, so the YAML value actually survives.

A misspelt FIREWALL_MODE silently disables what you configured

firewall.mode is one of the 35. Any value that is not auto, xdp, nftables, iptables or none resets the key to auto, with nothing logged - so FIREWALL_MODE=xpd in a unit file replaces a deliberate xdp or none in your config.yaml with mode selection you did not ask for. The same shape applies to capture.mode and ids.capture_mode, which at least warn.

Command-line flags​

synapse --help lists these. 84 flags exist; 63 of them write a configuration key.

Flags that write a configuration key​

The key is the YAML path on Configuration, which is where its meaning is documented. "Overrides the YAML" says when the merge applies the flag.

FlagValueFlag defaultYAML key it writesOverrides the YAML
--redis-urlstringredis://127.0.0.1/0proxy.redis.urlunless it is redis://127.0.0.1/0
--redis-prefixstringg0s:synapseproxy.redis.prefixunless it is ax:synapse
--ifacestringautonetwork.ifaceunless it is auto
--ifaceslist—network.ifaceswhen non-empty
--arxignis-api-keystring—platform.api_keywhen passed
--arxignis-base-urlstringhttps://api.gen0sec.com/v1platform.base_urlunless it is https://api.gen0sec.com/v1
--arxignis-telemetry-sending-enabled
also accepted: --arxignis-log-sending-enabled
true/false—platform.telemetry_sending_enabledwhen passed
--arxignis-include-response-bodyswitchfalseplatform.include_response_bodyalways, even when absent
--arxignis-max-body-sizeinteger1048576platform.max_body_sizealways, even when absent
--firewall-modestring—firewall.modewhen passed
--captcha-site-keystring—proxy.captcha.site_keywhen passed
--captcha-secret-keystring—proxy.captcha.secret_keywhen passed
--captcha-jwt-secretstring—proxy.captcha.jwt_secretwhen passed
--captcha-providerhcaptcha/recaptcha/turnstile/prosopo—proxy.captcha.providerwhen passed
--proxy-protocol-enabledswitchfalseproxy.protocol.enabledwhen given
--proxy-protocol-timeoutinteger1000proxy.protocol.timeout_msunless it is 1000
-d, --daemonswitchfalsedaemon.enabled → truewhen given
--daemon-pid-filestring/var/run/synapse.pid (Linux), empty (Windows)daemon.pid_fileunless it is the built-in default
--daemon-working-dirstring/ (Linux), C:\ProgramData\Gen0Sec\Synapse (Windows)daemon.working_directoryunless it is the built-in default
--daemon-userstring—daemon.userwhen passed
--daemon-groupstring—daemon.groupwhen passed
--daemon-chown-pid-fileswitchtruedaemon.chown_pid_filealways, even when absent
--modestring—modewhen passed
--network-ip-versionstring—network.ip_versionwhen passed
--threat-mmdb-urlstring—platform.threat.urlwhen passed
--threat-mmdb-pathstring—platform.threat.pathwhen passed
--threat-mmdb-refresh-secsinteger—platform.threat.refresh_secswhen passed
--file-logging-enabledtrue/false—logging.file_logging_enabledwhen passed
--log-directorystring—logging.log_directorywhen passed
--max-log-sizeinteger—logging.max_log_sizewhen passed
--log-file-countinteger—logging.log_file_countwhen passed
--syslog-enabledtrue/false—logging.syslog.enabledwhen passed
--syslog-facilitystring—logging.syslog.facilitywhen passed
--syslog-identifierstring—logging.syslog.identifierwhen passed
--proxy-address-httpstring—proxy.address_httpwhen passed
--proxy-address-tlsstring—proxy.address_tlswhen passed
--proxy-certificatesstring—proxy.certificateswhen passed
--proxy-tls-gradestring—proxy.tls_gradewhen passed
--proxy-default-certificatestring—proxy.default_certificatewhen passed
--upstream-confstring—proxy.upstream.confwhen passed
--upstream-healthcheck-methodstring—proxy.upstream.healthcheck.methodwhen passed
--upstream-healthcheck-intervalinteger—proxy.upstream.healthcheck.intervalwhen passed
--geoip-country-urlstring—platform.geoip.country.urlwhen passed
--geoip-country-pathstring—platform.geoip.country.pathwhen passed
--geoip-asn-urlstring—platform.geoip.asn.urlwhen passed
--geoip-asn-pathstring—platform.geoip.asn.pathwhen passed
--geoip-city-urlstring—platform.geoip.city.urlwhen passed
--geoip-city-pathstring—platform.geoip.city.pathwhen passed
--geoip-refresh-secsinteger—platform.geoip.refresh_secswhen passed
--acme-enabledtrue/false—proxy.acme.enabledwhen passed
--acme-portinteger—proxy.acme.portwhen passed
--acme-emailstring—proxy.acme.emailwhen passed
--acme-storage-pathstring—proxy.acme.storage_pathwhen passed
--acme-storage-typestring—proxy.acme.storage_typewhen passed
--acme-developmenttrue/false—proxy.acme.developmentwhen passed
--acme-redis-urlstring—proxy.acme.redis_urlwhen passed
--content-scanning-enabledtrue/false—proxy.content_scanning.enabledwhen passed
--content-scanning-clamav-serverstring—proxy.content_scanning.clamav_serverwhen passed
--content-scanning-max-file-sizeinteger—proxy.content_scanning.max_file_sizewhen passed
--internal-services-enabledtrue/false—proxy.internal_services.enabledwhen passed
--internal-services-portinteger—proxy.internal_services.portwhen passed
--internal-services-bind-ipstring—proxy.internal_services.bind_ipwhen passed
-t, --terminalswitchfalsedaemon.enabled → falsewhen given
One flag is not in every build

--captcha-provider exists only in builds that carry the proxy, so it is absent from synapse --help on Windows and on the legacy agent. Every other flag is present everywhere, whether or not the subsystem behind it is.

Flags that do not write a configuration key​

FlagValueDefaultWhat it does
-c, --configpath—The YAML file to load. Without it, synapse starts from built-in defaults plus flags and environment.
--security-rules-configpathsecurity_rules.yamlLocal access and WAF rules, used when no Gen0Sec API key is configured.
--log-levelerror / warn / info / debug / trace—Sets the log level. Beats logging.level and RUST_LOG - see Exception 1. The flag has no off; only logging.level, LOGGING_LEVEL and RUST_LOG accept it.
--event-socketpath/var/run/synapse-events.sock (Linux), C:\ProgramData\Gen0Sec\Synapse\events.sock (Windows)Where the daemon serves events for terminal mode, and where --terminal connects. daemon.event_socket wins over it in the daemon only - see Exception 2.
--control-socketpath/var/run/synapse-control.sock (Linux), C:\ProgramData\Gen0Sec\Synapse\control.sock (Windows)Where the daemon serves control commands. daemon.control_socket wins over it in the daemon only - see Exception 2.
--serviceinstall / uninstall—Windows only: register or remove the Windows service, then exit.
--upgradeswitch—Signal the running synapse to replace its binary in place, then exit. Linux only; on Windows the flag is accepted and synapse starts normally instead. It needs a build that carries graceful restart, which the legacy agent does not: there the signal has no handler and stops the process rather than upgrading it.
--show-firewall-rulestext / json—Print the rules the kernel is actually enforcing, read out of the pinned maps, then exit. Read-only, needs root, and does not need synapse to be running.
--firewall-pin-rootpath/sys/fs/bpf/hillockWhere to look for those pinned maps. Only meaningful with --show-firewall-rules.
--force-stopswitch—Stop synapse and remove its kernel datapath - attached programs and pinned maps outlive the process, so a crashed agent leaves enforcement running with nothing owning it. Needs root; safe to run twice.
--force-stop-dry-runswitch—Print what --force-stop would remove, and remove nothing.
--clear-certificatehost name—Remove one certificate from local storage and from Redis, then exit. Needs a build that carries the proxy; on Windows and on the legacy agent the flag is accepted and synapse starts normally instead.
-T, --tcpdumpswitchfalseNon-interactive packet output instead of the terminal view. Mutually exclusive with --terminal.
-r, --replyswitchfalseIn terminal mode, correlate SYN with SYN-ACK.
--block-modeswitchfalseIn terminal and replay runs, block what is classified as a threat and add the source address to the runtime blocklist.
--pcap-filepath—Replay a capture file through the full pipeline: same fingerprints, classification and rule evaluation as live traffic. Not supported with --daemon.
--pcap-limitinteger0Stop after this many packets. 0 reads the whole file.
--pcap-startYYYY-MM-DD or YYYY-MM-DD HH:MM:SS (UTC)—Skip packets before this timestamp.
--pcap-endYYYY-MM-DD or YYYY-MM-DD HH:MM:SS (UTC)—Skip packets after this timestamp.
--captcha-token-ttlinteger7200Nothing. Parsed and never read.
--captcha-cache-ttlinteger300Nothing. Parsed and never read.
--disable-xdp does not exist

Synapse's in-repo docs/CONFIGURATION.md lists a --disable-xdp flag. There is no such flag in the code, at this release or any other. To not attach XDP, set firewall.mode: none or pick a non-XDP capture.mode.

Environment variables​

The deprecated prefixes​

Every variable in the merge path is looked up unprefixed first. If that is unset, a deprecated prefixed form is tried, and using one logs a deprecation warning naming the variable to move to:

  • all variables: NAME, then AX_NAME.
  • the ten platform variables marked ◆ below: NAME, then AX_NAME, then AX_ARXIGNIS_NAME, then ARXIGNIS_NAME.

An unset variable and an empty one are different: an empty value is a value, and it is assigned.

Variables that override a configuration key​

116 variables, listed by the section of config.yaml they write. 13 further names are fallbacks, shown under the variable they stand in for. The key's meaning is documented on Configuration; this table only maps the variable onto the key.

Mode and threading​

VariableYAML keyValueUnparseable value
MODEmodestringused as given
MULTI_THREADmulti_threadtrue / falseignored
WORKER_THREADSworker_threadsintegerignored

Network​

VariableYAML keyValueUnparseable value
NETWORK_IFACEnetwork.ifacestringused as given
NETWORK_IFACESnetwork.ifacescomma-separated listused as given
NETWORK_IP_VERSIONnetwork.ip_versionipv4 / ipv6 / bothignored, and it warns

Firewall​

VariableYAML keyValueUnparseable value
FIREWALL_MODE
falls back to NETWORK_FIREWALL_MODE
firewall.modeauto / xdp / nftables / iptables / nonebuilt-in default

IDS (Thalamus)​

VariableYAML keyValueUnparseable value
IDS_ENABLED
falls back to FIREWALL_IDS_ENABLED
ids.enabledtrue / falsebuilt-in default
IDS_RULE_PATHS
falls back to FIREWALL_IDS_RULE_PATHS
ids.rule_pathscomma-separated listused as given
IDS_SNAPLEN
falls back to FIREWALL_IDS_SNAPLEN
ids.snaplenintegerbuilt-in default
IDS_CAPTURE_MODE
falls back to FIREWALL_IDS_CAPTURE_MODE
ids.capture_modexdp / afpacketrecvbuilt-in default, and it warns
IDS_POLL_TIMEOUT_MS
falls back to FIREWALL_IDS_POLL_TIMEOUT_MS
ids.poll_timeout_msintegerbuilt-in default
IDS_FLOW_TIMEOUT_SECS
falls back to FIREWALL_IDS_FLOW_TIMEOUT_SECS
ids.flow_timeout_secsintegerbuilt-in default
IDS_MAX_FLOWS
falls back to FIREWALL_IDS_MAX_FLOWS
ids.max_flowsintegerbuilt-in default
IDS_CLEANUP_INTERVAL_SECS
falls back to FIREWALL_IDS_CLEANUP_INTERVAL_SECS
ids.cleanup_interval_secsintegerbuilt-in default
IDS_STATS_LOG_INTERVAL_SECS
falls back to FIREWALL_IDS_STATS_LOG_INTERVAL_SECS
ids.stats_log_interval_secsintegerbuilt-in default
IDS_ENFORCE_BLOCK
falls back to FIREWALL_IDS_ENFORCE_BLOCK
ids.enforce_blocktrue / falsebuilt-in default
IDS_ENFORCE_EDGE_DROP
falls back to FIREWALL_IDS_ENFORCE_EDGE_DROP
ids.enforce_edge_droptrue / falsebuilt-in default

Capture​

VariableYAML keyValueUnparseable value
CAPTURE_MODEcapture.modeauto / xdp / af_packet / af_packet_recv / pfring / af_xdp / ndisbuilt-in default, and it warns
CAPTURE_SNAPLENcapture.snaplenintegerbuilt-in default
CAPTURE_ENABLEDcapture.enabledtrue / falseignored
CAPTURE_FILTERcapture.filterstringused as given
PFRING_ZERO_COPYcapture.pfring.zero_copytrue / falsebuilt-in default
PFRING_CLUSTER_IDcapture.pfring.cluster_idintegercleared

ML fingerprint classifier​

VariableYAML keyValueUnparseable value
CLASSIFIER_ENABLEDclassifier.enabledtrue / falseignored
CLASSIFIER_MODEL_PATHclassifier.model_pathstringused as given
CLASSIFIER_CACHE_SIZEclassifier.cache_sizeintegerignored
CLASSIFIER_THRESHOLDclassifier.thresholdnumberignored

Platform: API, GeoIP and threat feed​

VariableYAML keyValueUnparseable value
API_KEY ◆platform.api_keystringused as given
BASE_URL ◆platform.base_urlstringused as given
AUTHCHECK_URL ◆platform.authcheck.urlstringused as given
REMOTE_CONFIG_URL ◆platform.remote_config.urlstringused as given
AGENT_EVENTS_URL ◆platform.agent_events.urlstringused as given
DATA_EVENTS_URL ◆platform.data_events.urlstringused as given
TELEMETRY_URL ◆platform.telemetry_endpoint.urlstringused as given
TELEMETRY_SENDING_ENABLED ◆
falls back to LOG_SENDING_ENABLED
platform.telemetry_sending_enabledtrue / falseignored
INCLUDE_RESPONSE_BODY ◆platform.include_response_bodytrue / falsebuilt-in default
MAX_BODY_SIZE ◆platform.max_body_sizeintegerbuilt-in default
THREAT_MMDB_URLplatform.threat.urlstringused as given
THREAT_MMDB_PATHplatform.threat.pathstringused as given
THREAT_MMDB_REFRESH_SECSplatform.threat.refresh_secsintegerignored
GEOIP_COUNTRY_URLplatform.geoip.country.urlstringused as given
GEOIP_COUNTRY_PATHplatform.geoip.country.pathstringused as given
GEOIP_ASN_URLplatform.geoip.asn.urlstringused as given
GEOIP_ASN_PATHplatform.geoip.asn.pathstringused as given
GEOIP_CITY_URLplatform.geoip.city.urlstringused as given
GEOIP_CITY_PATHplatform.geoip.city.pathstringused as given
GEOIP_ENABLEDplatform.geoip.enabledtrue / falseignored
THREAT_MMDB_ENABLEDplatform.threat.enabledtrue / falseignored
GEOIP_COUNTRY_HEADERSplatform.geoip.country.headerscomma-separated k=vcleared
GEOIP_ASN_HEADERSplatform.geoip.asn.headerscomma-separated k=vcleared
GEOIP_CITY_HEADERSplatform.geoip.city.headerscomma-separated k=vcleared
THREAT_MMDB_HEADERSplatform.threat.headerscomma-separated k=vcleared
GEOIP_REFRESH_SECSplatform.geoip.refresh_secsintegerignored

Logging​

VariableYAML keyValueUnparseable value
LOGGING_LEVELlogging.levelstringused as given
LOGGING_FILE_ENABLEDlogging.file_logging_enabledtrue / falsebuilt-in default
LOGGING_DIRECTORYlogging.log_directorystringused as given
LOGGING_MAX_FILE_SIZElogging.max_log_sizeintegerbuilt-in default
LOGGING_FILE_COUNTlogging.log_file_countintegerbuilt-in default
LOGGING_SYSLOG_ENABLEDlogging.syslog.enabledtrue / falsebuilt-in default
LOGGING_SYSLOG_FACILITYlogging.syslog.facilitystringused as given
LOGGING_EVENTBRIDGE_LOG_ENABLEDlogging.eventbridge_log.enabledtrue / falseignored
LOGGING_SYSLOG_IDENTIFIERlogging.syslog.identifierstringused as given
LOGGING_ETW_ENABLEDlogging.etw.enabledtrue / falsebuilt-in default
LOGGING_WINDOWS_EVENT_LOG_ENABLEDlogging.windows_event_log.enabledtrue / falsebuilt-in default
LOGGING_WINDOWS_EVENT_LOG_SOURCElogging.windows_event_log.sourcestringused as given

Daemon​

VariableYAML keyValueUnparseable value
DAEMON_ENABLEDdaemon.enabledtrue / falsebuilt-in default
DAEMON_PID_FILEdaemon.pid_filestringused as given
DAEMON_WORKING_DIRECTORYdaemon.working_directorystringused as given
DAEMON_USERdaemon.userstringused as given
DAEMON_GROUPdaemon.groupstringused as given
DAEMON_CHOWN_PID_FILEdaemon.chown_pid_filetrue / falsebuilt-in default

Proxy​

VariableYAML keyValueUnparseable value
REDIS_URLproxy.redis.urlstringused as given
REDIS_PREFIXproxy.redis.prefixstringused as given
REDIS_SSL_CA_CERT_PATHproxy.redis.ssl.ca_cert_pathstringused as given
REDIS_SSL_CLIENT_CERT_PATHproxy.redis.ssl.client_cert_pathstringused as given
REDIS_SSL_CLIENT_KEY_PATHproxy.redis.ssl.client_key_pathstringused as given
REDIS_SSL_INSECUREproxy.redis.ssl.insecuretrue / falseignored
CONTENT_VERDICT_CACHE_TTL_SECSproxy.content_scanning.verdict_cache_ttl_secsintegerignored
CONTENT_SCAN_MEMORY_BUDGETproxy.content_scanning.scan_memory_budgetintegerignored
CONTENT_SCANNING_ENABLEDproxy.content_scanning.enabledtrue / falsebuilt-in default
CLAMAV_SERVERproxy.content_scanning.clamav_serverstringused as given
CONTENT_MAX_FILE_SIZEproxy.content_scanning.max_file_sizeintegerbuilt-in default
CAPTCHA_SITE_KEYproxy.captcha.site_keystringused as given
CAPTCHA_SECRET_KEYproxy.captcha.secret_keystringused as given
CAPTCHA_JWT_SECRETproxy.captcha.jwt_secretstringused as given
CAPTCHA_PROVIDERproxy.captcha.providerstringused as given
CAPTCHA_TOKEN_TTLproxy.captcha.token_ttlintegerbuilt-in default
CAPTCHA_CACHE_TTLproxy.captcha.cache_ttlintegerbuilt-in default
INTERNAL_SERVICES_ENABLEDproxy.internal_services.enabledtrue / falsebuilt-in default
INTERNAL_SERVICES_PORTproxy.internal_services.portintegerbuilt-in default
INTERNAL_SERVICES_BIND_IPproxy.internal_services.bind_ipstringused as given
INTERNAL_SERVICES_SERVE_ACME_CHALLENGEproxy.internal_services.serve_acme_challengetrue / falsebuilt-in default
PROXY_PROTOCOL_ENABLEDproxy.protocol.enabledtrue / falsebuilt-in default
PROXY_PROTOCOL_TIMEOUTproxy.protocol.timeout_msintegerbuilt-in default
PROXY_ADDRESS_HTTPproxy.address_httpstringused as given
PROXY_ADDRESS_TLSproxy.address_tlsstringused as given
PROXY_CERTIFICATESproxy.certificatesstringused as given
PROXY_H2Cproxy.h2ctrue / falseignored
PROXY_ALLOW_CONNECT_METHOD_PROXYINGproxy.allow_connect_method_proxyingtrue / falseignored
PROXY_KEEPALIVE_REQUEST_LIMITproxy.keepalive_request_limitintegercleared
PROXY_TLS_GRADEproxy.tls_gradestringused as given
PROXY_DEFAULT_CERTIFICATEproxy.default_certificatestringused as given
PROXY_DEFAULT_CERT_PATHproxy.default_cert_pathstringused as given
PROXY_DEFAULT_KEY_PATHproxy.default_key_pathstringused as given
UPSTREAM_CONFproxy.upstream.confstringused as given
UPSTREAM_HEALTHCHECK_METHODproxy.upstream.healthcheck.methodstringused as given
UPSTREAM_HEALTHCHECK_INTERVALproxy.upstream.healthcheck.intervalintegerignored
ACME_ENABLEDproxy.acme.enabledtrue / falsebuilt-in default
ACME_PORTproxy.acme.portintegerignored
ACME_BIND_IPproxy.acme.bind_ipstringused as given
ACME_EMAILproxy.acme.emailstringused as given
ACME_STORAGE_PATHproxy.acme.storage_pathstringused as given
ACME_STORAGE_TYPEproxy.acme.storage_typestringused as given
ACME_DEVELOPMENTproxy.acme.developmenttrue / falsebuilt-in default
ACME_REDIS_URLproxy.acme.redis_urlstringused as given

Two notes on that table:

  • Setting any one of REDIS_SSL_CA_CERT_PATH, REDIS_SSL_CLIENT_CERT_PATH, REDIS_SSL_CLIENT_KEY_PATH or REDIS_SSL_INSECURE creates the proxy.redis.ssl block if the YAML has none, which turns Redis TLS on as a side effect.
  • CAPTCHA_TOKEN_TTL and CAPTCHA_CACHE_TTL write proxy.captcha.token_ttl and proxy.captcha.cache_ttl, and both keys are read: the token TTL sets the expiry of the captcha token and the Max-Age of the cookie carrying it. The --captcha-token-ttl and --captcha-cache-ttl flags are the ones that do nothing - they are parsed and never read, so set the keys or the variables instead.

Variables read at runtime​

These are not part of the configuration merge and have no YAML equivalent. They are read where they are used, so an invalid value is usually ignored silently at that point.

Agent identity and status​

Reported to the Gen0Sec API. AGENT_TAGS, AGENT_IP_ADDRESSES, AGENT_IPS, AGENT_HEARTBEAT_SECS and AGENT_ID are read only when platform.api_key is set and the run is not a PCAP replay. AGENT_NAME and HOSTNAME are read on every run: they also name the instance in OpenTelemetry resource attributes and the host field on CEF lines.

VariableValueDefaultWhat it does
AGENT_NAMEstringthe host nameThe name this agent reports. The agent's id is derived from it, so changing the name changes the id.
AGENT_TAGScomma-separated listemptyTags reported with the agent's status.
AGENT_IP_ADDRESSEScomma-separated listemptyThe addresses this agent reports. Not validated as IP addresses.
AGENT_IPScomma-separated listemptyOlder name for the same thing, used only when AGENT_IP_ADDRESSES is unset.
AGENT_HEARTBEAT_SECSinteger seconds30How often the agent reports status. An unparseable value falls back to 30.
AGENT_ID——Nothing. Setting it logs AGENT_ID is ignored; agent_id is derived from agent_name.
HOSTNAMEstringthe host nameUsed as the agent name when AGENT_NAME is unset, and as the host field on CEF and syslog lines.

Smart firewall​

VariableValueDefaultWhat it does
AMYGDALA_PREBLOCKcomma-separated kind=value, optionally allow:kind=valueemptySeeds block and allow rules at startup. kind is one of ja4, ja4s, ja4t, ja4ts, ja4l, ja4ls, ja4h, ja4x, ja4ssh; entries with no =, or an unknown kind, are skipped with a warning. Setting it also starts the bridge even with firewall.amygdala.enabled false.
AMYGDALA_REACTOR_SCOPEsrc_tuple, src_ip, src_ip:<prefix>src_ip with prefix 32What a block covers: the full source tuple, the whole source address, or a prefix of it. An unknown value warns and leaves the default; an unparseable prefix falls back to 32. Setting it also starts the bridge.
AMYGDALA_SWEEP_INTERVAL_MSinteger milliseconds30000How often expired entries are evicted. Values below 1000 are raised to 1000 with a warning; an unparseable value falls back to 30000.
DANGEROUS_AFXDP_OWN_MODEexactly 1unsetPermits an afxdp_attach_mode other than shared. Without it, any other value is refused and forced back to shared. Devmap forwarding can rewrite the source MAC, so use it only on isolated interfaces.

Logging and tracing​

VariableValueDefaultWhat it does
RUST_LOGa bare levelinfoLog level, consulted last - see Exception 1.
SYNAPSE_LOG_FORMATjson (any case)textWrites app.log, error.log and the console line as JSON. access.log is unaffected - it is already JSON.
SYNAPSE_ACCESS_LOG_SAMPLE_RATEnumber from 0 to 1telemetry.access_log.sample_rateFraction of access-log events kept. A value outside the range, or an unparseable one, is ignored.

OpenTelemetry​

The endpoint, header and compression variables describe one OTLP destination, the environment's. It comes into being as soon as any of the four endpoint variables is set, and sits alongside the destination telemetry.otlp.endpoint names and the platform's. With only a per-signal endpoint set, the signals without one are not exported there at all.

The order of those three matters for everything below: the environment's, then telemetry.otlp.endpoint, then the platform. A URL two of them name is pushed to once, as the earlier, carrying the headers of both — the earlier's winning a clash.

The two timeouts are not so confined, and their rows say what each reaches.

The last five are not about a destination at all. They shape the resource, the sampler and the metric interval, and are read whether or not any OTLP endpoint is set — though with no destination configured only the sampler still acts, since the file sink carries no resource and the metric interval times a reader that exists only per destination.

Headers reach one destination, not all of them

OTEL_EXPORTER_OTLP_HEADERS is attached to the first destination only. The one configured under telemetry: gets telemetry.otlp.headers and nothing else, and the platform gets its own key. Nothing fails when a token is set in the wrong place; the destination that needed it simply receives requests without it. Synapse does warn at startup for the one case it can see — headers set in the environment while the environment names no endpoint to send them to.

Through 0.8.7 these variables reached every exporter, so a configuration that relied on one token covering all three destinations stops working on 0.8.8. See Upgrade Notes.

VariableValueDefaultWhat it does
OTEL_EXPORTER_OTLP_ENDPOINTbase URLunsetAdds an OTLP destination, the first of the three. The signal's path (/v1/logs, /v1/metrics, /v1/traces) is appended, replacing one the URL already ends in. An empty value counts as unset.
OTEL_EXPORTER_OTLP_{LOGS,METRICS,TRACES}_ENDPOINTURLunsetSends that one signal to this URL as given, in place of the general endpoint.
OTEL_EXPORTER_OTLP_HEADERScomma-separated k=vnoneHeaders for the first destination only, which is where an API token for it goes. Values are percent-decoded; an entry with no =, an empty name or an empty value is skipped.
OTEL_EXPORTER_OTLP_{LOGS,METRICS,TRACES}_HEADERScomma-separated k=vnoneThat signal's headers, read in place of the general variable rather than alongside it.
OTEL_EXPORTER_OTLP_COMPRESSIONgzip, zstd, noneunset, which compresses nothingCompression for the first destination only; the other two are sent uncompressed either way. Any other value warns at startup and is treated as none.
OTEL_EXPORTER_OTLP_{LOGS,METRICS,TRACES}_COMPRESSIONgzip, zstd, noneunsetThat signal's compression, in place of the general variable — including when it is set to nothing.
OTEL_EXPORTER_OTLP_TIMEOUTinteger milliseconds10000Two timeouts at once. It is the per-request timeout of the environment's destination — and, because synapse leaves the SDK's own timeout unset, the deadline the SDK gives the export retry loop at every destination. An unparseable value falls back to 10 s for both.
OTEL_EXPORTER_OTLP_{LOGS,METRICS,TRACES}_TIMEOUTinteger millisecondsunsetThat signal's retry deadline, at every destination, read in place of the general variable; an unparseable value falls through to it. No destination's per-request timeout changes.
OTEL_METRIC_EXPORT_INTERVALinteger milliseconds30000How often metrics are exported, to every destination.
OTEL_SERVICE_NAMEstringsynapseThe reported service name. telemetry.resource.service_name wins over it.
OTEL_RESOURCE_ATTRIBUTEScomma-separated k=v, percent-encodednoneExtra resource attributes. A bad escape discards the whole variable with a warning; telemetry.resource.attributes is applied after and wins.
OTEL_TRACES_SAMPLERalways_on, always_off, traceidratio, parentbased_always_on, parentbased_always_off, parentbased_traceidratioparentbased_traceidratioTrace sampler. An unknown value warns and uses the default.
OTEL_TRACES_SAMPLER_ARGnumber from 0 to 1telemetry.tracing.sample_rateThe ratio for the traceidratio samplers. Ignored by the always_* ones.

Proxy and ACME​

VariableValueDefaultWhat it does
REDIS_URLRedis URLredis://127.0.0.1:6379Several ACME storage paths read this variable directly rather than proxy.redis.url, so a Redis URL set only in the YAML leaves those paths on localhost. Set the variable too, or set proxy.acme.redis_url.
SYNAPSE_NODE_IPIP addressthe local socket addressThe node address recorded as the destination for post-TLS IDS events. Intended for the Kubernetes downward API (status.hostIP). An unparseable value is ignored.

Windows​

VariableValueDefaultWhat it does
SYNAPSE_WINDOWS_IFINDEXpositive integerunsetInterface index for Windows IDS capture. Wins over SYNAPSE_WINDOWS_IFACE.
SYNAPSE_WINDOWS_IFACEinterface name or numberunsetInterface for Windows IDS capture, used when the index is unset or zero.

Diagnostics​

Off unless set. The first three are switches: any value turns them on, including an empty one.

VariableValueWhat it does
SYNAPSE_BPF_PROBEany valueRuns map-creation probes at startup and logs the results.
SYNAPSE_LIBBPF_VERBOSEany valueSends libbpf and verifier output to the log.
SYNAPSE_IDS_SELFTESTany valueRuns an IDS-to-firewall decision self-test at startup.
SYNAPSE_FP_BUS_WRITER1 or trueStarts a second, bus-driven fingerprint writer alongside the normal one. A flow can then be logged twice.
THALAMUS_TRACE_ENABLED1, true, yes or onTraces IDS rule evaluation per packet. The four variables below apply only when it is on.
THALAMUS_TRACE_DIRpath (default ./trace)Where the trace files are written. Created if missing.
THALAMUS_TRACE_SIDScomma-separated rule idsTrace only these rules. Unset traces all of them.
THALAMUS_TRACE_FLOWScomma-separated ip:port-ip:portTrace only these flows.
THALAMUS_TRACE_PREVIEW_BYTESpositive integer (default 96)How many payload bytes each trace entry records.

Set by synapse itself​

VariableWhat it does
ECDYSIS_RUST_UPGRADEMarks a process as the child of a graceful binary upgrade. Set by the upgrade machinery; not an operator setting.

Custom builds only​

SYNAPSE_EGRESS_IDS and SYNAPSE_EGRESS_IFACES configure the egress IDS, which is in no published package - see What your package contains.

Documented elsewhere, never read​

BPF_STATS_ENABLED, TCP_FINGERPRINT_ENABLED and TOKIO_WORKER_THREADS appear in synapse's in-repo docs/ENV_VARS.md. No Rust file reads any of them. The thread count is worker_threads in the YAML, or WORKER_THREADS.

What your package contains​

Not every subsystem is in every package. A configuration key for one that is missing still parses; what happens then depends on the key, and it is not consistent.

synapse-agent
synapse-proxy
synapse-agent-ml
synapse-proxy-ml
Legacy agent
(not published)
Windows
L7 proxy: TLS termination, upstreams, ACME, WAF, captchayesyesnono
eBPF/XDP datapath and XDP captureyesyesnono
AF_PACKET captureyesyesyesno
NDIS capture---yes
Smart firewall (firewall.amygdala)yesyesiptables/nftables onlyno
Inline JA4 ClientHello enforcementyesyesnono
ML scoring (ml.*)noyesnono
Graceful binary upgrade (--upgrade)yesyesnono
Egress IDSnononono
PF_RING capturenononono
  • The -ml packages are the only ones with ML scoring. They are published separately because the ONNX runtime needs glibc 2.38 or newer; the plain packages have a glibc 2.31 floor and the -ml packages 2.39. Both come in x86_64 and arm64.
  • The agent and proxy packages carry the same binary. They differ in the unit file and in the configuration they ship. The binary being identical, a synapse-agent install can run a proxy: set mode: proxy in config.yaml. The two packages conflict with each other, so one host runs one of them.
  • On the synapse-proxy package, mode: in config.yaml does nothing. Its unit file carries Environment="MODE=proxy", and MODE is an environment variable, so it beats the file - which still says mode: "agent", because both packages ship the same config.yaml. This is the rule at the top of the page in its most consequential form: if you need that host to stop proxying, edit the unit or override MODE, not the YAML.
  • The legacy agent is the static build for old kernels and old glibc: agent only, AF_PACKET capture, iptables/nftables enforcement. It is not among the published release artefacts - it is built and tested per change, so ask support for a build if you need it. The column is here because the keys behave the same way on it.
  • The Windows build is agent only. It captures fingerprints through NDIS; it has no proxy, no eBPF, and no smart-firewall enforcement.
  • Egress IDS and PF_RING capture are in no published package. Both need a build made with non-default options.

Keys that are accepted and do nothing​

KeyNeedsWithout it
ml.*an -ml package, on Linuxnamed in a startup warning: Unused config options in <file>: ml. No scoring happens
CLASSIFIER_*an -ml package, on Linuxaccepted, no scoring, nothing logged
capture.mode: pf_ringa custom buildaccepted, no capture backend starts, nothing logged
firewall.amygdala.*Linux; a Windows build with the NDIS bridge, which no published Windows package isaccepted, no enforcement, nothing logged
ids.egress_ifaces, SYNAPSE_EGRESS_IDSa custom buildaccepted, no egress IDS, nothing logged
network.interfaces with the ids_egress featurea custom buildsynapse refuses to start: network.interfaces enables 'ids_egress', but this build has no egress IDS

Three outcomes, then, and it is worth knowing which one you are in. The last row refuses to start. The ml.* row is named in the Unused config options warning synapse logs for any section its build does not recognise - which is also how you find a typo in a section name. The rest are silent, and the only signal is the absence of a log line saying the subsystem started, so check the log on first start rather than assuming the key took effect.

The CLASSIFIER_* variables are silent where ml.* is not, because the unused-key warning comes from parsing the file: nothing inspects the environment for variables that this build cannot use.

capture.mode: auto never selects PF_RING, so it is safe everywhere: it resolves to XDP when the eBPF programs load, AF_PACKET when they do not, and NDIS on Windows.