Backend Configuration - houyhlouis/LLMOJ GitHub Wiki
Complete backend configuration reference
Existing fields were audited against 43a88bdd62fbf200889b36932fa921439a293190; registration mode is added on baseline 20dec6f. This page covers all 111 leaf fields of AppConfig, its structural objects and open dictionaries. It documents server-wide backend.yaml, not every problem or contest business setting.
File, defaults and activation
The backend reads the file selected by LIBREOJ_CONFIG_FILE, normally /opt/LLMOJ/config/backend.yaml after one-click installation. ConfigService loads YAML, transforms and validates it, and checks cross-field relationships. It does not merge the example or fill general defaults. “Example value” below therefore does not mean “the default obtained by omitting this field.” Preserve a complete configuration except where optional or derived behavior is documented.
All fields are read at process startup; there is no file hot reload. Restart the backend through your operational procedure after changes. preference, except serverSideOnly, is returned to browsers through auth/getSessionInfo; refresh/reinitialize the browser after restarting. Runtime preferences generally do not require a frontend rebuild. judge.limit is sent when a judge connects; verify reconnection before relying on changed values. Backend server.clusters is not judge capacity.
The schema does not use IsDefined for every object and configuration validation does not strip unknown keys through a whitelist. A typo not immediately rejected is not evidence that it works; missing objects may fail when a consumer initializes. Do not add undocumented keys or place secrets in public preference data.
Sources: schema, relationship validation, source example, installer generator.
Example values differ from first-install values
The installer creates configuration only when absent and preserves existing files on reruns. On first installation it replaces database, Redis, MinIO, session and maintenance secrets; sets external MinIO URLs to the site origin plus /storage/; uses metrics.allowedIps: [127.0.0.1]; takes the site name from --site-name; and sets services.mail.address: null, services.mail.transport: {jsonTransport: true} and preference.security.requireEmailVerification: false. The sample SMTP URL, @example and REPLACE_WITH_* are not working credentials.
backend.yaml contains secrets: never commit it or upload it to the Wiki. The installer gives it root ownership and backend-group read access (0640). Coordinate database/Redis/MinIO password changes with those services; this file does not rotate their credentials.
“Required” below means an item that a complete running configuration should supply. “Optional” permits omission/null, with consumer-specific behavior described in the notes. Required children of optional objects apply when that object is enabled. Use YAML numbers and true/false, not quoted numeric/boolean strings.
server — Listener and backend processes
| Object | Rule |
|---|---|
server |
Required structural object; children are listed below. |
| Full field | Type / requirement | Source example (not default) | Purpose, constraints and fallback |
|---|---|---|---|
server.hostname |
string; required |
127.0.0.1 |
Backend bind IP, not a hostname. The local installer binds loopback; Nginx supplies the public listener. |
server.port |
number; required |
2002 |
Backend listener, integer 1–65535. Coordinate changes with the Nginx upstream and local judge serverUrl. |
server.trustProxy |
string array; required | ["loopback"] |
Array of trusted Express proxy addresses, networks or predefined names such as loopback. Affects client IP/protocol and IP rate limits. Trust only real proxies. The TypeScript annotation says string, but validation and usage require an array. |
server.clusters |
number; optional |
null |
Optional nonnegative integer: omitted/null disables clustering; 0 enables it with os.cpus().length workers; a positive value selects the worker count. This is not judge capacity; more workers consume memory and metrics ports. |
metrics — Metrics
| Object | Rule |
|---|---|
metrics |
Omit/null to disable the metrics listener. |
| Full field | Type / requirement | Source example (not default) | Purpose, constraints and fallback |
|---|---|---|---|
metrics.hostname |
string; required when parent enabled |
127.0.0.1 |
Required bind IP when metrics is enabled; use loopback or a protected monitoring network. |
metrics.basePort |
number; required when parent enabled |
2020 |
Integer 1–65535. The primary uses this port and workers use basePort + worker.id. Reserve the range; base-port validation does not validate the final worker port. |
metrics.allowedIps |
string[]; optional |
[] |
Optional IP array. Missing/null/[] imposes no source restriction. Nonempty entries exactly match socket.remoteAddress, not proxy headers; CIDR is not supported. Installer value: [127.0.0.1]. |
services — Database, object storage, Redis and mail
| Object | Rule |
|---|---|
services |
Required structural object; children are listed below. |
services.database |
Required structural object; children are listed below. |
services.minio |
Required structural object; children are listed below. |
services.minio.default |
Required structural object; children are listed below. |
services.minio.forUserUpload |
Optional/null: fall back to default. |
services.minio.forUserDownload |
Optional/null: fall back to default. |
services.minio.forJudge |
Optional/null: fall back to default. |
services.mail |
Required structural object; children are listed below. |
| Full field | Type / requirement | Source example (not default) | Purpose, constraints and fallback |
|---|---|---|---|
services.database.type |
"mysql" | "mariadb"; required |
mariadb |
Database driver: mysql or mariadb only. |
services.database.host |
string; required |
127.0.0.1 |
Database host string: hostname or IP. |
services.database.port |
number; required |
13306 |
Database port, integer 1–65535. |
services.database.username |
string; required |
libreoj |
Database login, distinct from an OJ administrator account. |
services.database.password |
string; required |
REPLACE_WITH_DATABASE_PASSWORD |
Sensitive: database password. The installer generates it; never use the template placeholder. |
services.database.database |
string; required |
libreoj |
Database name; changing it does not migrate existing data. |
services.minio.default.endpoint |
string; required |
http://127.0.0.1:19000 |
Default server-side object-storage connection. HTTP(S) origin with root path / only; no credentials, query or fragment. Required. |
services.minio.default.urlEndpoint |
string; optional |
null |
Default server-side object-storage connection URL replacement prefix; paths such as /storage/ are allowed and must end in /. No query/fragment. Missing/null means no rewrite. |
services.minio.forUserUpload.endpoint |
string; required when parent enabled |
http://127.0.0.1:19000 |
Browser-upload signing connection. HTTP(S) origin with root path / only; no credentials, query or fragment. Omitting the entire override object falls back to default; supply endpoint when the object exists. |
services.minio.forUserUpload.urlEndpoint |
string; optional |
http://localhost/storage/ |
Browser-upload signing connection URL replacement prefix; paths such as /storage/ are allowed and must end in /. No query/fragment. Missing/null falls back to default.urlEndpoint; no default means no rewrite. |
services.minio.forUserDownload.endpoint |
string; required when parent enabled |
http://127.0.0.1:19000 |
Browser-download signing connection. HTTP(S) origin with root path / only; no credentials, query or fragment. Omitting the entire override object falls back to default; supply endpoint when the object exists. |
services.minio.forUserDownload.urlEndpoint |
string; optional |
http://localhost/storage/ |
Browser-download signing connection URL replacement prefix; paths such as /storage/ are allowed and must end in /. No query/fragment. Missing/null falls back to default.urlEndpoint; no default means no rewrite. |
services.minio.forJudge.endpoint |
string; required when parent enabled |
http://127.0.0.1:19000 |
Judge-download signing connection. HTTP(S) origin with root path / only; no credentials, query or fragment. Omitting the entire override object falls back to default; supply endpoint when the object exists. |
services.minio.forJudge.urlEndpoint |
string; optional |
http://localhost/storage/ |
Judge-download signing connection URL replacement prefix; paths such as /storage/ are allowed and must end in /. No query/fragment. Missing/null falls back to default.urlEndpoint; no default means no rewrite. |
services.minio.accessKey |
string; required |
REPLACE_WITH_MINIO_ACCESS_KEY |
Credential: MinIO access identifier. The installer uses libreoj-local; it must match the object store. |
services.minio.secretKey |
string; required |
REPLACE_WITH_MINIO_SECRET_KEY |
Sensitive: MinIO secret, randomly generated by the installer. Changing only this side breaks authentication. |
services.minio.bucket |
string; required |
libreoj-files |
Bucket for files. Application permissions and signed access still apply; renaming does not move existing objects. |
services.redis |
string; required |
redis://:[email protected]:16379/0 |
Sensitive ioredis connection string, possibly including a password and database number. Installer uses local port 16379/database 0 with a random password. |
services.mail.address |
string; optional |
[email protected] |
Optional valid sender email. Configure it for real mail delivery using an identity accepted by your SMTP service. Installer value: null. |
services.mail.transport |
URL string/object; required | smtp://username:[email protected]:25 |
URL or options object passed directly to Nodemailer createTransport, without per-option schema validation. Sensitive if it includes password/OAuth credentials. Installer uses {jsonTransport: true}, which creates messages without delivery. See the open-dictionary notes below. |
security — Sessions, CAPTCHA, PoW and rate limiting
| Object | Rule |
|---|---|
security |
Required structural object; children are listed below. |
security.captcha |
Keep the object; null both providers to disable third-party CAPTCHA, independently of PoW. |
security.captcha.turnstile |
Optional; null disables this provider. |
security.captcha.tencentCaptcha |
Optional; null disables this provider. |
security.proofOfWork |
Required structural object; children are listed below. |
security.crossOrigin |
Required structural object; children are listed below. |
security.rateLimit |
Omit/null to disable global IP limiting, not other fixed limits. |
| Full field | Type / requirement | Source example (not default) | Purpose, constraints and fallback |
|---|---|---|---|
security.sessionSecret |
string; required |
REPLACE_WITH_SESSION_SECRET |
Sensitive JWT signing/verification secret, randomly generated at installation. Rotating it invalidates existing login tokens. |
security.maintainceKey |
string; required |
REPLACE_WITH_MAINTENANCE_KEY |
Sensitive key for the maintaince-key header of POST /api/runMaintainceTasks. Keep the source spelling maintaince; this is not an administrator password. |
security.captcha.turnstile.siteKey |
string; required when parent enabled |
not set | Required nonempty public site key when Turnstile is enabled; sent to browsers. |
security.captcha.turnstile.secretKey |
string; required when parent enabled |
not set | Required nonempty private Turnstile secret; used by the server for verification with Cloudflare. |
security.captcha.tencentCaptcha.appSecretKey |
string; required when parent enabled |
not set | Sensitive Tencent CAPTCHA application secret: 1–32 visible non-whitespace ASCII characters (0x21–0x7e). |
security.captcha.tencentCaptcha.appId |
number; required when parent enabled |
not set | Tencent CAPTCHA application ID, integer ≥1; distinct from cloud SecretId. |
security.captcha.tencentCaptcha.secretId |
string; required when parent enabled |
not set | Credential: nonempty Tencent Cloud SecretId string. |
security.captcha.tencentCaptcha.secretKey |
string; required when parent enabled |
not set | Sensitive nonempty Tencent Cloud SecretKey string. |
security.proofOfWork.difficulty |
number; required |
4 |
Proof-of-work difficulty for ordinary protected actions, integer 1–8. Not an enable switch: 0 is invalid. Higher values require more browser work. |
security.proofOfWork.expensiveActionDifficulty |
number; required |
5 |
PoW difficulty for expensive actions, currently submission and Tencent CAPTCHA acquisition; integer 1–8. Independent of third-party CAPTCHA and global rate limiting. |
security.crossOrigin.enabled |
boolean; required |
false |
Enables the xdomain cross-origin proxy page/script. It is not a general Access-Control-Allow-Origin switch and does not enable CORS on all APIs. |
security.crossOrigin.whiteList |
string[]; required |
[] |
Array of frontend origins allowed by xdomain, such as https://oj.example.com. Use only controlled origins; consulted when enabled=true. |
security.rateLimit.maxRequests |
number; required when parent enabled |
200 |
Request-point allowance per client IP per window. Schema checks integer only; use a positive value operationally. Exceeded limits return HTTP 429. Omit/null the entire rateLimit object to disable this layer. |
security.rateLimit.durationSeconds |
number; required when parent enabled |
10 |
Rate-limit window in seconds. Schema checks integer only; use a positive value. Counters are in memory, coordinated by the primary in Node cluster mode, not shared across independent hosts. |
security.sessionIdleTimeoutDays |
number; optional |
30 |
A login session not used for this many days expires and must log in again. Integer ≥0; 0 disables the limit; omitted means 30. Enforced by the server on every request. |
security.sessionMaxAgeDays |
number; optional |
90 |
A login session expires this many days after login, even if used regularly. Integer ≥0; 0 disables the limit; omitted means 90. |
preference — Public and server-only preferences
| Object | Rule |
|---|---|
preference |
Public except serverSideOnly; do not store secrets. |
preference.security |
Required structural object; children are listed below. |
preference.pagination |
Required structural object; children are listed below. |
preference.misc |
Required structural object; children are listed below. |
preference.serverSideOnly |
Required structure; not sent to browsers. |
| Full field | Type / requirement | Source example (not default) | Purpose, constraints and fallback |
|---|---|---|---|
preference.siteName |
string; required |
LLMOJ |
Public site name used by pages, the dynamic footer, mail and event reports; installer takes --site-name (default LLMOJ). |
preference.copyrightNotice |
string; required |
LLMOJ |
Custom copyright text below “site name Powered by LLMOJ”. Blank text and the exact defaults LLMOJ / LibreOJ Open Source Project do not add a duplicate line. Other text is preserved; configuration files and project licenses are unchanged. |
preference.security.captchaEnabled |
boolean; derived; do not set |
derived | Derived public field: actual output is based on whether security.captcha configures a provider. A manually supplied value is overwritten. |
preference.security.turnstileSiteKey |
string; derived; do not set |
derived | Derived public field copied from security.captcha.turnstile.siteKey. Never place a secretKey here. |
preference.security.registrationMode |
"open" | "approval" | "closed"; optional |
open |
Registration fallback when no website override is saved: immediate activation, reviewer approval, or disabled. Missing/null defaults to open. The database policy saved by an administrator takes precedence; existing applications keep their state. See Registration approval. |
preference.security.requireEmailVerification |
boolean; required |
true |
Whether registration verifies an emailed code. Template true, installer false. Configure and verify mail delivery before enabling. False does not remove mail requirements from reset-password/change-email flows and does not disable registration. |
preference.security.allowUserChangeUsername |
boolean; required |
true |
Allows ordinary users to change their own username, still subject to EditOwnProfile; ManageUser provides the administrative path. |
preference.security.allowEveryoneCreateProblem |
boolean; required |
true |
Default fallback for logged-in users creating problems; it does not permit guests. Explicit CreateProblem allow/deny rules still participate. |
preference.security.allowNonPrivilegedUserEditPublicProblem |
boolean; required |
true |
Allows an otherwise authorized owner or Write-ACL user to edit a public problem. It does not give every user write access; global edit permissions still apply. |
preference.security.allowOwnerManageProblemPermission |
boolean; required |
false |
Fallback allowing an owner to manage that problem ACL; public problems also depend on allowNonPrivilegedUserEditPublicProblem. It does not grant site /permissions administration. |
preference.security.allowOwnerDeleteProblem |
boolean; required |
true |
Fallback allowing owners to delete their own problems; public-problem editing policy and explicit deletion permissions also apply. |
preference.security.discussionDefaultPublic |
boolean; required |
true |
Default public flag for new discussions; does not rewrite existing discussions or bypass object access control. |
preference.security.discussionReplyDefaultPublic |
boolean; required |
true |
Default public flag for new discussion replies; existing replies are unchanged. |
preference.security.allowEveryoneCreateDiscussion |
boolean; required |
true |
Default fallback for logged-in users creating discussions; explicit CreateDiscussion and related-object permissions still apply. |
preference.pagination.homepageUserList |
number; required |
10 |
Public homepage user-list size, integer ≥1. No additional cross-field relation is declared. |
preference.pagination.homepageProblemList |
number; required |
10 |
Public homepage problem-list size, integer ≥1. No additional cross-field relation is declared. |
preference.pagination.problemSet |
number; required |
50 |
Public problem-set page size, integer ≥1. Must not exceed queryLimit.problemSet. |
preference.pagination.searchProblemsPreview |
number; required |
7 |
Public problem-search preview size, integer ≥1. Must not exceed queryLimit.problemSet. |
preference.pagination.submissions |
number; required |
10 |
Public submission-list page size, integer ≥1. Must not exceed queryLimit.submissions. |
preference.pagination.submissionStatistics |
number; required |
10 |
Public submission-statistics page size, integer ≥1. Must not exceed queryLimit.submissionStatistics. |
preference.pagination.userList |
number; required |
30 |
Public user-list page size, integer ≥1. Must not exceed queryLimit.userList. |
preference.pagination.userAuditLogs |
number; required |
10 |
Public user-audit page size, integer ≥1. Must not exceed queryLimit.userAuditLogs. |
preference.pagination.discussions |
number; required |
10 |
Public discussion-list page size, integer ≥1. Must not exceed queryLimit.discussions. |
preference.pagination.searchDiscussionsPreview |
number; required |
7 |
Public discussion-search preview size, integer ≥1. Must not exceed queryLimit.discussions. |
preference.pagination.discussionReplies |
number; required |
40 |
Public full discussion-reply page size, integer ≥1. Must not exceed queryLimit.discussionReplies. |
preference.pagination.discussionRepliesHead |
number; required |
20 |
Public reply count initially shown before expansion, integer ≥1. Must be less than preference.pagination.discussionReplies. |
preference.pagination.discussionRepliesMore |
number; required |
20 |
Public reply count requested on expansion, integer ≥1. Must not exceed queryLimit.discussionReplies. |
preference.misc.appLogo |
string; required |
'' |
Logo URL/path string; empty uses the frontend default presentation. Frontend window.appLogo can override it, and theme mappings may select another image. |
preference.misc.appLogoForTheme |
Record<string, string>; required |
{"pure":"original","far":"inverted"} |
Open dictionary from theme names to strings, e.g. pure: original and far: inverted. original selects the normal logo, inverted applies inversion, another string is an image path, and default requests the default logo. Missing theme entries fall back to original. |
preference.misc.googleAnalyticsId |
string; optional |
null |
Optional Google Analytics identifier; null/empty disables loading. Public; enabling causes browser requests to analytics services. |
preference.misc.plausibleApiEndpoint |
string; optional |
null |
Optional Plausible API URL, validated as a URL; null disables loading. |
preference.misc.gravatarCdn |
string; required |
https://gravatar.com |
Avatar-service base URL string; the frontend appends avatar/. Can be overridden by window.gravatarCdn. |
preference.misc.redirectLegacyUrls |
boolean; required |
false |
Enables legacy redirects already defined in the frontend; it does not import old data or create arbitrary redirect rules. |
preference.misc.legacyContestsEntryUrl |
string/null; optional | null |
Retained compatibility field, optional string/null. The TypeScript annotation says boolean but runtime validation is IsString. No page consumer was found in this version; do not rely on it to display a legacy-contest entry. |
preference.misc.homepageUserListOnMainView |
boolean; required |
true |
Controls whether the homepage user list is placed in the main content area. |
preference.misc.sortUserByRating |
boolean; required |
false |
Preference for sorting users by rating; does not compute or update ratings. |
preference.misc.renderMarkdownInUserBio |
boolean; required |
false |
Whether user bios are rendered as Markdown; does not grant arbitrary HTML rendering. |
preference.misc.discussionReactionEmojis |
string[]; required |
["👍","👎","😄","😕","❤️","🤔","🤣","🌿","🍋","🕊️"] |
Nonempty, unique array of default reactions; each is one valid emoji with UTF-8 length at most 28 bytes. |
preference.misc.discussionReactionAllowCustomEmojis |
boolean; required |
true |
Allows reactions beyond the default list, still subject to emoji validation and the server-only blacklist. |
preference.serverSideOnly.discussionReactionCustomEmojisBlacklist |
string | unknown[]; required |
/(\uD83C[\uDDE6-\uDDFF]){2}/ |
Server-only: one emoji, a /regular expression/ string, or a flat array of these; [] means no blacklist. The template blocks flags. Although schema validation accepts nested arrays recursively, the consumer flattens once; use a flat array. Regex flags are not supported and the pattern must compile. |
preference.serverSideOnly.dynamicTaskPriority |
boolean; required |
true |
Server-only dynamic queue priority based on submission/problem/user context; false uses uniform priority. It does not set concurrency or rate limits. |
resourceLimit — Problem and file resource limits
| Object | Rule |
|---|---|
resourceLimit |
Required structural object; children are listed below. |
| Full field | Type / requirement | Source example (not default) | Purpose, constraints and fallback |
|---|---|---|---|
resourceLimit.problemTestdataFiles |
number; required |
2005 |
Maximum test-data file count per problem, integer ≥0. |
resourceLimit.problemTestdataSize |
number; required |
8589934592 |
Maximum total test-data bytes per problem, integer ≥0; template 8 GiB. |
resourceLimit.problemAdditionalFileFiles |
number; required |
40 |
Maximum additional-file count per problem, integer ≥0. |
resourceLimit.problemAdditionalFileSize |
number; required |
134217728 |
Maximum total additional-file bytes per problem, integer ≥0; template 128 MiB. |
resourceLimit.problemSamplesToRun |
number; required |
10 |
Maximum sample cases run for submissions, integer ≥0; 0 omits that sample stage. |
resourceLimit.problemTestcases |
number; required |
1000 |
Maximum test-case count in problem judge configuration, integer ≥1. |
resourceLimit.problemTimeLimit |
number; required |
2000 |
Upper bound for a problem time limit in milliseconds, integer ≥1; not a backend HTTP timeout. |
resourceLimit.problemMemoryLimit |
number; required |
512 |
Upper bound for a problem memory limit in MiB, integer ≥1; not the backend Node heap size. |
resourceLimit.submissionFileSize |
number; required |
10485760 |
Maximum submission-file bytes, integer ≥0; template 10 MiB. Reverse proxies, problem types and AI uploads may impose smaller limits. |
queryLimit — Query limits
| Object | Rule |
|---|---|
queryLimit |
Required structural object; children are listed below. |
| Full field | Type / requirement | Source example (not default) | Purpose, constraints and fallback |
|---|---|---|---|
queryLimit.problemSet |
number; required |
100 |
Maximum result count per request for problem-set queries, integer ≥1. This is a server request limit, not a default page size; check corresponding pagination relationships. |
queryLimit.submissions |
number; required |
10 |
Maximum result count per request for submission-list queries, integer ≥1. This is a server request limit, not a default page size; check corresponding pagination relationships. |
queryLimit.submissionStatistics |
number; required |
10 |
Maximum result count per request for submission-statistics queries, integer ≥1. This is a server request limit, not a default page size; check corresponding pagination relationships. |
queryLimit.searchUser |
number; required |
10 |
Maximum result count per request for user search, integer ≥1. This is a server request limit, not a default page size; check corresponding pagination relationships. |
queryLimit.searchGroup |
number; required |
10 |
Maximum result count per request for group search, integer ≥1. This is a server request limit, not a default page size; check corresponding pagination relationships. |
queryLimit.userList |
number; required |
100 |
Maximum result count per request for user lists, integer ≥1. This is a server request limit, not a default page size; check corresponding pagination relationships. |
queryLimit.userAuditLogs |
number; required |
20 |
Maximum result count per request for user-audit logs, integer ≥1. This is a server request limit, not a default page size; check corresponding pagination relationships. |
queryLimit.discussions |
number; required |
20 |
Maximum result count per request for discussion lists/search, integer ≥1. This is a server request limit, not a default page size; check corresponding pagination relationships. |
queryLimit.discussionReplies |
number; required |
50 |
Maximum result count per request for discussion-reply queries, integer ≥1. This is a server request limit, not a default page size; check corresponding pagination relationships. |
judge — Limits sent to judges
| Object | Rule |
|---|---|
judge |
Required structural object; children are listed below. |
judge.limit |
Required structural object; children are listed below. |
| Full field | Type / requirement | Source example (not default) | Purpose, constraints and fallback |
|---|---|---|---|
judge.limit.compilerMessage |
number; required |
524288 |
Maximum compiler-message bytes, positive integer; template 512 KiB. |
judge.limit.outputSize |
number; required |
104857600 |
Maximum contestant-output bytes, positive integer; template 100 MiB. Judge binaryCacheMaxSize must not be smaller. |
judge.limit.dataDisplay |
number; required |
128 |
Maximum displayed input/output preview bytes for ordinary judging, positive integer. |
judge.limit.dataDisplayForSubmitAnswer |
number; required |
128 |
Maximum contestant-answer preview bytes for output-only tasks, positive integer; test input/reference-answer previews still use dataDisplay. |
judge.limit.stderrDisplay |
number; required |
5120 |
Maximum displayed contestant stderr bytes, positive integer; template 5 KiB. |
eventReport — Event reporting
| Object | Rule |
|---|---|
eventReport |
Keep the object; null token disables reporting. |
| Full field | Type / requirement | Source example (not default) | Purpose, constraints and fallback |
|---|---|---|---|
eventReport.telegramBotToken |
string; optional |
null |
Optional sensitive Telegram bot token; absent/null/empty disables reporting. Enabling sends events to Telegram; only the primary owns the bot in cluster mode. |
eventReport.telegramApiRoot |
string; optional |
null |
Optional API root URL; missing/null uses the Telegraf default Telegram API. |
eventReport.sentTo |
string | number; optional |
@example |
Target chat ID (string or number), or a channel the bot may post to, such as @example. Supply a real destination when enabling the token. |
eventReport.proxyUrl |
string; optional |
null |
Optional ProxyAgent URL string; sensitive if it includes authentication. Applies to Telegram, not a global AI-provider proxy. |
vendor — IP location databases
| Object | Rule |
|---|---|
vendor |
Keep the object even when ip2region is null. |
vendor.ip2region |
Optional; null uses library defaults. |
| Full field | Type / requirement | Source example (not default) | Purpose, constraints and fallback |
|---|---|---|---|
vendor.ip2region.ipv4db |
string; required when parent enabled |
not set | IPv4 database file path when supplying custom ip2region data; readable by the service user. Omitting/nulling the entire block passes {} to the library for its bundled behavior. |
vendor.ip2region.ipv6db |
string; required when parent enabled |
not set | IPv6 database file path when supplying custom ip2region data; readable by the service user. Omitting/nulling the entire block passes {} to the library for its bundled behavior. |
Open dictionaries and array entries
| Path | Values and actual support |
|---|---|
services.mail.transport.* |
Options passed to Nodemailer, not a fixed LLMOJ key catalog; transport may be a URL or object. Common SMTP options are host, port, secure, auth.user and auth.pass; auth.* may include authentication-specific settings, tls.* supplies TLS options, pool selects pooling, and jsonTransport selects a non-delivering JSON transport. Use valid options supported by the library. LLMOJ neither merges these children nor verifies account usability at configuration validation. auth.* and credential-bearing URLs are sensitive. |
preference.misc.appLogoForTheme.* |
* is a frontend theme name; every value is a string. Arbitrary theme keys are accepted, but only the selected theme is read. Not a nested object. |
server.trustProxy[] |
Each item is a proxy address/network or Express predefined-name string. Do not substitute a single string for the array. |
metrics.allowedIps[] |
Each item is an IP address; an empty array allows every source, while nonempty entries use exact matching. |
security.crossOrigin.whiteList[] |
Each item is an origin allowed to use the xdomain proxy. |
preference.misc.discussionReactionEmojis[] |
Each entry is one valid emoji; the list must be nonempty and unique. |
preference.serverSideOnly.discussionReactionCustomEmojisBlacklist[] |
Use a flat array of emojis or /pattern/ strings. Do not rely on the schema's permissive acceptance of deeply nested arrays. |
services.mail.transport.auth.* and services.mail.transport.tls.* belong to the transport dictionary, not a separate LLMOJ secret store. Transport values remain server-side. Verify delivery, sender identity and mail-domain policy before enabling registration email verification. The installer's JSON transport does not deliver codes to recipients.
Registration, permissions and rate limits together
Site administrators can change registration policy at Site management (/site-management). Saving or restoring the server configuration takes effect immediately without a restart. This independent database setting does not rewrite YAML; restoring uses the backend’s currently loaded YAML default. Direct YAML edits require a backend restart, and an active website override still takes precedence. Review or homepage-editing permissions cannot change this policy.
This schema has no registrationEnabled, email-domain allowlist, arbitrary permissions.*, or per-endpoint rate-limit dictionary. Adding invented YAML keys does not enable such features. requireEmailVerification controls registration-code verification; username rules still belong to business validation.
Per-user allow/deny rules, administrator privileges and problem/discussion/group ACLs are stored in the database, not a subtree of this file. allowEveryone* supplies a fallback rather than bypassing explicit denials; ManagePermissions remains administrator-only. ManageRegistrationReviews defaults to false and can be granted per user to review applications without permission-management authority; see Registration approval. AI permissions ManageAiConfiguration, UseAi, GenerateTestdata, ImportProblem, and per-account provider keys also live outside this schema; see AI configuration. Business permission changes are generally read from the database per request and do not require YAML edits/restarts. Sources: permission decisions, problem authorization.
security.rateLimit counts HTTP requests by req.ip, so configure server.trustProxy accurately; an incorrect proxy setup can combine many users into one IP quota. PoW and third-party CAPTCHA are separate protections: null CAPTCHA providers do not disable PoW. PoW challenges currently expire after five minutes and issuance is limited to 20 per ten seconds per IP/authenticated user. These are code constants, not YAML fields; global maxRequests does not change them. See HTTP limiting and PoW implementation.
Change checklist
- Back up and edit the complete configuration actually in use; preserve identity and storage credentials. Do not replace it with the example.
- Check
paginationversusqueryLimitrelationships and associated port, public storage URL, mail and CAPTCHA settings. - Schedule a backend restart through your operational procedure; coordinate listener/proxy/credential changes with the affected services. Refresh browsers for public preference changes and verify judge reconnection for judging limits.
- Verify login, the changed feature and logs. Successful schema validation does not prove SMTP, Telegram, database or storage connectivity.
0 is valid only where fields permit ≥0 and does not universally mean unlimited. Administrative paths with ignore-limit permissions may bypass some problem limits. AI images, ZIP imports, model responses and local generators have separate bounds; increasing resourceLimit does not change them. Judge expansion belongs to the applicable operational procedure in distributed judging, not server.clusters.