HomeDeveloper docsConfiguration
Project reference 11 min read

Configuration

Installation-local configuration, execution profiles and environment settings.

Documentation · Português

Loading and validation

The source of truth is config/environment.py, config/engine.py, config/settings.py and .env.example. Settings load once at process startup. Exported variables take precedence over .env; interpolation is disabled, so ${VALUE} remains literal. Relative paths resolve against the source root; ~ expands to the service user's home.

Booleans accept 1/true/yes/y/on and 0/false/no/n/off, case-insensitively. Invalid spellings fail startup. Engine numeric limits must be positive. Join-code minimum ≤ default ≤ maximum, and viewport area cannot exceed width × height. GRAVEWRIGHT_PORT must be 1–65535. Public origins normalize the scheme and hostname to lowercase, preserving explicit ports and IPv6 brackets; HTTPS://VTT.EXAMPLE.COM therefore enables the same HTTPS settings as https://vtt.example.com. They reject paths, trailing slashes, credentials, empty ports, control characters, query strings and fragments, including empty ? or # delimiters. These validations do not imply that every setting has the same numeric parser or upper bound.

The Windows runner reads defaults from .env.example, then .env in the project root, with local security settings enforced by its dedicated profile. If the project .env is missing, it generates a complete configuration from .env.example with a new private secret. It never imports configuration from user-data directories. The reference below describes the standard server settings; feature settings also apply to the runner, while its bind address, debug, cookies, host/origin, proxy trust and single-process Channels choices are fixed for local execution. Set the runner's port in the project .env.

Environment reference

Values below match the sample/default configuration. An empty cell shown as “empty” means no configured value. Byte values use binary units in explanations.

VariableDefaultPurpose
GRAVEWRIGHT_HOST127.0.0.1Launcher bind address; main.py only
GRAVEWRIGHT_PORT3000Launcher port, 1–65535; main.py only
DJANGO_DEBUGtrueDjango debug mode; false requires private secret and Redis in standard server settings
DJANGO_ALLOWED_HOSTS127.0.0.1,localhost,[::1]Comma-separated hosts, without scheme or port
GRAVEWRIGHT_PUBLIC_ORIGINemptyPublic HTTP(S) origin; pins the WebSocket origin/Host including port, adds host and CSRF trusted origin, enables Secure cookies for HTTPS
GRAVEWRIGHT_DATABASEdata/gravewright.sqlite3SQLite filename; relative to source root; :memory: is supported
GRAVEWRIGHT_MEDIA_ROOTdata/mediaPrivate upload/module storage, relative to source root
GRAVEWRIGHT_MODULES_ROOTGRAVEWRIGHT_MEDIA_ROOT/modulesPrivate folder where module ZIP archives and extracted packages are installed; may be absolute or source-root-relative
GRAVEWRIGHT_API_MODULES_ROOTextensions/apiDefault folder for browser module sources, relative to source root; installed signed packages still live under the media root
GRAVEWRIGHT_DJANGO_MODULES_ROOTextensions/djangoDefault folder for trusted Django apps, relative to source root; appended to sys.path when it exists
GRAVEWRIGHT_SERVER_APPSemptyComma-separated import paths of trusted Django apps added to INSTALLED_APPS
GRAVEWRIGHT_SERVER_APP_PATHSemptyExtra package roots for those apps (: on Linux/macOS, ; on Windows); they precede the default Django folder
GRAVEWRIGHT_AUTH_MAX_ATTEMPTS30Authentication attempts per client-IP window
GRAVEWRIGHT_AUTH_WINDOW_SECONDS300Authentication window in seconds
GRAVEWRIGHT_REDIS_URLemptyChannels Redis URL; standard server settings allow empty/in-memory delivery only in debug
APP_NAMEGravewrightFallback product name; persisted host preferences can override
DEFAULT_LOCALEenFallback locale; current supported host locale is en
PRIVACY_ENABLEDfalseInitial privacy publication setting; the saved Privacy checkbox takes precedence
CAMPAIGN_JOIN_CODE_ENABLEDtrueEnable campaign join-code workflow
CAMPAIGN_CLONE_ENABLEDtrueEnable campaign cloning
CAMPAIGN_SNAPSHOTS_ENABLEDtrueEnable campaign snapshots
CAMPAIGN_SNAPSHOT_RETENTION20Maximum retained snapshots per campaign; pruned during snapshot creation
ADMINISTRATIVE_AUDIT_ENABLEDtrueEnable administrative audit records
ADMINISTRATIVE_AUDIT_RETENTION_DAYS180Audit retention in days; pruning occurs when audit code runs
TARGETED_HANDOUTS_ENABLEDtrueEnable targeted journal/handout sharing
CAMPAIGN_EXPORT_ENABLEDtrueEnable portable campaign exports
JOIN_CODE_DEFAULT_EXPIRES_HOURS168Default join-code lifetime in hours
JOIN_CODE_MIN_EXPIRES_HOURS1Minimum requested join-code lifetime in hours
JOIN_CODE_MAX_EXPIRES_HOURS720Maximum requested join-code lifetime in hours
JOIN_CODE_MAX_USES_LIMIT1000Upper limit for a join code's allowed uses
JOIN_CODE_REDEEM_MAX_ATTEMPTS10Maximum code redemption attempts per window
JOIN_CODE_REDEEM_WINDOW_SECONDS600Code redemption attempt window in seconds
TRUSTED_PROXIESemptyComma-separated proxy IPs/CIDRs trusted for forwarded client IPs and ASGI HTTP/WebSocket schemes; empty ignores forwarding headers
DATABASE_POOL_TIMEOUT30SQLite lock wait in seconds; this is not a connection pool size
DATABASE_ECHOfalseConfigure django.db.backends debug logging; actual SQL capture also follows Django's debug cursor behavior
WS_MAX_MESSAGE_MB0.0625Application limit per WebSocket message in MB (1 MB = 1024 * 1024 bytes)
WS_COMMANDS_PER_SECOND20WebSocket command rate per connection
WS_BURST_COMMANDS40WebSocket command burst capacity
APP_DEBUGfalseApplication/table renderer diagnostics; independent of DJANGO_DEBUG
COMMAND_PALETTE_ENABLEDtrueEnable the table command palette
LOBBY_READY_CHECK_ENABLEDtrueEnable lobby readiness controls
DYNAMIC_LIGHTING_ENABLEDtrueEnable dynamic-lighting feature
GRAVEWRIGHT_AUTOMATION_WORKER_ENABLEDtrueRun the ASGI background worker for persistent Blueprint Delay continuations; false leaves pending waits stored without executing them
SCENE_VIEWPORT_MAX_WIDTH_CHUNKS16Maximum streamed viewport width in chunks
SCENE_VIEWPORT_MAX_HEIGHT_CHUNKS16Maximum streamed viewport height in chunks
SCENE_VIEWPORT_MAX_AREA_CHUNKS256Maximum streamed viewport area in chunks
FOG_MAX_OPS_PER_COMMAND64Maximum fog operations per command
FOG_MAX_POLYGON_POINTS128Maximum points per fog polygon
FOG_MAX_COORDINATE_ABS100000Maximum absolute fog coordinate
FOG_REQUIRE_EXPECTED_VERSIONtrueRequire expected scene-state version for fog changes
TOKEN_CREATE_MANY_MAX50Maximum tokens per batch creation command
BOARD_MARKERS_MAX_PER_SCENE500Maximum markers per scene
BOARD_MEASUREMENTS_MAX_PER_USER50Maximum persisted board measurements per user
JOURNAL_IMAGE_MAX_MB10Journal image attachment limit in MB (1 MB = 1024 * 1024 bytes)
JOURNAL_PDF_MAX_MB25Journal PDF attachment limit in MB (1 MB = 1024 * 1024 bytes)
PDF_SYSTEM_UPLOAD_MAX_MB10PDF character sheet upload limit in MB (1 MB = 1024 * 1024 bytes)
GRAVEWRIGHT_MAP_MAX_PIXELS64000000Decoded map pixel limit
GRAVEWRIGHT_MAP_PILLOW_MAX_PIXELS64000000Additional decoded-pixel ceiling when using the Pillow tiler
GRAVEWRIGHT_MAP_TILERautoMap tiler: auto, vips or pillow
MAP_UPLOAD_MAX_MB51200Map upload limit in MB (50 GiB); other limits still apply
MAP_IMAGE_MAX_WIDTH500000Maximum source map width in pixels
MAP_IMAGE_MAX_HEIGHT500000Maximum source map height in pixels
MAP_MAX_TILE_COUNT4096Maximum generated map tiles
GRAVEWRIGHT_MARKETPLACE_URLemptyHTTPS URL of the module catalog JSON
GRAVEWRIGHT_MARKETPLACE_KEYS_FILEemptyLocal Ed25519 trust-key JSON path; relative to source root
GRAVEWRIGHT_RELEASES_REPOSITORYGravewright/gravewrightowner/repo used for compatible Django release discovery; empty leaves it unconfigured

The native installer configures the official Marketplace automatically when no catalog is configured. Existing custom catalogs are preserved.

Two optional commented settings are also recognized:

VariableDefaultPurpose
DJANGO_SECRET_KEYDevelopment-only insecure fallbackSet a persistent random private value outside debug mode; do not use the fallback in production
DJANGO_SECURE_COOKIESfalseEnable secure cookies; a configured HTTPS public origin always enables them. This flag does not configure TLS or the WebSocket origin

GRAVEWRIGHT_CONTAINER is read by release discovery to label the install format when set to 1, true or yes; it does not configure a container runtime or provide automatic updates. DJANGO_SETTINGS_MODULE selects the Python settings module; entry points default to config.settings.

Blueprint Delay worker

The ASGI host starts the Delay worker automatically. Daphne uses its reactor startup/shutdown hooks, including normal Runner execution; other ASGI hosts must deliver lifespan startup/shutdown events. Importing Django or running a management command does not start a scheduler. The worker checks due database rows every 250 ms, processing at most 20 per pass; actual execution can be later under load or after downtime. It does not require connected browsers, HTTP polling, Redis jobs or a separately launched process.

Pending waits live in the configured SQLite database and survive a server restart. GRAVEWRIGHT_AUTOMATION_WORKER_ENABLED=false disables processing for that process without discarding the queue. Scene-level Pause automation instead holds only that scene's waits until resume. The updater's existing managed maintenance flag holds the queue through health verification until the update is committed or rolled back. Apply the normal migrations before starting the host; the worker reports database failures in server logs and retries, rather than treating them as successful executions. See Persistent Delay for cancellation and copying.

Map tiling and large images

GRAVEWRIGHT_MAP_MAX_PIXELS is the exact map pixel limit. At startup, Pillow's header guard is raised to at least that configured value so it does not impose an unrelated lower limit. Exceeding either pixel guard reports a pixel-limit error. The independent byte, dimension and tile-count limits still apply. Both pixel settings must be positive; restart the server after changing them.

GRAVEWRIGHT_MAP_TILER=auto uses libvips when available and otherwise uses Pillow. pillow always selects Pillow; vips requires libvips and fails startup when it is unavailable. The default dependencies and native installers do not select the optional vips extra. For a source environment, install it with:

bash
uv sync --locked --extra vips

For a native Runner, install into the Python environment named by its generated launcher; the checkout's .venv may be a different environment. Keep --extra vips on subsequent dependency syncs. A plain exact uv sync, a native reinstall or preparation for a managed core update selects the default dependencies and can remove or omit the extra. Check availability again after those operations; setting GRAVEWRIGHT_MAP_TILER=vips does not install dependencies.

Pillow decodes the entire map. Its additional GRAVEWRIGHT_MAP_PILLOW_MAX_PIXELS ceiling defaults to 64 million pixels, even if the overall map limit is higher. A larger image requires libvips or an explicit increase to that ceiling after assessing available memory. This pixel ceiling bounds accepted image size, not resident memory: decoding, orientation, resampling and codecs also allocate memory. Libvips streams ordinary maps in strips, but formats, codecs and EXIF rotations can require additional memory and temporary disk space.

Only one map pyramid is generated at a time per server process, including decoding. Separate workers can still process maps concurrently. Both engines produce lossless WebP tiles with the same 512-pixel layout and partial edge tiles; reduced levels can differ because the resampling filters differ.

Native tiling stages the source image and generated tiles under installation-root data/runner/tmp/. An explicit GRAVEWRIGHT_RUNNER_ROOT changes the tool root, and a Django FILE_UPLOAD_TEMP_DIR override takes precedence for tiling scratch space; relative paths resolve against the installation root. The directory must be writable and have room for the source, staged tiles and any codec spill files while final tiles are saved to media storage. Storage or native generation failures remain server errors, and already saved tiles are removed if the upload fails. See deployment for synchronous upload timeouts and capacity planning.

Settings without environment switches

The following are configured in Python, not loaded automatically from similarly named .env entries:

SettingCurrent behavior
DATABASES.default.TEST.NAMEdata/test-gravewright.sqlite3, regardless of GRAVEWRIGHT_DATABASE
GRAVEWRIGHT_HEARTBEAT_SECONDS / GRAVEWRIGHT_PRESENCE_TTL5-second heartbeat / 20-second presence TTL
DATA_UPLOAD_MAX_MEMORY_SIZE8 MiB for Django request-data handling; not a universal uploaded-file byte cap
SESSION_COOKIE_AGE12 hours; session data is stored in the database
TIME_ZONEAmerica/Sao_Paulo, with timezone-aware datetimes
STATIC_ROOTSource-root staticfiles/
GRAVEWRIGHT_CONTENT_ROOTOptional Django settings override used by the content catalog; fallback is data/vtt/compendiums/. Merely exporting this name in .env has no effect
MAILERS.defaultConsole email backend

Authentication bodies are separately limited to 16 KiB in middleware. Archive imports/exports have limits in gravewright/administration/archives.py. Map uploads have independent byte, decoded-image and generated-tile bounds. Check the owning code before assuming one limit governs every path.

Persisted host preferences

gravewright.administration.preferences stores app identity, update channels and privacy content in the HostSettings singleton. APP_NAME and DEFAULT_LOCALE provide fallbacks, not forced overrides of every saved value. The supported locale list is currently ('en',). Setting DEFAULT_LOCALE=pt-BR does not add a Portuguese UI.

Privacy publication is enabled when either the environment flag or the stored privacy flag is true. Snapshot/audit retention is enforced by the relevant operation paths, not a background scheduler supplied by this project. User preferences and campaign settings are separate from these host settings.

For HTTPS, set the exact browser-facing public origin, including any non-default port. Other entries in DJANGO_ALLOWED_HOSTS do not become additional permitted WebSocket origins when a public origin is configured. Without it, WebSocket validation derives the origin from the ASGI scheme and Host; an absent scheme defaults to HTTP.

The ASGI proxy adapter accepts exactly one X-Forwarded-Proto header with value http or https only from the immediate socket peer in TRUSTED_PROXIES. It ignores duplicate, comma-separated, malformed or untrusted values. Keep Daphne's --proxy-headers disabled so the original peer remains available for this check. The same trust list controls client-IP forwarding. See deployment for a TLS proxy example, Redis and storage requirements.