Skip to content

Configuration reference ​

One seo.toml describes one project: its site identity, the provider targets to read, crawl limits, report settings and local storage. The schema is strict, so a misspelled key fails validation instead of silently disabling a setting. A missing config file is not an error; the defaults describe a runnable, read-only tool.

Location and precedence ​

Value precedence, lowest to highest: built-in defaults, the selected seo.toml, then LV24_SEO_* environment variables. --config PATH only selects the file; the engine looks for the explicit path first (it must exist, otherwise CONFIG_INVALID), then ./seo.toml, then the platform user config file: %APPDATA%\lv24-seo\config.toml on Windows, ~/.config/lv24-seo/config.toml on Linux, and the equivalent platformdirs location on macOS.

.env files are not loaded automatically. Export the variables in the shell, the service unit or the scheduler environment that runs the command.

Environment variables ​

VariableKey
LV24_SEO_DATA_DIRstorage.data_dir
LV24_SEO_GSC_PROPERTYproviders.gsc.property
LV24_SEO_YANDEX_HOST_IDproviders.yandex_webmaster.host_id
LV24_SEO_YANDEX_METRIKA_COUNTERproviders.yandex_metrika.counter
LV24_SEO_REPORT_LOCALEreport.locale
LV24_SEO_REPORT_TIMEZONEreport.timezone
LV24_SEO_SMTP_HOSTreport.email.smtp_host
LV24_SEO_SMTP_PORTreport.email.smtp_port
LV24_SEO_SMTP_USERreport.email.smtp_user
LV24_SEO_SMTP_FROMreport.email.from
LV24_SEO_MAIL_TOreport.email.to (comma-separated list)
LV24_SEO_TELEGRAM_CHAT_IDreport.telegram.chat_id

Unsupported LV24_SEO_* variables are ignored for forward compatibility. Secret variables are listed under Secrets; they are resolved by the secret store, not by this config model.

LV24_SEO_LOG_LEVEL is not a config key. Set it to DEBUG, INFO, WARNING, … to opt the process into redacting logging. The CLI and lv24-seo mcp serve install the handler; an unset variable leaves the caller's logging configuration untouched. Output is one JSON line per record when stderr is not a terminal (pipe, scheduler log, MCP host) and a plain line otherwise.

Key reference ​

Every key below is optional. An absent provider block means "not configured", not a failure.

[project] ​

KeyTypeDefaultNotes
namestring""Human label for the workspace.
base_urlstring""Default seed for crawl when --start is omitted.
default_propertystring""GSC property suggested by init and shown by properties. GSC queries use providers.gsc.property.

[providers.gsc] ​

KeyTypeDefaultNotes
propertystring""sc-domain:example.com or https://example.com/.
authstring"keychain"Reserved label. Accepted but ignored: credentials resolve from environment, then the OS keychain, then the opt-in file fallback.

[providers.yandex_webmaster] ​

KeyTypeDefaultNotes
host_idstring""Exact Yandex host id, for example https:example.com:443. Never guess it; read it from the Webmaster API or UI.

[providers.yandex_metrika] ​

KeyTypeDefaultNotes
counterstring""Metrika counter id. An unquoted integer in TOML is normalised to a string.
goalslist of integers[]Goal ids that count as conversions.

[providers.bing] ​

KeyTypeDefaultNotes
sitestring""Site URL as registered in Bing Webmaster Tools.

[providers.crux] ​

KeyTypeDefaultNotes
originstring""Origin queried for field data, for example https://example.com.

[providers.psi] ​

KeyTypeDefaultNotes
strategymobile or desktop"mobile"Lighthouse strategy.
urlslist of strings[]Pages checked by PageSpeed Insights. At most five are checked; lab scores come from the first URL.

There is no providers.ga4 block in the current strict schema. Adding one fails validation; GA4 is documented as an optional future source, not a configurable provider yet.

[crawl] ​

KeyTypeDefaultNotes
max_urlsinteger ≥ 1500URL budget for one crawl.
concurrencyinteger ≥ 14Reserved. The crawler is serial today, so this has no effect.
respect_robotsbooleantrueHonour robots.txt and Crawl-delay.
user_agentstring"lv24-seo/0.1"User-Agent sent by the crawler.

[report] ​

KeyTypeDefaultNotes
localeru, en or zh"ru"Language of rendered human output. Presentation only.
timezoneIANA zone name"Europe/Minsk"Zone used for report and stats windows, MCP default windows and local day boundaries.
week_startmonday or sunday"monday"Reserved. Weekly windows are day-based, so this currently has no effect.
thresholdsinline tablesee belowMovement thresholds used by reports and the only_on_change gate.

thresholds accepts clicks_pct (default 10.0), impressions_pct (default 15.0) and position_abs (default 1.5).

[report.email] and [report.telegram] (delivery) ​

There is no separate [delivery] table. Delivery is configured on the two channel blocks, and both are disabled by default.

KeyTypeDefaultNotes
email.enabledbooleanfalseDeliver the weekly report by email.
email.tolist of strings[]Recipients.
email.fromstring""Envelope sender.
email.only_on_changebooleantrueSkip a send when the period has no material change.
email.smtp_hoststring""SMTP host.
email.smtp_portinteger 1–65535587SMTP port.
email.smtp_userstring""SMTP user. The password comes from the secret store.
telegram.enabledbooleanfalseDeliver the weekly report to Telegram.
telegram.chat_idstring""Target chat id.
telegram.severitycritical or all"critical"Which findings trigger a Telegram message.

There are no report.email.locale or report.telegram.locale keys. Set report.locale once; it is the single source of truth for every rendered surface.

[storage] ​

KeyTypeDefaultNotes
data_dirpath"data"Local directory for seo.sqlite3 and engine state. A relative value written in the file resolves against the config file's parent; the built-in default resolves against the working directory. LV24_SEO_DATA_DIR overrides the file and stays process-scoped.
raw_retention_daysinteger ≥ 090Reserved. No raw archives are written, so pruning is a logged no-op.

Data directory ​

storage.data_dir has three scopes:

ValueResolved against
Relative value written in seo.toml (data_dir = "./data")the config file's parent
Built-in default (no [storage] block)the process working directory
LV24_SEO_DATA_DIRused as given; a relative value resolves against the process working directory. The environment always wins over the file

A config that pins a relative directory is self-contained: --config /srv/site/seo.toml uses /srv/site/data from any working directory. lv24-seo init creates <config dir>/data and writes data_dir = "./data". Paths are made absolute with os.path.abspath (~ expanded, .. normalised); symlinks are deliberately not resolved, so a config reached through a link anchors its data directory next to the link. The SQLite store is <data_dir>/seo.sqlite3.

lv24-seo doctor --json makes the scope visible: meta.data_dir is the effective directory, meta.data_dir_source is config when it is absolute (pinned by the file or an absolute LV24_SEO_DATA_DIR) and working directory when it is relative, and a relative directory with a config file present adds a warning that names the config's folder. A relative directory still works, but running the same config from two directories reads two different databases. Set an absolute path to pin it. lv24-seo config show --json reports the same effective path.

The opt-in <data_dir>/secrets.json fallback follows the same directory; the desktop app and the CLI share it only when they resolve the same data directory.

Secrets ​

Secrets never belong in seo.toml, logs, reports or test fixtures. Resolution order is environment, then the OS keychain, then an explicitly enabled file fallback.

SourceHow to use it
OS keychainlv24-seo auth set --provider <name> --stdin. Requires the optional secrets extra (pip install "lv24-seo[secrets]").
EnvironmentExport the provider variable listed below. Environment values are read-only and always win.
File fallbackSet LV24_SEO_ALLOW_FILE_SECRETS=1 and store data/secrets.json with 0600 permissions. On Windows the file relies on directory and file ACLs. Every run prints a warning.
Providerauth set --providerEnvironment variable
Google Search ConsolegscLV24_SEO_GSC_TOKEN
Yandex WebmasteryandexLV24_SEO_YANDEX_WEBMASTER_TOKEN
Yandex MetrikametrikaLV24_SEO_YANDEX_METRIKA_TOKEN
Bing WebmasterbingLV24_SEO_BING_API_KEY
Chrome UX ReportcruxLV24_SEO_CRUX_API_KEY
PageSpeed InsightspsiLV24_SEO_PSI_API_KEY
SMTPsmtpLV24_SEO_SMTP_PASSWORD
TelegramtelegramLV24_SEO_TELEGRAM_BOT_TOKEN

Gated writes also need LV24_SEO_WRITE_SECRET, a long random value kept in the same shell as the apply command. auth status reports presence and source, never a value, and does not verify permissions or make a network call. auth login prints manual instructions only.

Timezone and locale ​

report.timezone must be a valid IANA zone. It is used for report and stats windows, MCP default windows (gsc_search_analytics, seo_opportunities, seo_weekly_report) and local day boundaries. An unknown zone fails validation with CONFIG_INVALID; there is no silent UTC fallback. Provider data stays date-based and is not converted between zones.

report.locale accepts ru, en and zh; the default is ru. It selects the language and the number, percent, position, date and plural formatting of the weekly email, Telegram messages, the CLI human summary and the HTML written by report weekly --out. JSON envelopes, MCP payloads, stored snapshots, report.json and report diff stay canonical English, and machine codes are never translated. An unsupported locale fails validation instead of falling back silently.

Validation and redaction ​

  • Unknown keys, invalid TOML, a missing explicit --config path, an invalid timezone or locale, and an out-of-range SMTP port all fail with CONFIG_INVALID and exit 2.
  • lv24-seo config show --json prints the effective config with meta.redacted: true. Any key whose name looks like a credential (token, secret, password, api_key, authorization) is masked with ***.
  • lv24-seo doctor checks Python, platform, config readability, working-directory writability, the effective data directory and its scope, and token presence. It makes no network calls. A relative directory is reported with meta.data_dir_source: "working directory" and a warning; it is not a failure.

Minimal example ​

toml
[project]
name = "example.com"
base_url = "https://example.com"

[providers.gsc]
property = "sc-domain:example.com"

This is enough for doctor, properties, gsc query and report weekly, provided the GSC token is available to the secret store.

Full example ​

Based on the commented template written by lv24-seo init, with example values:

toml
# lv24-seo workspace configuration (generated by `lv24-seo init`).
# Secrets never belong here: use `lv24-seo auth set` or the matching LV24_SEO_* variable.

[project]
name = "example.com"
base_url = "https://example.com"
default_property = "sc-domain:example.com"

[providers.gsc]
property = "sc-domain:example.com"
auth = "keychain"

[providers.yandex_webmaster]
host_id = "https:example.com:443"

[providers.yandex_metrika]
counter = "123456"
goals = [1, 2]

[providers.bing]
site = "https://example.com"

[providers.crux]
origin = "https://example.com"

[providers.psi]
strategy = "mobile"
urls = ["https://example.com/", "https://example.com/pricing"]

[report]
locale = "ru"
timezone = "Europe/Minsk"
week_start = "monday"
thresholds = { clicks_pct = 10, impressions_pct = 15, position_abs = 1.5 }

[report.email]
enabled = false
to = ["owner@example.com"]
from = "seo@example.com"
only_on_change = true
smtp_host = "smtp.example.com"
smtp_port = 587
smtp_user = "seo@example.com"

[report.telegram]
enabled = false
chat_id = "123456789"
severity = "critical"

[crawl]
max_urls = 500
concurrency = 4
respect_robots = true
user_agent = "lv24-seo/0.1"

[storage]
data_dir = "./data"      # relative to this file; absolute values and LV24_SEO_DATA_DIR are used as-is
raw_retention_days = 90

Delivery stays off until enabled = true is set on a channel. Keep the SMTP password and the Telegram bot token in the secret store, not in this file.

Source: _source/engine-config.md, _source/engine-data-sources.md, _source/engine-releases-0.5.2.md; 0.5.2 config model in the engine repository.