Configuration
Ritual reads its settings from ritual.config.json in the base directory: the directory you run it from, or the one named by --base-dir. The file is optional. Every setting has a built-in default, so a workspace with no config file behaves like one holding the defaults. Directory paths in the file are resolved relative to the base directory.
Default settings
Section titled “Default settings”Ritual uses these values when there is no config file. A first write puts them on disk:
{ "decksDir": "./decks", "collectionsDir": "./collections", "wantedDir": "./wanted", "artDir": "./art", "priceSources": ["tcgplayer"], "defaultCategories": [ "Ramp", "Draw", "Removal", "Board Wipes", "Counterspells", "Tutors", "Recursion", "Protection", "Combo", "Tokens", "Burn", "Lifegain", "Finishers", "Utility" ], "defaultLanguage": "en", "uiLocale": "en", "cacheLockTimeoutSeconds": 300, "cacheSource": "scryfall", "searchDebounceMs": 500, "updateCheck": true, "admin": { "gitEnabled": false, "gitAutoCommit": false, "gitAutoPush": false, "trustProxy": false, "secureCookies": false, "ipAllowList": [], "ipDenyList": [], "userAgentAllowList": [], "userAgentDenyList": [], "rateLimitEnabled": true, "rateLimitMaxAttempts": 5, "rateLimitWindowMinutes": 5, "failedAuthDelayMs": 3000 }, "collectionSync": { "pullTarget": "Inbox" }}Directory options
Section titled “Directory options”| Field | Default | Description |
|---|---|---|
decksDir |
./decks |
Where deck markdown files live. |
collectionsDir |
./collections |
Where collection markdown files live. |
wantedDir |
./wanted |
Where wanted-list markdown files live. |
artDir |
./art |
Where custom card art images live. |
Paths are relative to the base directory. With --base-dir ~/mtg and "decksDir": "./my-decks", Ritual reads decks from ~/mtg/my-decks. Absolute paths ("/srv/mtg/decks") and paths outside the base directory ("../shared-decks") also work.
Ritual never creates artDir. It reads the directory only when a list references custom art; a missing directory means the workspace has none. A card’s file reference is relative to this directory, so moving the directory or changing artDir never breaks a reference.
Default price store
Section titled “Default price store”| Field | Default | Description |
|---|---|---|
defaultPriceSource |
the first priceSources entry |
The store prices are read from by default: tcgplayer, cardkingdom, cardmarket, or cardhoarder. |
The store decides the currency too (tcgplayer and cardkingdom are USD, cardmarket is EUR, cardhoarder is MTGO tix). Leave the key unset to use the first enabled price store, or TCGplayer when none is enabled. It sets the starting point for:
- the price command, and the admin price API and MCP
get_price_report, when no store is given. There is no separate currency setting or flag anywhere; a store’s currency always comes with it. - the CLI editor’s printing and finish picker price columns and the price lines shown when adding or editing cards. These read Scryfall prices in the store’s currency.
- the public and admin sites’ initial price store. The sites can only open on a store listed in
priceSources; otherwise they open on the first store in the header’s dropdown (the first enabled store of the first offered currency).
init-site prompts for it and enables the chosen store. Change it later with config set defaultPriceSource cardkingdom, or clear it with config unset defaultPriceSource. Unlike init-site, config set and the admin Settings page do not add the store to priceSources, so enable it there too if the sites should open on it.
Price stores (priceSources)
Section titled “Price stores (priceSources)”| Field | Default | Description |
|---|---|---|
priceSources |
["tcgplayer"] |
The stores the published and admin sites offer card prices from. |
Use any combination of four store names. The stores also decide which currencies the sites offer: only the ones an enabled store quotes in. TIX appears only with cardhoarder.
| Store | Price | Notes |
|---|---|---|
tcgplayer |
Scryfall’s USD market price (TCGplayer) | The default. |
cardmarket |
Scryfall’s EUR trend price (Cardmarket) | Already in the card cache; no extra download. |
cardkingdom |
Card Kingdom’s Near Mint retail price | Read from the daily pricelist feed sell mode also uses. Enabling it makes builds and servers download and refresh that ~70 MB feed, like site.sellMode does, and opens the same buylist API routes. |
cardhoarder |
Scryfall’s MTGO tix price (Cardhoarder) | Already in the card cache. Enable it to offer TIX on the sites. |
The sites’ header Prices dropdown offers each enabled store, and switches the whole site (index tiles included) to it; see Prices on the site.
An empty array (config set priceSources --remove tcgplayer) hides every price surface on the sites: per-card prices, totals, the price sort/filter/grouping, and the header’s store picker. The CLI price command (which has its own --source flag) and sell mode are unaffected.
ritual config set priceSources tcgplayer cardkingdomritual config set priceSources --add cardmarketDefault categories
Section titled “Default categories”| Field | Default | Description |
|---|---|---|
defaultCategories |
the fourteen below | The global category vocabulary. |
The shipped vocabulary is Ramp, Draw, Removal, Board Wipes, Counterspells, Tutors, Recursion, Protection, Combo, Tokens, Burn, Lifegain, Finishers, Utility.
This key decides two things:
- which categories are suggested wherever a category is typed, in the CLI and in both site editors
- the display order of a category that a list’s
.categories.jsonfile does not declare
A list may use any category name; nothing has to be declared here first. Edit the key on the admin Settings page as Default Categories (comma-separated; see Settings) or with config set. The admin site reads the value at runtime; the public site picks it up on the next build-site.
Each name must follow the category shape rule. A malformed name is refused where it is typed. A malformed entry found on load warns and resets the whole key to the shipped default. An explicit empty array means no suggestions.
ritual config set defaultCategories Ramp Draw Removalritual config set defaultCategories --add "Board Wipes"ritual config set defaultCategories --remove BurnDefault language
Section titled “Default language”| Field | Default | Description |
|---|---|---|
defaultLanguage |
en |
The language stamped on newly added cards. Also decides which Scryfall bulk file backs the card cache. |
This key applies to add-card, the editors, and imports whose source states no language. The value is a Scryfall language code, stored lowercase:
| Code | Language | Code | Language |
|---|---|---|---|
en |
English | ru |
Russian |
es |
Spanish | zhs |
Simplified Chinese |
fr |
French | zht |
Traditional Chinese |
de |
German | he |
Hebrew |
it |
Italian | la |
Latin |
pt |
Portuguese | grc |
Ancient Greek |
ja |
Japanese | ar |
Arabic |
ko |
Korean | sa |
Sanskrit |
ph |
Phyrexian |
These are Scryfall’s codes, not ISO codes: Chinese is zhs/zht (not zh), and grc/ph have no ISO equivalent. config set defaultLanguage accepts common aliases (jp → ja, kr → ko, sp → es, cs → zhs, ct → zht, and full English names like Japanese) and stores the canonical code. The admin Settings page offers the same list as a dropdown.
On card lines the language is a bracket token ([ja]) that is omitted for English. A bare line always means en, whatever this key says. The key only controls what is stamped on new cards; it never reinterprets existing lines.
An edit session starts with this key as its session language and can change it from the 🌐 Card Language menu row without writing the config file. edit also warns once on startup when the key is absent.
This is not the interface language
Section titled “This is not the interface language”defaultLanguage decides which printing of a card is recorded. The language of Ritual’s own text is a separate key, uiLocale.
The all-cards consequence
Section titled “The all-cards consequence”Any non-English defaultLanguage switches every card-cache download from Scryfall’s default_cards bulk file (one English card object per printing) to the much larger all_cards bulk file, which carries every language. This covers cache preload-all, the freshness prompts, and the cache feed. Expect a several-times-larger download and cache.
The cache records which bulk file built it. When that disagrees with defaultLanguage, in either direction, commands that check cache freshness offer a full redownload, or run one under --refresh auto. See cache status.
Interface language
Section titled “Interface language”| Field | Default | Description |
|---|---|---|
uiLocale |
en |
The language of Ritual’s own interface text: CLI output, prompts, menus, help, and both web apps. A BCP-47 tag such as en, de, de-AT, or pt-BR. |
The tag is stored canonicalized (de-at is persisted as de-AT).
You can override the value per run or per shell without touching the file. When the key is absent, Ritual falls back to detecting your OS locale:
--locale <tag> → RITUAL_LOCALE → uiLocale → OS detection → enritual locale prints which of those tiers won, beside the current defaultLanguage. Detection has limits, notably under WSL and on native Windows; on those platforms set this key.
Where the value takes effect:
- The CLI and the admin site read it at runtime. It is also editable on the admin Settings page.
- The public site picks it up on the next build-site, where
--localeoverrides it for one build. - A live-backend
serve --apideployment reports the current value on every index request with no rebuild.
Translations are separate from this setting. No translated catalogs ship yet, so every value renders English text today. Dates, numbers, and currency do follow the tag. See Localization, including how to contribute a translation.
Cache lock timeout
Section titled “Cache lock timeout”| Field | Default | Description |
|---|---|---|
cacheLockTimeoutSeconds |
300 |
How long a cache refresh waits for another process’s refresh to finish before failing. Positive integer. |
Refreshes take an exclusive lock (cache/.ritual-cache-lock) so concurrent processes (CLI commands, the cache server, the admin server) never interleave writes. A waiting process breaks the lock immediately when its holder is provably no longer running, so a stale lock from a crash never blocks the cache for longer than one attempt.
Search debounce
Section titled “Search debounce”| Field | Default | Description |
|---|---|---|
searchDebounceMs |
500 |
Milliseconds the web editors’ add-card search waits after a keystroke before requesting autocomplete. Non-negative integer; 0 disables the wait. |
The admin editors read the value at runtime. The public site picks it up on the next build-site. Also editable on the admin Settings page.
Update check
Section titled “Update check”| Field | Default | Description |
|---|---|---|
updateCheck |
true |
Whether Ritual looks up the newest release (a day after a successful lookup, sooner after a failed or unfinished one) and prints a one-line notice on stderr when a newer one exists. |
Only release builds check, never development or Docker builds. Set it to false to turn the check and the notice off for this workspace, or set RITUAL_NO_UPDATE_CHECK=1 to turn them off everywhere. See Update notice. Also editable on the admin Settings page.
Cache source
Section titled “Cache source”| Field | Default | Description |
|---|---|---|
cacheSource |
scryfall |
Where card-cache refreshes download from: scryfall (Scryfall’s bulk API directly) or feed (an experimental peer-to-peer cache feed). |
cacheFeedUrl |
— | The feed URL used when cacheSource is feed, and by cache feed fetch without --url. Must be an http(s) URL. |
With feed, a refresh checks the feed’s infohashes and downloads only when they changed. It falls back to Scryfall when the feed is unreachable. When cacheFeedUrl is absent, the built-in default feed is used.
Collection sync
Section titled “Collection sync”| Field | Default | Description |
|---|---|---|
collectionSync.pullTarget |
Inbox |
The collection list collection-sync pull adds new cards to. Non-empty list name. |
Every card that appeared on Archidekt lands in this list, which is created on first use. Only you know which binder it belongs in, so move it from there. The key falls back to its default when omitted. Override it for one run with collection-sync pull --into <list>.
Export presets (exportPresets key)
Section titled “Export presets (exportPresets key)”| Field | Default | Description |
|---|---|---|
exportPresets |
— | Named output shapes for the export command. |
Each preset holds:
format:csv,json,text, ormdcolumns: an array in output order. Always stored; read only bycsv/jsonoutput.headerandquoteAll: the CSV togglesdialect(optional):ritual(the default),archidekt,arena, ormoxfield. See dialects. A dialect spellscsv/jsonvalues (archidektwrites finish and condition the way Archidekt’s importer reads them) and picks thetextdecklist’s line and board form (arenaandmoxfieldwrite bare board markers overN Name (SET) CNlines).mdignores it and always writes Ritual’s canonical markdown.
Manage presets with export --save-preset, the export wizard, or by hand; config set does not manage this key. The key is present only after a preset has been saved. The built-in archidekt preset needs no config; a saved preset of that name shadows it.
{ "exportPresets": { "trade-sheet": { "format": "csv", "columns": ["name", "set", "collectorNumber", "condition", "quantity"], "header": true, "quoteAll": false } }}Admin options (admin key)
Section titled “Admin options (admin key)”The admin key holds settings for the admin server: git integration, network access control, and login rate limiting. Set them from the admin Settings page, with config set admin.<field>, or by hand. Each field falls back to its default when omitted.
Git integration
Section titled “Git integration”| Field | Default | Description |
|---|---|---|
admin.gitEnabled |
false |
Enable git integration for admin-surface file changes. |
admin.gitAutoCommit |
false |
With gitEnabled, auto-commit files written through the admin web UI or the MCP server. |
admin.gitAutoPush |
false |
With gitAutoCommit, push each commit after creating it. |
These keys apply to the admin web UI and the MCP server. CLI commands never auto-commit, including ritual import-changes, which leaves applied changes in your working tree.
Network and authentication security
Section titled “Network and authentication security”The remaining admin keys secure the server. The config property table lists their types and defaults, and the admin command’s Security section explains each one:
- Network options (
admin.trustProxy,admin.secureCookies) - IP and User-Agent allow/deny lists (
admin.ipAllowList,admin.ipDenyList,admin.userAgentAllowList,admin.userAgentDenyList) - Rate limiting (
admin.rateLimitEnabled,admin.rateLimitMaxAttempts,admin.rateLimitWindowMinutes,admin.failedAuthDelayMs)
Site config (site key)
Section titled “Site config (site key)”The site key holds public-site settings in three groups:
- Deployment settings (
ciSystem,deployMode,distDir,detectChanges).ritual init-sitewrites them, and they exist only after you run it. You may change them by hand; runritual migrateafterwards so the generated deploy workflow follows (for example, after settingdetectChanges). - Publish lists (
includeDecks,includeCollections,includeWantedListsand theirexclude*counterparts) decide which listsbuild-sitepublishes. Set them from the admin Settings page, the per-list visibility toggles on the admin Manage Lists page, withconfig set, or by hand. - Other user settings:
bannedPrintings(seeconfig),apiBaseUrl, andsellMode, described below.
{ "decksDir": "./decks", "...": "...", "site": { "ciSystem": "github-actions", "deployMode": "publish-for-me", "distDir": "dist", "detectChanges": false, "includeDecks": ["*"], "includeCollections": ["Red Binder", "ECL"], "includeWantedLists": ["*"], "excludeDecks": ["Untuned Brew"], "excludeCollections": [], "excludeWantedLists": [], "sellMode": false }}| Field | Default | Description |
|---|---|---|
ciSystem |
— | github-actions or manual. |
deployMode |
— | publish-for-me or local-build (github-actions only). |
distDir |
— | The directory containing your built site (github-actions only). |
detectChanges |
— | Whether the workflow runs detect-changes (github-actions only). |
includeDecks |
["*"] |
Which decks build-site publishes (see below). |
includeCollections |
["*"] |
Which collections build-site publishes. |
includeWantedLists |
["*"] |
Which wanted lists build-site publishes. |
excludeDecks |
[] |
Decks to drop even when includeDecks selects them. |
excludeCollections |
[] |
Collections to drop even when includeCollections selects them. |
excludeWantedLists |
[] |
Wanted lists to drop even when includeWantedLists selects them. |
bannedPrintings |
[] |
Printings barred from default-printing selection (see config). |
apiBaseUrl |
— | Base URL of a live backend for a split deployment (see below). |
sellMode |
false |
Whether the sites offer sell mode, in beta (see below). |
Pointing a static build at a live backend (apiBaseUrl)
Section titled “Pointing a static build at a live backend (apiBaseUrl)”site.apiBaseUrl supports a split deployment: the static build lives on a CDN, and a separately hosted serve --api instance provides live list data and card search. When set, build-site writes the URL into index.json, and the site uses that backend. If the backend is unreachable, the site falls back to its static data.
The value must be an http(s) URL (stored without a trailing slash), or the empty string for a site reverse-proxied on the same origin as its API. Leave it unset for a fully static site. serve --api itself needs no configuration.
Offering sell mode (sellMode)
Section titled “Offering sell mode (sellMode)”site.sellMode decides whether the sites offer sell mode: Card Kingdom buylist prices beside each card, the buylist filters, buylist grouping and sorting, and the sell-cart export. It defaults to off, because enabling it makes builds and cache refreshes download and index Card Kingdom’s ~70 MB pricelist. Opt in with:
ritual config set site.sellMode trueor tick Offer sell mode on the admin’s Settings page. Unticking it removes the key rather than storing false.
The cardkingdom entry of priceSources uses the same feed and turns on the same feed behavior: the build’s buylist download and quote generation, the servers’ startup refresh, and the buylist API routes. (cache preload-all refreshes the buylist only under site.sellMode.) Sell mode itself (the toggle, filters, and cart export) is governed by this key alone.
The key governs every surface, the admin site included:
build-siterefreshes the buylist under the run’s--refreshpolicy and writes each list’s buy prices into its JSON, so a fully static site offers sell mode with no backend.serveincludes the same quotes in its live payloads.serve --apireads the key at startup to decide whether to refresh a day-old buylist, so turning it on for a running server leaves that process’s feed unrefreshed until it restarts.adminrefreshes the buylist at startup, offers the editors’ sell toggle and the Refresh buylist card, and answers its/api/sell/*and/api/buylist/*routes. With the key off those routes answer404and the UI hides the surfaces that call them. Both follow a change to this key immediately; no restart or page reload is needed.cache preload-allrefreshes the buylist alongside the card cache.
ritual sell is the exception: running it is itself the request for Card Kingdom prices, so it works whatever this key says.
A single run can opt in without a config write: --sell-mode on build-site, serve, admin, or mcp (see Sell mode). The flag is enable-only and lasts for that process, so config get site.sellMode keeps reporting the stored value, and an admin --sell-mode server keeps offering sell mode even after its Settings checkbox is unticked and saved.
A running server started with the flag reports it: GET /api/config and the MCP get_config tool answer with the stored config as config plus overrides: {"site.sellMode": true}. The CLI never reports overrides, since each command is a fresh process.
Choosing which lists to publish
Section titled “Choosing which lists to publish”Each include* list controls which lists in that category build-site publishes:
-
["*"]means publish everything in that category. This is the default, and applies even when there is nositekey at all. -
An explicit list of display names (the list’s
# Titleheading, falling back to the file name) publishes only the matching lists. Names must match exactly: unlike the--decks-style flags, config names are not folded for case, accents, or separators. An entry that matches no list is a warning, not a failure:⚠️ site.includeDecks lists 'Old Name', which matches no deck in /home/you/ritual/decks — it may have been renamed or removed. -
[]publishes none of that category.
Each category also has an exclude* list. Any display name in it is dropped even when the include* list selects it, including under the wildcard. Exclusion always wins. The exclude lists default to [] and have no wildcard. To publish everything except a few lists, keep includeDecks at ["*"] and add the exceptions to excludeDecks. The admin Manage Lists page’s per-list visibility toggles edit only these exclude lists.
The build-site flags --decks, --collections, and --wanted-lists override these settings for one run. They accept display names or file base names, case- and accent-insensitive. A name they cannot resolve fails the build rather than warning. See When a list will not build.
Ritual bookkeeping (managed key)
Section titled “Ritual bookkeeping (managed key)”The managed key belongs to Ritual. init-site stamps it and migrate keeps it current. Don’t edit it: config set and config unset refuse it, and a PUT /api/config body’s copy is ignored.
{ "managed": { "version": "0.2.0", "migration": 2, "output": "sha256:…" }}| Field | Description |
|---|---|
version |
The Ritual release that last brought the workspace up to date, and the release a “Publish for me” workflow downloads (the CI pin). Development builds never write it. Without a pin, the workflow uses the latest stable release (or the newest prerelease while none exists) and logs a warning naming it. |
migration |
The highest numbered migration applied. Absent means 0. |
output |
A hash of the files Ritual generated for this workspace (workflow, .gitignore entries, agent skills). A different hash means a refresh is due. |
Editing the file
Section titled “Editing the file”Edit ritual.config.json by hand, with config set, or from the admin Settings page. If the file does not exist, create it by hand (a {} document is valid and means “all defaults”) or let config set write the first one. Saving from the admin UI also refreshes the in-memory config, so later admin or CLI commands pick up the change immediately.
ritual init-site writes the deployment portion of the site key, and config set refuses it; edit it by hand and then run ritual migrate to regenerate the workflow. The publish lists (site.includeDecks, site.includeCollections, site.includeWantedLists and their site.exclude* counterparts) are user settings you can edit from the admin Settings page, the Manage Lists visibility toggles, or with config set.
A field missing from the file takes its default.
Validation
Section titled “Validation”Invalid values are rejected where they are entered. config set refuses them with an error. Saving from the admin Settings page (PUT /api/config) rejects the whole update, including unknown top-level keys and wrong-typed fields, before anything is persisted.
Values edited into the file by hand are validated when the config loads. A malformed field (or an invalid admin, site, or managed object) is ignored with a warning, and its default applies for that run. Fix the file to clear the warning. A file whose JSON itself is broken fails the command outright; see Malformed files are a hard error.
When the file is created
Section titled “When the file is created”Reading the config never creates it. The file appears only when something writes a setting:
ritual config set(orconfig unset)ritual init-site, which records thesitekey and themanagedblockritual migrate, which updates themanagedblock- the admin server’s Settings page or the MCP
update_configtool (bothPUT /api/config), and other admin writes such as the Manage Lists visibility toggles ritual export --save-preset
A plain ritual lists in a fresh directory leaves it untouched. A workspace is defined by its decks/, collections/, and wanted/ folders, not by a config file.
The first write puts the full defaulted document on disk, not just the key you set. See config list on why its (default) markers say nothing about which keys are on disk.
Malformed files are a hard error
Section titled “Malformed files are a hard error”If ritual.config.json exists but is not valid JSON, or is not a JSON object, the command fails instead of falling back to defaults. The file is never rewritten:
$ ritual listsritual.config.json is not valid JSON: /home/you/mtg/ritual.config.json JSON Parse error: Property name must be a string literalFix the file (or delete it to fall back to defaults) and try again.The exit code is 1. Failing loudly protects your settings: treating a broken file as “no config” would let the next write replace what is still on disk. Fix the syntax, or delete the file to start again from defaults.
This applies to the whole document. A single field with a bad value is a softer failure; see Validation.
Ritual is unofficial Fan Content permitted under the Fan Content Policy. Not approved or endorsed by Wizards of the Coast. Portions of the materials used are property of Wizards of the Coast. ©Wizards of the Coast LLC.
Card data, images and symbols come from Scryfall, which is not affiliated with this project. Ritual is free software under the AGPL-3.0. License and attribution.