v0.156.1
  1"""
  2Default Plain settings. Override these with settings in the module pointed to
  3by the PLAIN_SETTINGS_MODULE environment variable.
  4"""
  5
  6import os
  7
  8from .secret import Secret
  9from .utils import get_app_info_from_pyproject
 10
 11# MARK: Core Settings
 12
 13DEBUG: bool = False
 14
 15name, version = get_app_info_from_pyproject()
 16NAME: str = name
 17VERSION: str = version
 18
 19# List of strings representing installed packages.
 20INSTALLED_PACKAGES: list[str] = []
 21
 22URLS_ROUTER: str
 23
 24# Whether routes have trailing slashes by default. Routes can override
 25# per-endpoint with `path(..., force_slash=True|False)`. Requests that
 26# disagree with a route's effective form are 308-redirected to it.
 27URLS_TRAILING_SLASH: bool = False
 28
 29# List of environment variable prefixes to check for settings.
 30# Settings can be configured via environment variables using these prefixes.
 31# Example: ENV_SETTINGS_PREFIXES = ["PLAIN_", "MYAPP_"]
 32# Then both PLAIN_DEBUG and MYAPP_DEBUG would set the DEBUG setting.
 33ENV_SETTINGS_PREFIXES: list[str] = ["PLAIN_"]
 34
 35# MARK: HTTP and Security
 36
 37# Hosts/domain names that are valid for this site.
 38# - An empty list [] allows all hosts (useful for development).
 39# - ".example.com" matches example.com and all subdomains
 40# - "192.168.1.0/24" matches IP addresses in that CIDR range
 41ALLOWED_HOSTS: list[str] = []
 42
 43# Path for the built-in healthcheck endpoint.
 44# When set, the server responds directly on the event loop with a 200 "ok"
 45# before the thread pool or any middleware runs.
 46# Example: HEALTHCHECK_PATH = "/up/"
 47HEALTHCHECK_PATH: str = ""
 48
 49# Default headers for all responses.
 50# Header values can include {request.attribute} placeholders for dynamic content.
 51# Example: "script-src 'nonce-{request.csp_nonce}'" will use the request's nonce.
 52# Views can override, remove, or extend these headers - see plain/http/README.md
 53# for customization patterns.
 54DEFAULT_RESPONSE_HEADERS: dict = {
 55    # "Content-Security-Policy": "default-src 'self'; script-src 'self' 'nonce-{request.csp_nonce}'",
 56    # https://hstspreload.org/
 57    # "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
 58    "Cross-Origin-Opener-Policy": "same-origin",
 59    "Referrer-Policy": "same-origin",
 60    "X-Content-Type-Options": "nosniff",
 61    "X-Frame-Options": "DENY",
 62}
 63
 64# Whether to redirect all non-HTTPS requests to HTTPS (blanket redirect).
 65# For anything more advanced (custom host, path exemptions, etc.), write
 66# your own middleware.
 67HTTPS_REDIRECT_ENABLED: bool = True
 68
 69# If your Plain app is behind a proxy that sets a header to specify secure
 70# connections, AND that proxy ensures that user-submitted headers with the
 71# same name are ignored (so that people can't spoof it), set this value to
 72# a string in the format "Header-Name: value". For any requests that come in
 73# with that header/value, request.is_https() will return True.
 74# WARNING! Only set this if you fully understand what you're doing. Otherwise,
 75# you may be opening yourself up to a security risk.
 76# Example: HTTPS_PROXY_HEADER = "X-Forwarded-Proto: https"
 77HTTPS_PROXY_HEADER: str = ""
 78
 79# Whether to use the X-Forwarded-Host, X-Forwarded-Port, and X-Forwarded-For
 80# headers when determining the host, port, and client IP for the request.
 81# Only enable these when behind a trusted proxy that overwrites these headers.
 82HTTP_X_FORWARDED_HOST: bool = False
 83HTTP_X_FORWARDED_PORT: bool = False
 84HTTP_X_FORWARDED_FOR: bool = False
 85
 86# A secret key for this particular Plain installation. Used in secret-key
 87# hashing algorithms. Set this in your settings, or Plain will complain
 88# loudly.
 89SECRET_KEY: Secret[str]
 90
 91# List of secret keys used to verify the validity of signatures. This allows
 92# secret key rotation.
 93SECRET_KEY_FALLBACKS: Secret[list[str]] = []
 94
 95# MARK: Internationalization
 96
 97# Local time zone for this installation. All choices can be found here:
 98# https://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
 99# systems may support all possibilities). This is interpreted as the default
100# user time zone.
101TIME_ZONE: str = "UTC"
102
103
104# MARK: URL Configuration
105
106# The base URL of the site, used to generate absolute URLs outside of request contexts.
107# Should include scheme and host with no trailing slash (e.g. "https://example.com").
108BASE_URL: str = ""
109
110# MARK: File Uploads
111
112# List of upload handler classes to be applied in order.
113FILE_UPLOAD_HANDLERS: list[str] = [
114    "plain.internal.files.uploadhandler.MemoryFileUploadHandler",
115    "plain.internal.files.uploadhandler.TemporaryFileUploadHandler",
116]
117
118# Maximum size, in bytes, of a request before it will be streamed to the
119# file system instead of into memory.
120FILE_UPLOAD_MAX_MEMORY_SIZE: int = 2621440  # i.e. 2.5 MB
121
122# Maximum size in bytes of request data (excluding file uploads) that will be
123# read before a SuspiciousOperationError400 (RequestDataTooBigError400) is raised.
124DATA_UPLOAD_MAX_MEMORY_SIZE: int = 2621440  # i.e. 2.5 MB
125
126# Maximum number of GET/POST parameters that will be read before a
127# SuspiciousOperationError400 (TooManyFieldsSentError400) is raised.
128DATA_UPLOAD_MAX_NUMBER_FIELDS: int = 1000
129
130# Maximum number of files encoded in a multipart upload that will be read
131# before a SuspiciousOperationError400 (TooManyFilesSentError400) is raised.
132DATA_UPLOAD_MAX_NUMBER_FILES: int = 100
133
134# Directory in which upload streamed files will be temporarily saved. A value of
135# `None` will make Plain use the operating system's default temporary directory
136# (i.e. "/tmp" on *nix systems).
137FILE_UPLOAD_TEMP_DIR: str | None = None
138
139# MARK: Middleware
140
141# List of middleware to use. Order is important; in the request phase, these
142# middleware will be applied in the order given, and in the response
143# phase the middleware will be applied in reverse order.
144MIDDLEWARE: list[str] = []
145
146# MARK: CSRF
147
148# A list of trusted origins for unsafe (POST/PUT/DELETE etc.) requests.
149# These origins will be allowed regardless of the normal CSRF checks.
150# Each origin should be a full origin like "https://example.com" or "https://sub.example.com:8080"
151CSRF_TRUSTED_ORIGINS: list[str] = []
152
153# Regex patterns for paths that should be exempt from CSRF protection
154# Examples: [r"^/api/", r"/webhooks/.*", r"/health$"]
155CSRF_EXEMPT_PATHS: list[str] = []
156
157# MARK: Logging
158
159FRAMEWORK_LOG_LEVEL: str = "INFO"
160LOG_LEVEL: str = "INFO"
161LOG_FORMAT: str = "keyvalue"
162LOG_STREAM: str = "split"  # "split", "stdout", or "stderr"
163
164# MARK: Server
165
166SERVER_WORKERS: int = int(os.environ.get("WEB_CONCURRENCY", 0))  # 0 = auto (CPU count)
167SERVER_THREADS: int = 4
168SERVER_TIMEOUT: int = 30
169SERVER_ACCESS_LOG: bool = True
170SERVER_ACCESS_LOG_FIELDS: list[str] = [
171    "method",
172    "path",
173    "query",
174    "status",
175    "duration_ms",
176    "size",
177    "ip",
178    "user_agent",
179    "referer",
180]
181SERVER_GRACEFUL_TIMEOUT: int = 30
182SERVER_SENDFILE: bool = True
183SERVER_CONNECTIONS: int = 1000
184SERVER_H2_MAX_CONCURRENT_STREAMS: int = 100
185SERVER_MAX_REQUESTS: int = 1000  # 0 = disabled
186SERVER_MAX_REQUESTS_JITTER: int = 100  # random variance to stagger restarts
187
188# MARK: Preflight Checks
189
190# Silence checks by name
191PREFLIGHT_SILENCED_CHECKS: list[str] = []
192
193# Silence specific check results by id
194PREFLIGHT_SILENCED_RESULTS: list[str] = []
195
196# MARK: Shell
197
198SHELL_IMPORT: str = ""