Server Configuration

This page owns the gonemaster-server configuration model.

Precedence

Configuration is applied in this order:

  1. Command-line flags
  2. GONEMASTER_* environment variables
  3. JSON config file passed with --config
  4. Built-in defaults

Use environment variables for secrets such as database connection strings.

Print the effective config and exit:

gonemaster-server --dump-config

Runtime Settings

The admin UI Settings page and PUT /api/v1/settings store settings in the database. A stored setting overrides the config file and the environment, but not a command-line flag, and takes effect without a restart. This includes the rate limits and public_url on /public/ and /analysis/.

PUT /api/v1/settings takes a JSON object of setting names and values. The server stores nothing and answers 400 when a name is read-only (readonly_setting), when a name is not a runtime setting (unknown_setting), or when a value is outside its type or range (invalid_setting):

SettingAccepted values
worker_countInteger 1 to 1024.
max_concurrent_jobsInteger 0 to 1024.
stuck_job_timeout_minutesInteger 0 to 525600.
retention_daysInteger 0 to 36500.
purge_interval_secondsInteger 1 to 31536000.
cross_job_hot_cache_ttl_secondsInteger 1 to 86400.
rate_limit_max, rate_limit_get_maxInteger 1 to 100000.
rate_limit_windowDuration above zero, such as 1m.
min_levelA log level name.
ipv6_modeauto, on or off.
public_urlAs in Core Settings .
rate_limit_enabled, allow_private_undelegated_ip, allow_non_global_targets, show_score_admin, show_score_public, show_nameserver_timings_admin, show_nameserver_timings_public, show_dnssec_chain_public, show_asn_names_public, mcp_enabled, mcp_allow_write, scheduler_enabledtrue or false.

The read-only settings are listen_addr, db_driver, db_dsn, profile_path, and every setting set by a command-line flag. GET /api/v1/settings reports such a setting with its effective value, source cli_flag and readonly true, and the Settings page cannot change it. The server checks stored settings against the same ranges at startup and after each write, and logs and ignores a stored value outside them.

Core Settings

SettingPurpose
listen_addrAddress and port for the HTTP listener.
max_body_sizeMaximum request body size.
worker_countNumber of workers that dequeue jobs.
max_concurrent_jobsMaximum number of engine runs at once.
stuck_job_timeout_minutesFail abandoned running jobs after this many minutes (0 = disable).
cross_job_hot_cacheEnables cross-job nameserver cache sharing.
cross_job_hot_cache_ttl_secondsTTL for cross-job hot-cache entries.
min_levelMinimum log level stored and returned in results.
ipv6_modeIPv6 in test runs. auto (default): before each run the engine checks that the host has a route to a global IPv6 address from a source address that is not link-local, and turns IPv6 off for the run without one, logging IPV6_AUTO_DISABLED. The check opens a UDP socket and sends nothing. on: no check; net.ipv6 of the profile applies. off: no run sends IPv6 queries. A job that disables IPv6 keeps it off in every mode.
profile_pathDefault engine profile file.
excludeTestcase ids and module names that no run executes. See Excluded testcases .
public_urlSite root the deployment answers at, e.g. https://example.com/. Also builds og:image, the API examples, robots.txt and sitemap.xml, so it is the root and not the public UI’s own URL. MUST be empty or an absolute http or https URL without userinfo, query, fragment, whitespace, control characters, quotes, angle brackets, backslash or backtick; the server refuses to start otherwise.
public_ui_pathPath under public_url where visitors reach the public UI. Default public/; set "" when a proxy serves it at the root. Does not move the server’s own mount.
scoring_config_pathOptional JSON scoring configuration file.
debugCaptures request/response bodies in the access log and implies log_level=debug.
log_formatOperational log encoding: text (default, human-readable) or json (one object per line, for aggregation).
log_levelMinimum operational log level: debug, info (default), warn, or error.
trusted_proxy_cidrsCIDRs (or bare IPs) of reverse proxies allowed to set X-Forwarded-For. Empty (default) trusts nothing and uses RemoteAddr. See public-api-and-proxy.md .
read_timeoutPer-connection read timeout (default 30s).
write_timeoutPer-connection write timeout (default 60s). Must exceed public_api.analysis_request_timeout.
idle_timeoutIdle keep-alive timeout (default 60s).
auth.admin_tokensAdmin token hashes. Empty (default) is open mode. See authentication.md .
auth.protect_publicRequires an admin token on /public/, /analysis/ and /pub/api/v1/ too. Default false; MUST NOT be true without auth.admin_tokens. See authentication.md .
public_api.allow_private_undelegated_ipAllow loopback / link-local / private / CGNAT / multicast / broadcast IPs as undelegated NS targets on the public API. Default false; enable on private/internal deployments.
public_api.allow_non_global_targetsPermit querying non-globally-reachable nameserver addresses. Default false, which clamps the engine guard on for every job so no caller-selected profile can relax it; set true on private/internal deployments. Complements (does not replace) allow_private_undelegated_ip: that flag is admission-time input validation, this is the query-time guard. A public instance that wants to run private undelegated tests must set both. The guard also covers the RIPE whois sources of the profile asn_db, so an internal whois mirror requires true.

Environment Variables

VariableConfig field
GONEMASTER_LISTENlisten_addr
GONEMASTER_WORKER_COUNTworker_count
GONEMASTER_MAX_CONCURRENT_JOBSmax_concurrent_jobs
GONEMASTER_STUCK_JOB_TIMEOUTstuck_job_timeout_minutes
GONEMASTER_MIN_LEVELmin_level
GONEMASTER_IPV6_MODEipv6_mode
GONEMASTER_PROFILEprofile_path
GONEMASTER_EXCLUDEexclude (comma-separated)
GONEMASTER_DEBUGdebug
GONEMASTER_LOG_FORMATlog_format
GONEMASTER_LOG_LEVELlog_level
GONEMASTER_DB_DRIVERdatabase.driver
GONEMASTER_DB_DSNdatabase.dsn
GONEMASTER_DB_RETENTION_DAYSdatabase.retention_days
GONEMASTER_PUBLIC_API_RATE_LIMIT_ENABLEDpublic_api.rate_limit_enabled
GONEMASTER_PUBLIC_API_RATE_LIMIT_MAXpublic_api.rate_limit_max
GONEMASTER_PUBLIC_API_RATE_LIMIT_GET_MAXpublic_api.rate_limit_get_max
GONEMASTER_PUBLIC_API_RATE_LIMIT_WINDOWpublic_api.rate_limit_window
GONEMASTER_PUBLIC_API_ALLOW_PRIVATE_UNDELEGATED_IPpublic_api.allow_private_undelegated_ip
GONEMASTER_PUBLIC_API_ALLOW_NON_GLOBAL_TARGETSpublic_api.allow_non_global_targets
GONEMASTER_TRUSTED_PROXY_CIDRStrusted_proxy_cidrs (comma-separated)
GONEMASTER_READ_TIMEOUTread_timeout
GONEMASTER_WRITE_TIMEOUTwrite_timeout
GONEMASTER_IDLE_TIMEOUTidle_timeout
GONEMASTER_CROSS_JOB_HOT_CACHEcross_job_hot_cache
GONEMASTER_CROSS_JOB_HOT_CACHE_TTLcross_job_hot_cache_ttl_seconds
GONEMASTER_EXTERNAL_DATA_ENABLEDexternal_data.enabled
GONEMASTER_EXTERNAL_DATA_REFRESH_INTERVALexternal_data.refresh_interval
GONEMASTER_EXTERNAL_DATA_RECORD_TTLexternal_data.record_ttl
GONEMASTER_EXTERNAL_DATA_NEGATIVE_TTLexternal_data.negative_ttl
GONEMASTER_EXTERNAL_DATA_TIMEOUTexternal_data.timeout
GONEMASTER_EXTERNAL_DATA_MAX_REQUESTS_PER_MINUTEexternal_data.max_requests_per_minute
GONEMASTER_EXTERNAL_DATA_MAX_CACHED_RECORDSexternal_data.max_cached_records
GONEMASTER_ANALYSIS_VANTAGE_LABELanalysis.vantage_label
GONEMASTER_ADMIN_TOKEN_HASHESauth.admin_tokens (comma-separated label=sha256:...)
GONEMASTER_AUTH_PROTECT_PUBLICauth.protect_public
GONEMASTER_SCHEDULER_ENABLEDscheduler_enabled

Invalid integer, boolean, or duration values emit a warning and are ignored.

Flags

Common flags:

--config PATH
--listen ADDR
--max-body-size BYTES
--debug
--dump-config
--version
--shutdown-timeout DURATION
--workers N
--max-concurrent-jobs N
--cross-job-hot-cache
--no-cross-job-hot-cache
--cross-job-hot-cache-ttl N
--profile PATH
--exclude NAME
--min-level LEVEL
--log-format text|json
--log-level debug|info|warn|error
--trusted-proxy-cidrs LIST
--read-timeout DURATION
--write-timeout DURATION
--idle-timeout DURATION
--public-api-allow-private-undelegated-ip

External data flags:

--external-data-enabled
--external-data-refresh-interval DURATION
--external-data-record-ttl DURATION
--external-data-negative-ttl DURATION
--external-data-timeout DURATION
--external-data-max-requests-per-minute N
--external-data-max-cached-records N

Analysis flags:

--analysis-vantage-label LABEL
--scheduler-enabled=false

Resolver override flags:

--positive-cache-ttl N
--negative-cache-ttl N
--timeout N
--retry N
--retrans N
--fallback
--no-fallback
--sourceaddr4 IPADDR
--sourceaddr6 IPADDR

Database and public API flags are covered in database.md and public-api-and-proxy.md , and the authentication flags --admin-token-hashes and --auth-protect-public in authentication.md .

Config File Example

{
  "listen_addr": "127.0.0.1:8080",
  "max_body_size": 1048576,
  "debug": false,
  "worker_count": 16,
  "max_concurrent_jobs": 16,
  "cross_job_hot_cache": true,
  "cross_job_hot_cache_ttl_seconds": 60,
  "timeout": 5,
  "retry": 2,
  "retrans": 3,
  "fallback": true,
  "min_level": "INFO",
  "ipv6_mode": "auto",
  "log_format": "text",
  "log_level": "info",
  "profile_path": "/etc/gonemaster/profile.json",
  "public_url": "https://gonemaster.example/",
  "public_ui_path": "public/",
  "database": {
    "driver": "sqlite",
    "dsn": "/var/lib/gonemaster/gonemaster.db",
    "retention_days": 90
  },
  "public_api": {
    "rate_limit_enabled": true,
    "rate_limit_max": 10,
    "rate_limit_get_max": 600,
    "rate_limit_window": "10m",
    "allow_private_undelegated_ip": false,
    "allow_non_global_targets": false
  },
  "analysis": {
    "tag_view_min_level": "NOTICE",
    "vantage_label": "Stockholm, SE"
  },
  "external_data": {
    "enabled": false
  },
  "trusted_proxy_cidrs": ["127.0.0.1/32"],
  "read_timeout": "30s",
  "write_timeout": "60s",
  "idle_timeout": "60s"
}

To proxy the public UI to the site root, set public_ui_path to "" and see ui.md .

Profiles

The server has two profile sources:

  • A process-wide base profile from the built-in default plus profile_path.
  • Stored profiles in the database, referenced by jobs, batches, public profiles, and tag defaults.

Stored profiles are sparse overrides. They contain only the settings that differ from the engine default.

Example stored profile config:

{
  "resolver": {
    "defaults": {
      "timeout": 5
    }
  }
}

Stored profiles can be referenced from jobs, batches, public profiles, and tag defaults. The server validates stored profile JSON on create and update.

See the Profile Settings Reference for every resolver.defaults knob, its default, range, and CLI flag.

Excluded testcases

exclude removes testcases from every run on the instance, as defined in Testcase selection .

Each run stores its effective profile, whose test_cases list omits the excluded testcases. A run diff across a change of exclude reports the findings of a newly excluded testcase as cleared.

Profile Compatibility

Stored profiles record the engine schema version used when they were last edited. When engine defaults gain new test cases or test-level tags, the compatibility endpoints and admin UI can show profiles that need review.

Available repair operations include:

  • add missing test cases
  • add missing test-level tags
  • reset test cases to inherited defaults
  • reset one test-level module
  • mark a profile as reviewed

Deterministic Resolver Behavior

The built-in profile uses parallel and unordered resolver behavior for speed. For deterministic ordered output, use a profile with:

{
  "resolver": {
    "defaults": {
      "unordered": false,
      "parallel": 1
    }
  }
}

Bounding Slow Nameservers

By default the engine waits out slow or unresponsive nameservers, and the verdict reflects that slowness. Two profile settings can cap the wall-clock cost:

  • resolver.defaults.fast_fail_timeout_count skips a nameserver after this many consecutive timeouts on a protocol (default 3, 0 disables). It only reacts to silence.
  • resolver.defaults.nameserver_max_total_ms skips a nameserver address once the cumulative time spent querying it in a run exceeds this many milliseconds (default 0 = disabled). Unlike fast-fail it also bounds slow-but-responding servers, whose successes keep resetting the consecutive-timeout count.

The latency budget trades query coverage for speed: once an address is skipped the run gathers less data about its zone, which can lower the grade for very slow zones. It is therefore off by default. A conservative starting point is 60000 to 120000 ms (60-120 s); lower values such as 30000 ms engage sooner and affect more zones.

Apply it process-wide through profile_path:

{
  "resolver": {
    "defaults": {
      "nameserver_max_total_ms": 60000
    }
  }
}

Or scope it to specific runs by saving a stored profile with the same sparse override (Admin UI, Profiles) and selecting it for a job or batch, for example a profile applied to the TLD cohort. Each run records its effective profile, so you can confirm the value took effect.

Result Display Settings

The config file can hide score, nameserver timing and AS holder UI elements:

{
  "show_score_admin": true,
  "show_score_public": true,
  "show_nameserver_timings_admin": true,
  "show_nameserver_timings_public": true,
  "show_asn_names_public": true
}

These settings affect UI display. They do not remove stored data.

show_asn_names_public (default true) enables GET /pub/api/v1/jobs/{public_id}/asn-names, which the public UI calls to show the registered holder under each finding that names an AS number. When it is on, the server queries Team Cymru’s IP to ASN mapping service outside test runs, for the AS numbers of each result that is opened. Set it to false to stop these queries. See public-api-and-proxy.md .

MCP Settings

{
  "mcp_enabled": false,
  "mcp_allow_write": false
}

mcp_enabled (default false) serves the Model Context Protocol tools at POST /api/v1/mcp, behind the admin token auth. mcp_allow_write (default false) also registers the write tools batch_enqueue, batch_cancel, and cancel_job, for every token holder. Both are in the admin UI under Settings, MCP, and take effect without a restart. See ../mcp/server-endpoint.md .

Scheduler Settings

{
  "scheduler_enabled": true
}

scheduler_enabled (default true) lets the cohort scheduler submit the snapshot runs of due cohort schedules. When it is false, no schedule fires and every schedule is kept unchanged. When it returns to true, an overdue schedule is handled as after downtime: it fires once, or is skipped when catch_up is false and the occurrence is over one hour late. The value applies at the next check, within one minute, without a restart. It is in the admin UI under Settings, Scheduler. The flag --scheduler-enabled=false and GONEMASTER_SCHEDULER_ENABLED=false turn it off at startup; a flag-set value is read-only on the Settings page. See ../analysis/cohorts.md .

Analysis Settings

The analysis block holds capture-time and presentation policy for the analysis dashboard:

{
  "analysis": {
    "tag_view_min_level": "NOTICE",
    "vantage_label": "Stockholm, SE"
  }
}
SettingPurpose
tag_view_min_levelFloor for the tags written into a snapshot’s tag view at capture time. A tag whose worst level in the snapshot is below the floor gets no row and no detail page. One of INFO, NOTICE, WARNING, ERROR, CRITICAL; default NOTICE. A cohort can override it.
vantage_labelName of the network location the runs were made from. Default empty.

vantage_label changes presentation only. The latency figures come from queries this instance made, which is one network location; the dashboard says so either way. With the label set the caveat names the place (“Measured from Stockholm, SE; a single vantage point.”), which matters once results from several instances are compared. The label is served through the public catalog, is trimmed, and is ignored if longer than 64 characters.

External Reference Data

The analysis dashboard can show public registry data next to a measurement: the RDAP registration record for a domain, and a direct link to the registry’s own RDAP service. The data comes from a provider that fetches on its own schedule and serves from an in-memory cache. It is off by default:

{
  "external_data": {
    "enabled": false,
    "refresh_interval": "24h",
    "record_ttl": "168h",
    "negative_ttl": "1h",
    "timeout": "10s",
    "max_requests_per_minute": 30,
    "max_cached_records": 20000,
    "sources": {
      "iana_tlds": "https://data.iana.org/TLD/tlds-alpha-by-domain.txt",
      "rdap_bootstrap": "https://data.iana.org/rdap/dns.json"
    }
  }
}
SettingPurpose
enabledTurns the provider on. Default false.
refresh_intervalHow often the two IANA datasets are re-fetched. Conditional requests make an unchanged file cost one 304.
record_ttlHow long a cached per-domain RDAP record is served before it is refreshed.
negative_ttlHow long a failed fetch suppresses retries for the same object.
timeoutPer-request timeout for one outbound fetch.
max_requests_per_minuteBudget shared by all outbound fetches. It bounds what a crawler walking every detail page can make the server do.
max_cached_recordsCache ceiling for per-domain records; least recently used are dropped first.
sourcesDataset locations. Point them at a mirror if the deployment cannot reach IANA.

Enabling this makes the server open outbound HTTPS connections on its own initiative: to IANA for the two datasets, and to the RDAP service of the registry behind each domain a visitor opens. The domain names sent are already public in the cohorts the dashboard serves. Air-gapped and privacy-sensitive deployments should leave it off.

Fetching is constrained: HTTPS only, redirects followed only to HTTPS, literal and resolved loopback, link-local and private destinations refused, a 2 MiB response cap, and a gonemaster/<version> user agent. A request for a page never waits on a third party: a cache miss renders a placeholder and the record appears on the next request. GET /api/v1/analysis/status reports the dataset freshness, the cached record count, the queue depth, and the last fetch error.

The cache is memory only. A restart re-fetches the two datasets and repopulates records as they are viewed.

Operational Logging

log_format and log_level control the server’s operational logs (lifecycle, access log, warnings, errors). They are independent of min_level, which governs the DNS test result data. Set log_format=json for log aggregation and pick a log_level floor of debug, info, warn, or error.

--debug (or debug: true) captures request/response bodies in the access log and implies log_level=debug; leave it off in production so bodies are not logged.

Every /api/v1 and /pub/api/v1 request gets an X-Request-Id. The server generates one by default and echoes it in the response header. An inbound X-Request-Id is honored only when the request comes from a trusted_proxy_cidrs peer; from any other client it is ignored and a fresh ID is generated, so the ID cannot be spoofed on a directly exposed server.

See operations.md for the log formats, the access-log fields, and shipping logs to journald or Loki.