Configuration (config.json)
Config file location, load rules, and key reference for config.json.
Use this as a reference for config.json.
Defaults and normalization come from cw_platform/config_base.py.
Need the raw, generated default JSON blocks? See Default config values.
config.json contains tokens and secrets.
Redact it before sharing logs or screenshots.
Avoid editing config.json by hand.
Most settings are configurable in the UI.
At a glance
Config file
<CONFIG_BASE>/config.json
Created if missing.
Docker default
/config/config.json
Used when /app exists.
Manual edits
Avoid
UI covers most settings.
Secrets
Tokens and password hashes
Redact before sharing.
Config file location
CrossWatch chooses the config base directory in this order.
1
CONFIG_BASE env var is set
CONFIG_BASE value
2
Running in Docker image (/app exists)
/config
3
Running from source
Project root (one level above cw_platform/)
The file path is always:
<CONFIG_BASE>/config.json
Load and save behavior
Defaults + deep merge
Your config overlays DEFAULT_CFG. Missing keys are filled in.
Unknown keys preserved
Extra keys are kept. Useful for experiments.
Version stamping
version is rewritten on load/save.
Runtime normalization
Some fields are auto-added, clamped, or coerced.
Pair feature normalization
pairs[].features.* are coerced to a uniform shape.
Atomic saves
Writes use a temp file then replace config.json.
Redaction helper
Sensitive fields can be masked for UI/log output.
Defaults + deep merge
On startup, CrossWatch:
Loads
config.json.Deep-merges it over
DEFAULT_CFG.
Merge rules:
Dict + dict merges recursively.
Any other type overrides the default.
Version stamping
On load/save:
version= app version (leadingvremoved)
1
api.versionAPI.CURRENT_VERSION
2
APP_VERSION env var
3
Fallback v0.7.0
Runtime normalization (auto-added and clamped)
These behaviors happen during config load/save.
They can rewrite values in config.json.
security.webhook_ids
If missing or too short, IDs are auto-generated.
They use token_urlsafe(24).
Keys:
security.webhook_ids.plextraktsecurity.webhook_ids.jellyfintraktsecurity.webhook_ids.embytraktsecurity.webhook_ids.plexwatcher
First-run UI marker
On first run only, the loader adds:
Normalized / clamped values
scheduling.modeis normalized to one of:disabledhourlyevery_n_hoursdaily_time
scheduling.every_n_hoursis clamped to1..12scheduling.daily_timefalls back to03:30if invalidui.protocolis forced tohttporhttpsui.tls.valid_daysis clamped to1..3650Provider
*.rate_limit.*values are coerced to numbers.Then clamped to a minimum of
0.
Pair feature normalization
Each feature becomes:
true
Enabled, adds on, removes off
false
Disabled (enable/add/remove all false)
Missing keys
Defaults: enable:true, add:true, remove:false
Invalid values
Feature disabled
Ratings has extra fields.
See Ratings feature.
Redaction
redact_config(cfg) replaces sensitive values with ••••••••.
app_auth.password.hash
Password hash
app_auth.password.salt
Password salt
app_auth.session.token_hash
Session token hash
Key reference
Top-level
version
string
Managed by the app.
security
object
Runtime-generated webhook IDs.
pairs
array
Pair definitions created in the UI.
plex, trakt, …
object
Provider connection and tuning.
sync
object
Global orchestrator defaults.
runtime
object
Logging and guardrails.
metadata
object
Metadata lookup settings (TMDb).
scrobble
object
Real-time progress forwarding.
scheduling
object
Periodic runs.
ui
object
UI toggles.
app_auth
object
Optional UI login.
Security (security)
security)This node stores webhook route IDs.
CrossWatch can auto-generate them.
security.webhook_ids.plextrakt
string
Legacy Plex → Trakt webhook path id.
security.webhook_ids.jellyfintrakt
string
Legacy Jellyfin → Trakt webhook path id.
security.webhook_ids.embytrakt
string
Legacy Emby → Trakt webhook path id.
security.webhook_ids.plexwatcher
string
Plex Watcher/webhook path id.
If you change these IDs, update your webhook URLs.
Restart CrossWatch after edits.
Providers
plex
server URL, token, tuning
Sync + scrobble source
jellyfin / emby
server URL, token, tuning
Sync + scrobble source
trakt / simkl / mdblist
OAuth/API keys
Tracker targets/sources
anilist
OAuth/API keys
Anime tracker
tmdb
API key
Matching support
tmdb_sync
API key + session
TMDb account sync
tautulli
URL + API key
History import
crosswatch
local paths + snapshot rules
Local backup provider
Provider profiles
Most providers can store multiple profiles under an instances map (config key name).
The implicit profile id is
default.Additional profile ids are named like
PLEX-P01,TRAKT-P02, etc.
Profiles are used by:
sync pairs (
source_instance,target_instance)Watcher routes (
provider_instance,sink_instance)
Guide: Profiles
Plex
Connection
plex.server_url
string
Example: http://192.168.1.10:32400
plex.verify_ssl
bool
Keep false for self-signed.
plex.account_token
string
Set via UI PIN login.
plex.client_id
string
Client identifier.
plex.machine_id
string
Targets a specific server.
plex.username
string
Plex Home profile name.
plex.account_id
string
Server-local account ID.
plex.home_pin
string
Optional Plex Home PIN.
plex.timeout
number
Seconds.
plex.max_retries
int
Retry budget.
plex.fallback_GUID
bool
Discover fallback for missing GUID mapping.
Scrobble filters
plex.scrobble.libraries
array
Empty means all libraries.
History
plex.history.libraries
array
Whitelist of library GUIDs. Empty = all.
plex.history.include_marked_watched
bool
Include Plex “marked watched” state (add-only behavior).
plex.history_workers
int
Parallel workers for history indexing. 12–16 is ideal on a local NAS.
Ratings
plex.ratings.libraries
array
Whitelist of library GUIDs. Empty = all.
plex.rating_workers
int
Parallel workers for ratings indexing. 12–16 is ideal on a local NAS.
Watchlist (Discover-driven)
plex.watchlist_allow_pms_fallback
bool
Allow PMS watchlist fallback when needed. Keep false for strict Discover-only behavior.
plex.watchlist_page_size
int
Discover page size (100–200). Higher is faster, but can trigger 504s.
plex.watchlist_query_limit
int
Max Discover search results per query (10–25).
plex.watchlist_write_delay_ms
int
Optional pacing between Discover writes. Set 50–150 if you hit 429/5xx.
plex.watchlist_title_query
bool
Use title/slug tokens for Discover candidate fetching.
plex.watchlist_use_metadata_match
bool
Try PMS /library/metadata/matches first, then fallback to Discover.
plex.watchlist_guid_priority
array
GUID resolution order (first match wins).
TMDb (Sync)
Used only when you connect TMDb (Sync) for watchlist/ratings syncing.
tmdb_sync.api_key
string
TMDb v3 API key.
tmdb_sync.session_id
string
User session ID from the connect flow.
tmdb_sync.account_id
string
Optional. Auto-discovered if missing.
tmdb_sync.timeout
number
HTTP timeout in seconds.
tmdb_sync.max_retries
int
Retry budget for transient HTTP errors.
tmdb_sync.debug
bool
Enables extra TMDb sync debug logging.
Trakt
OAuth
trakt.client_id
string
From your Trakt app.
trakt.client_secret
string
From your Trakt app.
trakt.access_token
string
OAuth2 access token.
trakt.refresh_token
string
OAuth2 refresh token.
trakt.scope
string
Usually public or private.
trakt.token_type
string
Usually Bearer.
trakt.expires_at
int
Epoch seconds.
trakt._pending_device
object
Temporary device code state (PIN flow).
HTTP
trakt.timeout
int
HTTP timeout (seconds).
trakt.max_retries
int
Retry budget (429/5xx backoff).
Rate limiting
trakt.rate_limit.get_per_sec
number
GET requests / second
trakt.rate_limit.post_per_sec
number
Writes / second
Rate-limit defaults and tuning: Provider rate limiting
Watchlist
trakt.watchlist_use_etag
bool
Use ETag + local shadow to skip unchanged lists.
trakt.watchlist_shadow_ttl_hours
int
Refresh ETag baseline periodically even if 304s keep coming.
trakt.watchlist_batch_size
int
Chunk size for add/remove to reduce rate spikes.
trakt.watchlist_log_rate_limits
bool
Log X-RateLimit-* and Retry-After when present.
trakt.watchlist_freeze_details
bool
Persist last status & ids for debugging.
Ratings
trakt.ratings_per_page
int
Items per page when indexing (10–100).
trakt.ratings_max_pages
int
Safety cap per type.
trakt.ratings_chunk_size
int
Batch size for POST/REMOVE.
History
trakt.history_per_page
int
Max allowed by Trakt (100).
trakt.history_max_pages
int
Safety cap for large histories.
trakt.history_unresolved
bool
Enable unresolved “freeze” files.
trakt.history_number_fallback
bool
Episode number fallback if episode IDs are missing.
trakt.history_collection
bool
Mirror history adds into your Trakt Collection.
SIMKL
simkl.access_token
string
OAuth2 access token.
simkl.refresh_token
string
OAuth2 refresh token.
simkl.token_expires_at
int
Epoch seconds.
simkl.client_id
string
From your SIMKL app.
simkl.client_secret
string
From your SIMKL app.
simkl.date_from
string
YYYY-MM-DD (optional backfill start).
Rate limiting
simkl.rate_limit.get_per_sec
number
GET requests / second
simkl.rate_limit.post_per_sec
number
Writes / second
Rate-limit defaults and tuning: Provider rate limiting
AniList
anilist.client_id
string
From your AniList app.
anilist.client_secret
string
From your AniList app.
anilist.access_token
string
OAuth access token.
anilist.user
object
Cached viewer object (id/name).
MDBList
Connection
mdblist.api_key
string
Your MDBList API key.
mdblist.timeout
int
HTTP timeout (seconds).
mdblist.max_retries
int
Retry budget.
Rate limiting
mdblist.rate_limit.get_per_sec
number
GET requests / second
mdblist.rate_limit.post_per_sec
number
Writes / second
Rate-limit defaults and tuning: Provider rate limiting
Watchlist
mdblist.watchlist_shadow_ttl_hours
int
Shadow TTL (hours). 0 disables.
mdblist.watchlist_shadow_validate
bool
Validate shadow on every run.
mdblist.watchlist_page_size
int
GET page size for /watchlist/items.
mdblist.watchlist_batch_size
int
Batch size for add/remove writes.
mdblist.watchlist_freeze_details
bool
Store extra details for “not_found” freezes.
Ratings
mdblist.ratings_per_page
int
Items per page when indexing.
mdblist.ratings_max_pages
int
Max pages to fetch (safety cap).
mdblist.ratings_chunk_size
int
Batch size for POST/REMOVE.
mdblist.ratings_write_delay_ms
int
Optional pacing between writes.
mdblist.ratings_max_backoff_ms
int
Max backoff time for retries.
mdblist.ratings_since
string
First-run baseline; watermark overrides after.
History
mdblist.history_per_page
int
Items per page for watched delta.
mdblist.history_max_pages
int
Max pages to fetch (safety cap).
mdblist.history_chunk_size
int
Batch size for watched/unwatched writes.
mdblist.history_write_delay_ms
int
Optional pacing between writes.
mdblist.history_max_backoff_ms
int
Max backoff time for retries.
mdblist.history_since
string
First-run baseline; watermark overrides after.
Tautulli
Connection
tautulli.server_url
string
Example: http://host:8181
tautulli.api_key
string
Tautulli API key.
tautulli.verify_ssl
bool
Verify TLS certificates.
tautulli.timeout
number
HTTP timeout (seconds).
tautulli.max_retries
int
Retry budget.
History import
tautulli.history.user_id
string
Optional user filter.
tautulli.history.per_page
int
Tautulli history page size.
tautulli.history.max_pages
int
Safety cap.
Jellyfin / Emby
Same structure.
Connection
*.server
string
Base URL.
*.access_token
string
Auth token.
*.user_id
string
Active user.
*.device_id
string
Client device id.
*.username
string
Optional login username.
*.user
string
Optional display name (hydrated after auth).
*.verify_ssl
bool
TLS verification.
*.timeout
number
Seconds.
*.max_retries
int
Retry budget.
Scrobble filters
*.scrobble.libraries
array
Whitelist of library GUIDs. Empty = all.
Watchlist (emulated)
*.watchlist.mode
string
favorites | playlist | collections
*.watchlist.playlist_name
string
Used when mode is playlist.
*.watchlist.watchlist_query_limit
int
Batch size.
*.watchlist.watchlist_write_delay_ms
int
Delay between writes.
*.watchlist.watchlist_guid_priority
array
ID match order.
History
*.history.history_query_limit
int
Batch size.
*.history.history_write_delay_ms
int
Delay between writes.
*.history.history_guid_priority
array
ID match order.
*.history.libraries
array
Whitelist of library GUIDs. Empty = all.
Ratings
*.ratings.ratings_query_limit
int
Ratings query limit (default 2000).
*.ratings.libraries
array
Whitelist of library GUIDs. Empty = all.
CrossWatch (local provider)
crosswatch.root_dir
string
Local provider folder.
crosswatch.enabled
bool
Enables provider.
crosswatch.auto_snapshot
bool
Snapshot before writes.
crosswatch.retention_days
int
0 keeps forever.
crosswatch.max_snapshots
int
0 unlimited.
crosswatch.restore_watchlist
string
latest, empty, or a specific snapshot stem.
crosswatch.restore_history
string
latest, empty, or a specific snapshot stem.
crosswatch.restore_ratings
string
latest, empty, or a specific snapshot stem.
Sync (sync)
sync)Global defaults. Pair-level settings override these by design.
sync.enable_add
bool
Allow additions by default.
sync.enable_remove
bool
Safer default: disabled unless you opt in.
sync.one_way_remove_mode
string
One-way delete semantics: source_deletes (default) or mirror.
sync.verify_after_write
bool
When supported, re-check destination after writes.
sync.dry_run
bool
Plan and log only. No writes.
sync.drop_guard
bool
Guard against empty/suspect snapshots.
sync.allow_mass_delete
bool
If false, block large delete plans.
sync.tombstone_ttl_days
int
How long “observed deletes” stay valid.
sync.include_observed_deletes
bool
If false, skip processing observed deletes for the run.
One-way delete modes
This setting matters only for one-way pairs.
It is ignored for two-way sync.
It is also gated by remove toggles:
Global:
sync.enable_removePair feature:
pairs[].features.<feature>.remove
If either is false, no deletes happen.
source_deletes (default)
Safest behavior.
CrossWatch removes items from the destination only when the item looks like a real deletion on the source.
That avoids deleting:
destination-only items
items you manually added on the destination
mirror
Strict mirroring.
CrossWatch removes anything present on the destination but missing on the source.
This is destructive.
Example (Python-style)
Force mirroring (JSON)
Set:
mirror can delete destination-only items.
Use it only if you really want strict mirroring.
Two-way defaults (optional)
sync.bidirectional.enabled
bool
Default off. Pairs still decide final mode.
sync.bidirectional.mode
string
Placeholder default (usually two-way).
sync.bidirectional.source_of_truth
string
Optional tie-breaker if you enforce strict authority.
Blackbox (including flapper protection)
sync.blackbox.enabled
bool
Turn off to fully disable blackbox logic.
sync.blackbox.promote_after
int
Promote after N consecutive unresolved/fail events.
sync.blackbox.unresolved_days
int
Minimum unresolved age before it counts (0 = immediate).
sync.blackbox.pair_scoped
bool
Track per pair to avoid blocking the same title elsewhere.
sync.blackbox.cooldown_days
int
Auto-prune after cooldown.
sync.blackbox.block_adds
bool
Block planned ADDs while blackboxed.
sync.blackbox.block_removes
bool
Block planned REMOVEs while blackboxed.
Runtime (runtime)
runtime)runtime.debug
bool
Extra verbose logging (debug level).
runtime.debug_http
bool
Extra verbose HTTP logging.
runtime.debug_mods
bool
Extra verbose MOD logs for Synchronization Providers.
runtime.state_dir
string
Optional override for state dir. Avoid for containers.
runtime.telemetry.enabled
bool
Usage stats.
runtime.snapshot_ttl_sec
int
Reuse snapshots within this window.
runtime.apply_chunk_size
int
Batch size for apply.
runtime.apply_chunk_pause_ms
int
Pause between chunks.
runtime.apply_chunk_size_by_provider
object
Per-provider overrides (example: SIMKL).
runtime.suspect_min_prev
int
Minimum previous size to enable suspect guard.
runtime.suspect_shrink_ratio
number
Shrink ratio to trigger suspect guard.
Metadata (metadata)
metadata)metadata.locale
string
Example: en-US.
metadata.ttl_hours
int
Coarse resolver cache TTL.
Scrobble (scrobble)
scrobble)scrobble.enabled
bool
Master toggle.
scrobble.mode
string
watch or webhook.
scrobble.delete_plex
bool
Legacy name but still valid. Auto-remove movies from watchlists.
scrobble.delete_plex_types
array
Legacy name but still valid. Types: movie, show, episode.
Watcher mode (scrobble.watch)
scrobble.watch)scrobble.watch.autostart
bool
Start watcher on boot if enabled + mode=watch.
scrobble.watch.routes
array
Routes table (recommended).
scrobble.watch.provider
string
Legacy fallback. Prefer routes.
scrobble.watch.sink
string
Legacy fallback. Prefer routes.
scrobble.watch.plex_simkl_ratings
bool
Forward Plex ratings to SIMKL.
scrobble.watch.plex_trakt_ratings
bool
Forward Plex ratings to Trakt.
scrobble.watch.plex_mdblist_ratings
bool
Forward Plex ratings to MDBList.
scrobble.watch.pause_debounce_seconds
int
Ignore micro-pauses just after start.
scrobble.watch.suppress_start_at
int
Kill near-end start flaps (credits).
scrobble.watch.filters.username_whitelist
array
["name", "id:123", "uuid:abcd…"]
scrobble.watch.filters.server_uuid
string
Restrict to a specific server (Plex).
Route shape (each entry in scrobble.watch.routes[]):
As of v0.9.15, configure routes manually.
Legacy Watcher fields are not auto-converted anymore.
Webhook mode (scrobble.webhook)
scrobble.webhook)scrobble.webhook.pause_debounce_seconds
int
Ignore micro-pauses.
scrobble.webhook.suppress_start_at
int
Suppress near-end start flaps.
scrobble.webhook.suppress_autoplay_seconds
int
Plex autoplay suppression window.
scrobble.webhook.probe_session_progress
bool
Probe Plex sessions to match item by ratingKey/sessionKey.
scrobble.webhook.plex_trakt_ratings
bool
Forward Plex ratings to Trakt in webhook mode.
scrobble.webhook.filters_plex.username_whitelist
array
Accepted Account.title values. Empty = allow all.
scrobble.webhook.filters_plex.server_uuid
string
Restrict to a specific Plex server.
Progress rules (scrobble.trakt)
scrobble.trakt)Used by Trakt, SIMKL, and MDBList sinks.
scrobble.trakt.progress_step
int
Send progress in % steps.
scrobble.trakt.stop_pause_threshold
int
STOP below this becomes PAUSE.
scrobble.trakt.force_stop_at
int
STOP at/above this is forced.
scrobble.trakt.regress_tolerance_percent
int
Clamp small backwards jumps.
Scheduling (scheduling)
scheduling)scheduling.enabled
bool
Master toggle for periodic runs.
scheduling.mode
string
disabled, hourly, every_n_hours, daily_time.
scheduling.every_n_hours
int
When mode=every_n_hours, run every N hours (1–12).
scheduling.daily_time
string
When mode=daily_time, run at HH:MM (24h).
scheduling.advanced
object
Ordered per-pair schedule (optional).
Advanced scheduling (scheduling.advanced)
scheduling.advanced)Use this only if you want strict control.
It runs jobs in order.
scheduling.advanced.enabled(bool)scheduling.advanced.jobs[](array)
Related: Scheduling
UI (ui)
ui)ui.show_watchlist_preview
bool
Show Watchlist Preview on Main tab.
ui.show_playingcard
bool
Show Now Playing card on Main tab.
ui.show_AI
bool
Show ASK AI (GitBook) in UI.
ui.protocol
string
http or https.
TLS (ui.tls)
ui.tls)ui.tls.self_signed
bool
Auto-generate a self-signed certificate when missing.
ui.tls.hostname
string
Used for certificate CN/SAN.
ui.tls.valid_days
int
Certificate validity (days).
ui.tls.cert_file
string
Optional override path to a PEM cert.
ui.tls.key_file
string
Optional override path to a PEM key.
UI auth (app_auth)
app_auth)app_auth.enabled
bool
Enables login.
app_auth.username
string
Required when enabled.
Pairs (pairs[])
pairs[])Each entry is one sync connection.
id
string
Generated by UI/API.
enabled
bool
Master switch.
source
string
Provider name.
source_instance
string
Provider profile id (defaults to default).
target
string
Provider name.
target_instance
string
Provider profile id (defaults to default).
mode
string
one-way or two-way.
features
object
Per-feature rules.
Feature format
enable
bool
Run feature at all.
add
bool
Allow adds to target.
remove
bool
Allow removes on target.
Example:
Special: pairs[].features.ratings
pairs[].features.ratingstypes
array
movies, shows, seasons, episodes. all expands.
mode
string
only_new or from_date.
from_date
string
Used only when mode = "from_date".
Last updated
Was this helpful?