package config import ( "fmt" "github.com/klauspost/cpuid/v2" "github.com/urfave/cli/v2" "github.com/photoprism/photoprism/internal/ai/face" "github.com/photoprism/photoprism/internal/config/ttl" "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/ffmpeg/encode" "github.com/photoprism/photoprism/internal/service/hub/places" "github.com/photoprism/photoprism/internal/thumb" "github.com/photoprism/photoprism/pkg/authn" "github.com/photoprism/photoprism/pkg/fs" "github.com/photoprism/photoprism/pkg/i18n" "github.com/photoprism/photoprism/pkg/media" "github.com/photoprism/photoprism/pkg/service/http/header" "github.com/photoprism/photoprism/pkg/service/http/scheme" "github.com/photoprism/photoprism/pkg/time/tz" "github.com/photoprism/photoprism/pkg/txt" ) // Flags configures the global command-line interface (CLI) parameters. var Flags = CliFlags{ { Flag: &cli.StringFlag{ Name: "auth-mode", Aliases: []string{"a"}, Usage: "authentication `MODE` (public, password)", Value: "password", EnvVars: EnvVars("AUTH_MODE"), }}, { Flag: &cli.StringFlag{ Name: "auth-secret", Usage: "secret `KEY` for signing authentication tokens", EnvVars: EnvVars("AUTH_SECRET"), Hidden: true, }}, { Flag: &cli.BoolFlag{ Name: "public", Aliases: []string{"p"}, Hidden: true, Usage: "disables authentication, advanced settings, and WebDAV remote access", EnvVars: EnvVars("PUBLIC"), }}, { Flag: &cli.StringFlag{ Name: "admin-user", Aliases: []string{"login"}, Usage: "`USERNAME` of the superadmin account that is created on first startup", Value: "admin", EnvVars: EnvVars("ADMIN_USER", "ADMIN_USERNAME"), }}, { Flag: &cli.StringFlag{ Name: "admin-password", Aliases: []string{"pw"}, Usage: fmt.Sprintf("initial `PASSWORD` of the superadmin account (%d-%d characters)", entity.PasswordLength, txt.ClipPassword), EnvVars: EnvVars("ADMIN_PASSWORD"), }}, { Flag: &cli.IntFlag{ Name: "password-length", Usage: "minimum password `LENGTH` in characters", Value: 8, EnvVars: EnvVars("PASSWORD_LENGTH"), }}, { Flag: &cli.StringFlag{ Name: "oidc-uri", Usage: "issuer `URI` for single sign-on via OpenID Connect, e.g. https://accounts.google.com", Value: "", EnvVars: EnvVars("OIDC_URI"), }}, { Flag: &cli.StringFlag{ Name: "oidc-client", Usage: "client `ID` for single sign-on via OpenID Connect", Value: "", EnvVars: EnvVars("OIDC_CLIENT"), }}, { Flag: &cli.StringFlag{ Name: "oidc-secret", Usage: "client `SECRET` for single sign-on via OpenID Connect", Value: "", EnvVars: EnvVars("OIDC_SECRET"), }}, { Flag: &cli.StringFlag{ Name: "oidc-scopes", Usage: "client authorization `SCOPES` for single sign-on via OpenID Connect", Value: authn.OidcDefaultScopes, EnvVars: EnvVars("OIDC_SCOPES"), }}, { Flag: &cli.StringFlag{ Name: "oidc-provider", Usage: "custom identity provider `NAME`, e.g. Google", Value: "", EnvVars: EnvVars("OIDC_PROVIDER"), }}, { Flag: &cli.StringFlag{ Name: "oidc-icon", Usage: "custom identity provider icon `URI`", Value: "", EnvVars: EnvVars("OIDC_ICON"), }}, { Flag: &cli.BoolFlag{ Name: "oidc-redirect", Usage: "automatically redirects unauthenticated users to the configured identity provider", EnvVars: EnvVars("OIDC_REDIRECT"), }}, { Flag: &cli.BoolFlag{ Name: "oidc-register", Usage: "allows new users to create an account when they sign in with OpenID Connect", EnvVars: EnvVars("OIDC_REGISTER"), }}, { Flag: &cli.StringFlag{ Name: "oidc-username", Usage: "preferred username `CLAIM` for new OpenID Connect users (preferred_username, name, nickname, email)", Value: authn.OidcClaimPreferredUsername, EnvVars: EnvVars("OIDC_USERNAME"), }}, { Flag: &cli.BoolFlag{ Name: "oidc-webdav", Usage: "allows new OpenID Connect users to use WebDAV when they have a role that allows it", EnvVars: EnvVars("OIDC_WEBDAV"), }}, { Flag: &cli.BoolFlag{ Name: "disable-oidc", Usage: "disables single sign-on via OpenID Connect, even if an identity provider has been configured", EnvVars: EnvVars("DISABLE_OIDC"), }}, { Flag: &cli.Int64Flag{ Name: "session-maxage", Value: DefaultSessionMaxAge, Usage: "session expiration time in `SECONDS`, doubled for accounts with 2FA (-1 to disable)", EnvVars: EnvVars("SESSION_MAXAGE"), }}, { Flag: &cli.Int64Flag{ Name: "session-timeout", Value: DefaultSessionTimeout, Usage: "session idle time in `SECONDS`, doubled for accounts with 2FA (-1 to disable)", EnvVars: EnvVars("SESSION_TIMEOUT"), }}, { Flag: &cli.Int64Flag{ Name: "session-cache", Value: DefaultSessionCache, Usage: "session cache duration in `SECONDS` (60-3600)", EnvVars: EnvVars("SESSION_CACHE"), }}, { Flag: &cli.StringFlag{ Name: "log-level", Aliases: []string{"l"}, Usage: "log message verbosity `LEVEL` (trace, debug, info, warning, error)", Value: "info", EnvVars: EnvVars("LOG_LEVEL"), }}, { Flag: &cli.BoolFlag{ Name: "prod", Usage: "disables debug mode and only logs startup warnings and errors", EnvVars: EnvVars("PROD"), }}, { Flag: &cli.BoolFlag{ Name: "debug", Usage: "enables debug mode for development and troubleshooting", EnvVars: EnvVars("DEBUG"), }}, { Flag: &cli.BoolFlag{ Name: "trace", Usage: "enables trace mode to display all debug and trace logs", EnvVars: EnvVars("TRACE"), }}, { Flag: &cli.BoolFlag{ Name: "test", Hidden: true, Usage: "enables test mode", }}, { Flag: &cli.BoolFlag{ Name: "unsafe", Hidden: true, Usage: "disables safety checks", EnvVars: EnvVars("UNSAFE"), }}, { Flag: &cli.BoolFlag{ Name: "demo", Hidden: true, Usage: "enables demo mode", EnvVars: EnvVars("DEMO"), }}, { Flag: &cli.BoolFlag{ Name: "sponsor", Hidden: true, Usage: "your continuous support helps to pay for development and operating expenses", EnvVars: EnvVars("SPONSOR"), }}, { Flag: &cli.StringFlag{ Name: "partner-id", Hidden: true, Usage: "hosting partner id", EnvVars: EnvVars("PARTNER_ID"), }}, { Flag: &cli.PathFlag{ Name: "config-path", Aliases: []string{"c"}, Usage: "config storage `PATH` or options.yml filename, values in this file override CLI flags and environment variables if present", EnvVars: EnvVars("CONFIG_PATH"), TakesFile: true, }}, { Flag: &cli.StringFlag{ Name: "defaults-yaml", Aliases: []string{"y"}, Usage: "loads default config values from `FILENAME` if it exists, does not override CLI flags or environment variables", Value: "/etc/photoprism/defaults.yml", EnvVars: EnvVars("DEFAULTS_YAML"), TakesFile: true, }}, { Flag: &cli.PathFlag{ Name: "originals-path", Aliases: []string{"o"}, Usage: "storage `PATH` of your original media files (photos and videos)", EnvVars: EnvVars("ORIGINALS_PATH"), TakesFile: true, }}, { Flag: &cli.IntFlag{ Name: "originals-limit", Aliases: []string{"mb"}, Value: 1000, Usage: "maximum size of media files in `MB` (1-100000; -1 to disable)", EnvVars: EnvVars("ORIGINALS_LIMIT"), }}, { Flag: &cli.IntFlag{ Name: "resolution-limit", Aliases: []string{"mp"}, Value: DefaultResolutionLimit, Usage: "maximum resolution of media files in `MEGAPIXELS` (1-900; -1 to disable)", EnvVars: EnvVars("RESOLUTION_LIMIT"), }}, { Flag: &cli.StringFlag{ Name: "users-path", Usage: "relative `PATH` to create base and upload subdirectories for users", Value: fs.UsersDir, EnvVars: EnvVars("USERS_PATH"), }}, { Flag: &cli.PathFlag{ Name: "storage-path", Aliases: []string{"s"}, Usage: "writable storage `PATH` for sidecar, cache, and database files", EnvVars: EnvVars("STORAGE_PATH"), TakesFile: true, }}, { Flag: &cli.PathFlag{ Name: "import-path", Aliases: []string{"im"}, Usage: "base `PATH` from which files can be imported to originals *optional*", EnvVars: EnvVars("IMPORT_PATH"), TakesFile: true, }}, { Flag: &cli.PathFlag{ Name: "import-dest", Usage: "relative originals `PATH` in which files should be imported by default *optional*", EnvVars: EnvVars("IMPORT_DEST"), TakesFile: true, }}, { Flag: &cli.StringFlag{ Name: "import-allow", Usage: "restricts imports to these file types (comma-separated list of `EXTENSIONS`; leave blank to allow all)", EnvVars: EnvVars("IMPORT_ALLOW"), }}, { Flag: &cli.BoolFlag{ Name: "upload-nsfw", Aliases: []string{"n"}, Usage: "allows uploads that might be offensive (detecting unsafe content requires TensorFlow)", EnvVars: EnvVars("UPLOAD_NSFW"), }}, { Flag: &cli.StringFlag{ Name: "upload-allow", Usage: "restricts uploads to these file types (comma-separated list of `EXTENSIONS`; leave blank to allow all)", EnvVars: EnvVars("UPLOAD_ALLOW"), }}, { Flag: &cli.BoolFlag{ Name: "upload-archives", Usage: "allows upload of zip archives (will be extracted before import)", EnvVars: EnvVars("UPLOAD_ARCHIVES"), }}, { Flag: &cli.IntFlag{ Name: "upload-limit", Value: 1000, Usage: "maximum total size of uploaded files in `MB` (1-100000; -1 to disable)", EnvVars: EnvVars("UPLOAD_LIMIT"), }}, { Flag: &cli.PathFlag{ Name: "cache-path", Aliases: []string{"ca"}, Usage: "custom cache `PATH` for sessions and thumbnail files *optional*", EnvVars: EnvVars("CACHE_PATH"), TakesFile: true, }}, { Flag: &cli.PathFlag{ Name: "temp-path", Aliases: []string{"tmp"}, Usage: "temporary file `PATH` *optional*", EnvVars: EnvVars("TEMP_PATH"), TakesFile: true, }}, { Flag: &cli.PathFlag{ Name: "assets-path", Aliases: []string{"as"}, Usage: "assets `PATH` containing static resources like icons, models, and translations", EnvVars: EnvVars("ASSETS_PATH"), TakesFile: true, }}, { Flag: &cli.PathFlag{ Name: "theme-path", Usage: "custom user interface theme `PATH` containing styles, scripts, and images", EnvVars: EnvVars("THEME_PATH"), TakesFile: true, Hidden: true, }}, { Flag: &cli.PathFlag{ Name: "models-path", Usage: "custom model assets `PATH` where computer vision models are located", EnvVars: EnvVars("MODELS_PATH"), TakesFile: true, }}, { Flag: &cli.PathFlag{ Name: "sidecar-path", Aliases: []string{"sc"}, Usage: "custom relative or absolute sidecar `PATH` *optional*", EnvVars: EnvVars("SIDECAR_PATH"), }}, { Flag: &cli.BoolFlag{ Name: "sidecar-yaml", Usage: "creates YAML sidecar files to back up picture metadata", EnvVars: EnvVars("SIDECAR_YAML"), }, DocDefault: "true"}, { Flag: &cli.BoolFlag{ Name: "usage-info", Usage: "displays storage usage information in the user interface", EnvVars: EnvVars("USAGE_INFO"), }}, { Flag: &cli.Uint64Flag{ Name: "files-quota", Usage: "maximum total size of all indexed files in `GB` (0 for unlimited)", EnvVars: EnvVars("FILES_QUOTA"), }}, { Flag: &cli.PathFlag{ Name: "backup-path", Aliases: []string{"ba"}, Usage: "custom base `PATH` for creating and restoring backups *optional*", EnvVars: EnvVars("BACKUP_PATH"), TakesFile: true, }}, { Flag: &cli.StringFlag{ Name: "backup-schedule", Usage: "backup `SCHEDULE` in cron format (e.g. \"0 12 * * *\" for daily at noon) or at a random time (daily, weekly)", Value: DefaultBackupSchedule, EnvVars: EnvVars("BACKUP_SCHEDULE"), }}, { Flag: &cli.IntFlag{ Name: "backup-retain", Usage: "`NUMBER` of index backups to keep (-1 to keep all)", Value: DefaultBackupRetain, EnvVars: EnvVars("BACKUP_RETAIN"), }}, { Flag: &cli.BoolFlag{ Name: "backup-database", Usage: "enables regular backups based on the configured schedule", EnvVars: EnvVars("BACKUP_DATABASE"), }, DocDefault: "true"}, { Flag: &cli.BoolFlag{ Name: "backup-albums", Usage: "enables the use of YAML files for backing up album metadata", EnvVars: EnvVars("BACKUP_ALBUMS"), }, DocDefault: "true"}, { Flag: &cli.IntFlag{ Name: "index-workers", Aliases: []string{"workers"}, Usage: "maximum `NUMBER` of indexing workers, default depends on the number of physical cores", Value: cpuid.CPU.PhysicalCores / 2, EnvVars: EnvVars("INDEX_WORKERS", "WORKERS"), }, DocDefault: " "}, { Flag: &cli.StringFlag{ Name: "index-schedule", Usage: "indexing `SCHEDULE` in cron format (e.g. \"@every 3h\" for every 3 hours; \"\" to disable)", Value: DefaultIndexSchedule, EnvVars: EnvVars("INDEX_SCHEDULE"), }}, { Flag: &cli.StringFlag{ Name: "wakeup-interval", Aliases: []string{"i"}, Usage: "`TIME` between facial recognition, file sync, and metadata worker runs (1-86400s)", Value: DefaultWakeupInterval.String(), EnvVars: EnvVars("WAKEUP_INTERVAL"), }}, { Flag: &cli.IntFlag{ Name: "auto-index", Usage: "delay before automatically indexing files in `SECONDS` when uploading via WebDAV (-1 to disable)", Value: DefaultAutoIndexDelay, EnvVars: EnvVars("AUTO_INDEX"), }}, { Flag: &cli.IntFlag{ Name: "auto-import", Usage: "delay before automatically importing files in `SECONDS` when uploading via WebDAV (-1 to disable)", Value: DefaultAutoImportDelay, EnvVars: EnvVars("AUTO_IMPORT"), }}, { Flag: &cli.BoolFlag{ Name: "read-only", Aliases: []string{"r"}, Usage: "disables features that require write permission for the originals folder", EnvVars: EnvVars("READONLY"), }}, { Flag: &cli.BoolFlag{ Name: "experimental", Aliases: []string{"e"}, Usage: "enables new features that may be incomplete or unstable", EnvVars: EnvVars("EXPERIMENTAL"), }}, { Flag: &cli.BoolFlag{ Name: "disable-frontend", Usage: "disables the web user interface so that only the service API endpoints are accessible", EnvVars: EnvVars("DISABLE_FRONTEND"), }}, { Flag: &cli.BoolFlag{ Name: "disable-settings", Usage: "disables the settings frontend and related API endpoints, e.g. in combination with public mode", EnvVars: EnvVars("DISABLE_SETTINGS"), }}, { Flag: &cli.BoolFlag{ Name: "disable-backups", Usage: "prevents database and album backups as well as YAML sidecar files from being created", EnvVars: EnvVars("DISABLE_BACKUPS"), }}, { Flag: &cli.BoolFlag{ Name: "disable-restart", Usage: "prevents admins from restarting the server through the user interface", EnvVars: EnvVars("DISABLE_RESTART"), }}, { Flag: &cli.BoolFlag{ Name: "disable-webdav", Usage: "prevents other apps from accessing PhotoPrism as a shared network drive", EnvVars: EnvVars("DISABLE_WEBDAV"), }}, { Flag: &cli.BoolFlag{ Name: "disable-places", Usage: "disables interactive world maps and reverse geocoding", EnvVars: EnvVars("DISABLE_PLACES"), }}, { Flag: &cli.BoolFlag{ Name: "disable-tensorflow", Usage: "disables features depending on TensorFlow, e.g. image classification and face recognition", EnvVars: EnvVars("DISABLE_TENSORFLOW"), }}, { Flag: &cli.BoolFlag{ Name: "disable-faces", Usage: "disables face detection and recognition (requires TensorFlow)", EnvVars: EnvVars("DISABLE_FACES"), }}, { Flag: &cli.BoolFlag{ Name: "disable-classification", Usage: "disables image classification (requires TensorFlow)", EnvVars: EnvVars("DISABLE_CLASSIFICATION"), }}, { Flag: &cli.BoolFlag{ Name: "disable-ffmpeg", Usage: "disables video transcoding and thumbnail extraction with FFmpeg", EnvVars: EnvVars("DISABLE_FFMPEG"), }}, { Flag: &cli.BoolFlag{ Name: "disable-exiftool", Usage: "disables metadata extraction with ExifTool (required for full Video, Live Photo, and XMP support)", EnvVars: EnvVars("DISABLE_EXIFTOOL"), }}, { Flag: &cli.BoolFlag{ Name: "disable-vips", Usage: "disables image processing and conversion with libvips", EnvVars: EnvVars("DISABLE_VIPS"), }}, { Flag: &cli.BoolFlag{ Name: "disable-sips", Usage: "disables file conversion using the sips command under macOS", EnvVars: EnvVars("DISABLE_SIPS"), }}, { Flag: &cli.BoolFlag{ Name: "disable-darktable", Usage: "disables conversion of RAW images with Darktable", EnvVars: EnvVars("DISABLE_DARKTABLE"), }}, { Flag: &cli.BoolFlag{ Name: "disable-rawtherapee", Usage: "disables conversion of RAW images with RawTherapee", EnvVars: EnvVars("DISABLE_RAWTHERAPEE"), }}, { Flag: &cli.BoolFlag{ Name: "disable-imagemagick", Usage: "disables conversion of image files with ImageMagick", EnvVars: EnvVars("DISABLE_IMAGEMAGICK"), }}, { Flag: &cli.BoolFlag{ Name: "disable-heifconvert", Usage: "disables conversion of HEIC images with libheif", EnvVars: EnvVars("DISABLE_HEIFCONVERT"), }}, { Flag: &cli.BoolFlag{ Name: "disable-jpegxl", Usage: "disables JPEG XL file format support", EnvVars: EnvVars("DISABLE_JPEGXL"), }}, { Flag: &cli.BoolFlag{ Name: "disable-raw", Usage: "disables indexing and conversion of RAW images", EnvVars: EnvVars("DISABLE_RAW"), }}, { Flag: &cli.BoolFlag{ Name: "raw-presets", Usage: "enables custom user presets when converting RAW images (reduces performance)", EnvVars: EnvVars("RAW_PRESETS"), }}, { Flag: &cli.BoolFlag{ Name: "exif-bruteforce", Usage: "performs a brute-force search if no Exif headers were found", EnvVars: EnvVars("EXIF_BRUTEFORCE"), }}, { Flag: &cli.StringFlag{ Name: "default-locale", Aliases: []string{"lang"}, Usage: "default user interface language `CODE`", Value: i18n.Default.Locale(), EnvVars: EnvVars("DEFAULT_LOCALE"), }}, { Flag: &cli.StringFlag{ Name: "default-timezone", Aliases: []string{"tz"}, Usage: "default time zone `NAME`, e.g. for scheduling backups", Value: tz.Local, EnvVars: EnvVars("DEFAULT_TIMEZONE"), }}, { Flag: &cli.StringFlag{ Name: "default-theme", Usage: "default user interface theme `NAME`", EnvVars: EnvVars("DEFAULT_THEME"), }}, { Flag: &cli.StringFlag{ Name: "places-locale", Usage: "location details language `CODE`, e.g. en, de, or local", Value: places.LocalLocale, EnvVars: EnvVars("PLACES_LOCALE"), }}, { Flag: &cli.StringFlag{ Name: "app-name", Usage: "app `NAME` when installed as a Progressive Web App (PWA)", Value: "", EnvVars: EnvVars("APP_NAME"), }}, { Flag: &cli.StringFlag{ Name: "app-mode", Usage: "app display `MODE` (fullscreen, standalone, minimal-ui, browser)", Value: "standalone", EnvVars: EnvVars("APP_MODE"), }}, { Flag: &cli.StringFlag{ Name: "app-icon", Usage: "home screen app `ICON` (logo, app, crisp, mint, bold, square)", EnvVars: EnvVars("APP_ICON"), }}, { Flag: &cli.StringFlag{ Name: "app-color", Usage: "app background and splash screen `COLOR`", Value: DefaultAppColor, EnvVars: EnvVars("APP_COLOR"), }}, { Flag: &cli.StringFlag{ Name: "imprint", Usage: "legal information `TEXT`, displayed in the page footer", Value: "", Hidden: true, EnvVars: EnvVars("IMPRINT"), }}, { Flag: &cli.StringFlag{ Name: "legal-info", Usage: "legal information `TEXT`, displayed in the page footer", Value: "", EnvVars: EnvVars("LEGAL_INFO"), }}, { Flag: &cli.StringFlag{ Name: "imprint-url", Usage: "legal information `URL`", Value: "", Hidden: true, EnvVars: EnvVars("IMPRINT_URL"), }}, { Flag: &cli.StringFlag{ Name: "legal-url", Usage: "legal information `URL`", Value: "", EnvVars: EnvVars("LEGAL_URL"), }}, { Flag: &cli.StringFlag{ Name: "wallpaper-uri", Usage: "login screen background image `URI`", Value: "", EnvVars: EnvVars("WALLPAPER_URI"), }}, { Flag: &cli.StringFlag{ Name: "site-url", Usage: "canonical site `URL` used in generated links and to determine HTTPS/TLS (scheme://host[:port])", Value: "http://localhost:2342/", EnvVars: EnvVars("SITE_URL"), }}, { Flag: &cli.StringFlag{ Name: "site-author", Usage: "site `OWNER`, copyright, or artist", EnvVars: EnvVars("SITE_AUTHOR"), }}, { Flag: &cli.StringFlag{ Name: "site-title", Usage: "site `TITLE`", Value: "", EnvVars: EnvVars("SITE_TITLE"), }}, { Flag: &cli.StringFlag{ Name: "site-caption", Usage: "site `CAPTION`", Value: "AI-Powered Photos App", EnvVars: EnvVars("SITE_CAPTION"), }}, { Flag: &cli.StringFlag{ Name: "site-description", Usage: "site `DESCRIPTION` *optional*", EnvVars: EnvVars("SITE_DESCRIPTION"), }}, { Flag: &cli.StringFlag{ Name: "site-favicon", Usage: "site favicon `FILENAME` *optional*", EnvVars: EnvVars("SITE_FAVICON"), TakesFile: true, }}, { Flag: &cli.StringFlag{ Name: "site-preview", Usage: "sharing preview image `URL`", EnvVars: EnvVars("SITE_PREVIEW"), }}, { Flag: &cli.StringFlag{ Name: "cdn-url", Usage: "content delivery network `URL`", EnvVars: EnvVars("CDN_URL"), }}, { Flag: &cli.BoolFlag{ Name: "cdn-video", Usage: "streams videos over the specified CDN", EnvVars: EnvVars("CDN_VIDEO"), }}, { Flag: &cli.StringFlag{ Name: "cors-origin", Usage: "origin `URL` from which browsers are allowed to perform cross-origin requests (leave blank to disable or use * to allow all)", EnvVars: EnvVars("CORS_ORIGIN"), Value: header.DefaultAccessControlAllowOrigin, }}, { Flag: &cli.StringFlag{ Name: "cors-headers", Usage: "one or more `HEADERS` that browsers should see when performing a cross-origin request", EnvVars: EnvVars("CORS_HEADERS"), Value: header.DefaultAccessControlAllowHeaders, }}, { Flag: &cli.StringFlag{ Name: "cors-methods", Usage: "one or more `METHODS` that may be used when performing a cross-origin request", EnvVars: EnvVars("CORS_METHODS"), Value: header.DefaultAccessControlAllowMethods, }}, { Flag: &cli.StringFlag{ Name: "cluster-domain", Usage: "cluster `DOMAIN` (lowercase DNS name; 1–63 chars)", EnvVars: EnvVars("CLUSTER_DOMAIN"), }}, { Flag: &cli.StringFlag{ Name: "cluster-uuid", Usage: "cluster `UUID` (v4) to scope node credentials", EnvVars: EnvVars("CLUSTER_UUID"), Hidden: true, }}, { Flag: &cli.StringFlag{ Name: "cluster-cidr", Usage: "cluster `CIDR` (e.g., 10.0.0.0/8) for IP-based authorization", EnvVars: EnvVars("CLUSTER_CIDR"), Hidden: true, }}, { Flag: &cli.StringFlag{ Name: "portal-url", Usage: "base `URL` of the cluster management portal", Value: DefaultPortalUrl, EnvVars: EnvVars("PORTAL_URL"), }}, { Flag: &cli.StringFlag{ Name: "join-token", Usage: "secret `TOKEN` required to join a cluster; min 24 chars", EnvVars: EnvVars("JOIN_TOKEN"), }}, { Flag: &cli.StringFlag{ Name: "node-name", Usage: "node `NAME` (unique in cluster domain; [a-z0-9-]{1,32})", EnvVars: EnvVars("NODE_NAME"), }}, { Flag: &cli.StringFlag{ Name: "node-role", Usage: "node `ROLE` (instance or service)", EnvVars: EnvVars("NODE_ROLE"), }}, { Flag: &cli.StringFlag{ Name: "node-uuid", Usage: "node `UUID` (v7) that uniquely identifies this instance", EnvVars: EnvVars("NODE_UUID"), Hidden: true, }}, { Flag: &cli.StringFlag{ Name: "node-client-id", Usage: "node OAuth client `ID` (auto-assigned via join token)", EnvVars: EnvVars("NODE_CLIENT_ID"), Hidden: true, }}, { Flag: &cli.StringFlag{ Name: "node-client-secret", Usage: "node OAuth client `SECRET` (auto-assigned via join token)", EnvVars: EnvVars("NODE_CLIENT_SECRET"), Hidden: true, }}, { Flag: &cli.StringFlag{ Name: "jwks-url", Usage: "JWKS endpoint `URL` provided by the cluster portal for JWT verification", EnvVars: EnvVars("JWKS_URL"), }}, { Flag: &cli.IntFlag{ Name: "jwks-cache-ttl", Usage: "JWKS cache lifetime in `SECONDS` (default 300, max 3600)", Value: 300, EnvVars: EnvVars("JWKS_CACHE_TTL"), }}, { Flag: &cli.StringFlag{ Name: "jwt-scope", Usage: "allowed JWT `SCOPES` (space separated). Leave empty to accept defaults", EnvVars: EnvVars("JWT_SCOPE"), }}, { Flag: &cli.IntFlag{ Name: "jwt-leeway", Usage: "JWT clock skew allowance in `SECONDS` (default 60, max 300)", Value: 60, EnvVars: EnvVars("JWT_LEEWAY"), }}, { Flag: &cli.StringFlag{ Name: "advertise-url", Usage: "advertised `URL` for intra-cluster calls (scheme://host[:port])", Value: "", EnvVars: EnvVars("ADVERTISE_URL"), }}, { Flag: &cli.StringFlag{ Name: "https-proxy", Usage: "proxy server `URL` to be used for outgoing connections *optional*", EnvVars: EnvVars("HTTPS_PROXY"), }}, { Flag: &cli.BoolFlag{ Name: "https-proxy-insecure", Usage: "ignores invalid HTTPS certificates when using a proxy", EnvVars: EnvVars("HTTPS_PROXY_INSECURE"), }}, { Flag: &cli.StringFlag{ Name: "trusted-platform", Usage: "trusted client IP header `NAME`, e.g. when running behind a cloud provider load balancer", Value: "", EnvVars: EnvVars("TRUSTED_PLATFORM"), }}, { Flag: &cli.StringSliceFlag{ Name: "trusted-proxy", Usage: "`CIDR` ranges or IPv4/v6 addresses from which reverse proxy headers can be trusted, separated by commas", Value: cli.NewStringSlice(header.CidrDockerInternal), EnvVars: EnvVars("TRUSTED_PROXY"), }}, { Flag: &cli.StringSliceFlag{ Name: "proxy-client-header", Usage: "proxy client IP header `NAME`, e.g. X-Forwarded-For, X-Client-IP, X-Real-IP, or CF-Connecting-IP", Value: cli.NewStringSlice(header.XForwardedFor), EnvVars: EnvVars("PROXY_CLIENT_HEADER"), }}, { Flag: &cli.StringSliceFlag{ Name: "proxy-proto-header", Usage: "proxy protocol header `NAME`", Value: cli.NewStringSlice(header.XForwardedProto), EnvVars: EnvVars("PROXY_PROTO_HEADER"), }}, { Flag: &cli.StringSliceFlag{ Name: "proxy-proto-https", Usage: "forwarded HTTPS protocol `NAME`", Value: cli.NewStringSlice(scheme.Https), EnvVars: EnvVars("PROXY_PROTO_HTTPS"), }}, { Flag: &cli.BoolFlag{ Name: "disable-tls", Usage: "disables HTTPS/TLS even if the site URL starts with https:// and a certificate is available", EnvVars: EnvVars("DISABLE_TLS"), }}, { Flag: &cli.BoolFlag{ Name: "default-tls", Usage: "uses a self-signed HTTPS/TLS certificate if no other certificate is available", EnvVars: EnvVars("DEFAULT_TLS"), }}, { Flag: &cli.StringFlag{ Name: "tls-email", Usage: "`EMAIL` address to enable automatic HTTPS via Let's Encrypt", EnvVars: EnvVars("TLS_EMAIL"), Hidden: true, }}, { Flag: &cli.StringFlag{ Name: "tls-cert", Usage: "public HTTPS certificate `FILENAME` (.crt), ignored for Unix domain sockets", EnvVars: EnvVars("TLS_CERT"), }}, { Flag: &cli.StringFlag{ Name: "tls-key", Usage: "private HTTPS key `FILENAME` (.key), ignored for Unix domain sockets", EnvVars: EnvVars("TLS_KEY"), }}, { Flag: &cli.StringFlag{ Name: "http-mode", Aliases: []string{"mode"}, Usage: "Web server `MODE` (debug, release, test)", EnvVars: EnvVars("HTTP_MODE"), }}, { Flag: &cli.StringFlag{ Name: "http-compression", Aliases: []string{"z"}, Usage: "Web server compression `METHOD` (gzip, none)", EnvVars: EnvVars("HTTP_COMPRESSION"), }}, { Flag: &cli.BoolFlag{ Name: "http-cache-public", Usage: "allows static content to be cached by a CDN or caching proxy", EnvVars: EnvVars("HTTP_CACHE_PUBLIC"), }}, { Flag: &cli.IntFlag{ Name: "http-cache-maxage", Value: int(ttl.CacheDefault), Usage: "time in `SECONDS` until cached content expires", EnvVars: EnvVars("HTTP_CACHE_MAXAGE"), }}, { Flag: &cli.IntFlag{ Name: "http-video-maxage", Value: int(ttl.CacheVideo), Usage: "time in `SECONDS` until cached videos expire", EnvVars: EnvVars("HTTP_VIDEO_MAXAGE"), }}, { Flag: &cli.StringFlag{ Name: "http-host", Aliases: []string{"ip"}, Value: "0.0.0.0", Usage: "Web server `IP` address or Unix domain socket, e.g. unix:/var/run/photoprism.sock?force=true&mode=660", EnvVars: EnvVars("HTTP_HOST"), }}, { Flag: &cli.IntFlag{ Name: "http-port", Aliases: []string{"port"}, Value: 2342, Usage: "Web server port `NUMBER`, ignored for Unix domain sockets", EnvVars: EnvVars("HTTP_PORT"), }}, { Flag: &cli.StringFlag{ Name: "database-driver", Aliases: []string{"db"}, Usage: "database `DRIVER` (sqlite, mysql)", Value: "sqlite", EnvVars: EnvVars("DATABASE_DRIVER"), }}, { Flag: &cli.StringFlag{ Name: "database-dsn", Aliases: []string{"dsn"}, Usage: "database connection `DSN` (sqlite file, optional for mysql)", EnvVars: EnvVars("DATABASE_DSN"), }}, { Flag: &cli.StringFlag{ Name: "database-name", Aliases: []string{"db-name"}, Value: "photoprism", Usage: "database schema `NAME`", EnvVars: EnvVars("DATABASE_NAME"), }}, { Flag: &cli.StringFlag{ Name: "database-server", Aliases: []string{"db-server"}, Usage: "database `HOST` incl. port, e.g. \"mariadb:3306\" (or socket path)", EnvVars: EnvVars("DATABASE_SERVER"), }}, { Flag: &cli.StringFlag{ Name: "database-user", Aliases: []string{"db-user"}, Value: "photoprism", Usage: "database user `NAME`", EnvVars: EnvVars("DATABASE_USER"), }}, { Flag: &cli.StringFlag{ Name: "database-password", Aliases: []string{"db-pass"}, Usage: "database user `PASSWORD`", EnvVars: EnvVars("DATABASE_PASSWORD"), }}, { Flag: &cli.IntFlag{ Name: "database-timeout", Usage: "timeout in `SECONDS` for establishing a database connection (1-60)", EnvVars: EnvVars("DATABASE_TIMEOUT"), Value: 15, }}, { Flag: &cli.IntFlag{ Name: "database-conns", Usage: "maximum `NUMBER` of open database connections", EnvVars: EnvVars("DATABASE_CONNS"), }}, { Flag: &cli.IntFlag{ Name: "database-conns-idle", Usage: "maximum `NUMBER` of idle database connections", EnvVars: EnvVars("DATABASE_CONNS_IDLE"), }}, { Flag: &cli.StringFlag{ Name: "database-provision-driver", Usage: "auto-provisioning `DRIVER` (auto, mysql)", Value: Auto, EnvVars: EnvVars("DATABASE_PROVISION_DRIVER"), Hidden: true, }}, { Flag: &cli.StringFlag{ Name: "database-provision-dsn", Usage: "auto-provisioning `DSN`", EnvVars: EnvVars("DATABASE_PROVISION_DSN"), Hidden: true, }}, { Flag: &cli.StringFlag{ Name: "ffmpeg-bin", Usage: "FFmpeg `COMMAND` for video transcoding and thumbnail extraction", Value: encode.FFmpegBin, EnvVars: EnvVars("FFMPEG_BIN"), }}, { Flag: &cli.StringFlag{ Name: "ffmpeg-encoder", Aliases: []string{"vc"}, Usage: "FFmpeg AVC video encoder `NAME`", Value: "libx264", EnvVars: EnvVars("FFMPEG_ENCODER"), }}, { Flag: &cli.IntFlag{ Name: "ffmpeg-size", Usage: "encoding resolution limit in `PIXELS` (720-7680)", Value: thumb.Sizes[thumb.Fit4096].Width, EnvVars: EnvVars("FFMPEG_SIZE"), }}, { Flag: &cli.IntFlag{ Name: "ffmpeg-quality", Usage: fmt.Sprintf("encoding `QUALITY` (%d-%d, where %d is almost lossless)", encode.WorstQuality, encode.BestQuality, encode.BestQuality), Value: encode.DefaultQuality, EnvVars: EnvVars("FFMPEG_QUALITY"), }}, { Flag: &cli.IntFlag{ Name: "ffmpeg-bitrate", Usage: fmt.Sprintf("bitrate `LIMIT` in Mbps for forced transcoding of non-AVC videos (%d-%d; %d to disable)", encode.MinBitrateLimit, encode.MaxBitrateLimit, encode.NoBitrateLimit), Value: encode.DefaultBitrateLimit, EnvVars: EnvVars("FFMPEG_BITRATE"), }}, { Flag: &cli.StringFlag{ Name: "ffmpeg-preset", Usage: "FFmpeg compression `PRESET` when using an encoder that supports it, e.g. fast, medium, or slow", Value: encode.PresetFast, EnvVars: EnvVars("FFMPEG_PRESET"), }}, { Flag: &cli.StringFlag{ Name: "ffmpeg-device", Usage: "FFmpeg device `PATH` when using a hardware encoder that supports it as parameter", EnvVars: EnvVars("FFMPEG_DEVICE"), }}, { Flag: &cli.StringFlag{ Name: "ffmpeg-map-video", Usage: "transcoding video stream `MAP`", Value: encode.DefaultMapVideo, EnvVars: EnvVars("FFMPEG_MAP_VIDEO"), }, DocDefault: fmt.Sprintf("`%s`", encode.DefaultMapVideo)}, { Flag: &cli.StringFlag{ Name: "ffmpeg-map-audio", Usage: "transcoding audio stream `MAP`", Value: encode.DefaultMapAudio, EnvVars: EnvVars("FFMPEG_MAP_AUDIO"), }, DocDefault: fmt.Sprintf("`%s`", encode.DefaultMapAudio)}, { Flag: &cli.StringFlag{ Name: "exiftool-bin", Usage: "ExifTool `COMMAND` for extracting metadata", Value: "exiftool", EnvVars: EnvVars("EXIFTOOL_BIN"), }}, { Flag: &cli.StringFlag{ Name: "sips-bin", Usage: "Sips `COMMAND` for media file conversion *macOS only*", Value: "sips", EnvVars: EnvVars("SIPS_BIN"), }}, { Flag: &cli.StringFlag{ Name: "sips-exclude", Usage: "file `EXTENSIONS` not to be used with Sips *macOS only*", Value: "avif, avifs, thm", EnvVars: EnvVars("SIPS_EXCLUDE", "SIPS_BLACKLIST"), }}, { Flag: &cli.StringFlag{ Name: "darktable-bin", Usage: "Darktable CLI `COMMAND` for RAW to JPEG conversion", Value: "darktable-cli", EnvVars: EnvVars("DARKTABLE_BIN"), }}, { Flag: &cli.StringFlag{ Name: "darktable-exclude", Usage: "file `EXTENSIONS` not to be used with Darktable", Value: "thm", EnvVars: EnvVars("DARKTABLE_EXCLUDE", "DARKTABLE_BLACKLIST"), }}, { Flag: &cli.PathFlag{ Name: "darktable-cache-path", Usage: "custom Darktable cache `PATH`", Value: "", EnvVars: EnvVars("DARKTABLE_CACHE_PATH"), TakesFile: true, }}, { Flag: &cli.PathFlag{ Name: "darktable-config-path", Usage: "custom Darktable config `PATH`", Value: "", EnvVars: EnvVars("DARKTABLE_CONFIG_PATH"), TakesFile: true, }}, { Flag: &cli.StringFlag{ Name: "rawtherapee-bin", Usage: "RawTherapee CLI `COMMAND` for RAW to JPEG conversion", Value: "rawtherapee-cli", EnvVars: EnvVars("RAWTHERAPEE_BIN"), }}, { Flag: &cli.StringFlag{ Name: "rawtherapee-exclude", Usage: "file `EXTENSIONS` not to be used with RawTherapee", Value: "dng, thm", EnvVars: EnvVars("RAWTHERAPEE_EXCLUDE", "RAWTHERAPEE_BLACKLIST"), }}, { Flag: &cli.StringFlag{ Name: "imagemagick-bin", Usage: "ImageMagick CLI `COMMAND` for image file conversion", Value: "convert", EnvVars: EnvVars("IMAGEMAGICK_BIN"), }}, { Flag: &cli.StringFlag{ Name: "imagemagick-exclude", Usage: "file `EXTENSIONS` not to be used with ImageMagick", Value: "heif, heic, heics, avif, avifs, jxl, thm", EnvVars: EnvVars("IMAGEMAGICK_EXCLUDE", "IMAGEMAGICK_BLACKLIST"), }}, { Flag: &cli.StringFlag{ Name: "heifconvert-bin", Usage: "libheif HEIC image conversion `COMMAND`", Value: "", EnvVars: EnvVars("HEIFCONVERT_BIN"), }, DocDefault: "heif-dec"}, { Flag: &cli.StringFlag{ Name: "heifconvert-orientation", Usage: "Exif `ORIENTATION` of images generated with libheif (keep, reset)", Value: media.KeepOrientation, EnvVars: EnvVars("HEIFCONVERT_ORIENTATION"), }}, { Flag: &cli.StringFlag{ Name: "download-token", Usage: "`DEFAULT` download URL token for originals (leave blank for a random value)", EnvVars: EnvVars("DOWNLOAD_TOKEN"), }}, { Flag: &cli.StringFlag{ Name: "preview-token", Usage: "`DEFAULT` thumbnail and video streaming URL token (leave blank for a random value)", EnvVars: EnvVars("PREVIEW_TOKEN"), }}, { Flag: &cli.StringFlag{ Name: "thumb-library", Aliases: []string{"thumbs"}, Usage: "image processing `LIBRARY` to be used for generating thumbnails (auto, imaging, vips)", Value: Auto, EnvVars: EnvVars("THUMB_LIBRARY"), }}, { Flag: &cli.StringFlag{ Name: "thumb-color", Usage: "standard color `PROFILE` for thumbnails (auto, preserve, srgb, none)", Value: thumb.ColorAuto, EnvVars: EnvVars("THUMB_COLOR"), }}, { Flag: &cli.StringFlag{ Name: "thumb-filter", Aliases: []string{"filter"}, Usage: "downscaling filter `NAME` (imaging best to worst: blackman, lanczos, cubic, linear, nearest)", Value: thumb.ResampleAuto.String(), EnvVars: EnvVars("THUMB_FILTER"), }}, { Flag: &cli.IntFlag{ Name: "thumb-size", Usage: "maximum size of pre-generated thumbnails in `PIXELS` (720-7680)", Value: thumb.SizeCached, EnvVars: EnvVars("THUMB_SIZE"), }}, { Flag: &cli.IntFlag{ Name: "thumb-size-uncached", Usage: "maximum size of thumbnails generated on demand in `PIXELS` (720-7680)", Value: thumb.SizeOnDemand, EnvVars: EnvVars("THUMB_SIZE_UNCACHED"), }}, { Flag: &cli.BoolFlag{ Name: "thumb-uncached", Aliases: []string{"u"}, Usage: "generates missing thumbnails on demand (high memory and cpu usage)", EnvVars: EnvVars("THUMB_UNCACHED"), }}, { Flag: &cli.StringFlag{ Name: "jpeg-quality", Aliases: []string{"q"}, Usage: "higher values increase the image `QUALITY` and file size (25-100)", Value: thumb.QualityMedium.String(), EnvVars: EnvVars("JPEG_QUALITY"), }}, { Flag: &cli.IntFlag{ Name: "jpeg-size", Usage: "maximum size of generated JPEG images in `PIXELS` (720-30000)", Value: 7680, EnvVars: EnvVars("JPEG_SIZE"), }}, { Flag: &cli.IntFlag{ Name: "png-size", Usage: "maximum size of generated PNG images in `PIXELS` (720-30000)", Value: 7680, EnvVars: EnvVars("PNG_SIZE"), }}, { Flag: &cli.StringFlag{ Name: "vision-yaml", Usage: "computer vision model configuration `FILENAME` *optional*", Value: "", EnvVars: EnvVars("VISION_YAML"), TakesFile: true, }}, { Flag: &cli.BoolFlag{ Name: "vision-api", Usage: "enables the computer vision API endpoints under /api/v1/vision (requires authorization)", EnvVars: EnvVars("VISION_API"), }}, { Flag: &cli.StringFlag{ Name: "vision-uri", Usage: "vision service base `URI`, e.g. https://example.com/api/v1/vision (leave blank to disable)", Value: "", EnvVars: EnvVars("VISION_URI"), }}, { Flag: &cli.StringFlag{ Name: "vision-key", Usage: "vision service access `TOKEN` *optional*", Value: "", EnvVars: EnvVars("VISION_KEY"), }}, { Flag: &cli.StringFlag{ Name: "vision-schedule", Usage: "vision worker `SCHEDULE` for background processing (e.g. \"0 12 * * *\" for daily at noon) or at a random time (daily, weekly)", EnvVars: EnvVars("VISION_SCHEDULE"), }}, { Flag: &cli.StringFlag{ Name: "vision-filter", Usage: "vision worker search `FILTER` applied to scheduled runs (same syntax as photoprism vision run)", Value: "public:true", EnvVars: EnvVars("VISION_FILTER"), }}, { Flag: &cli.BoolFlag{ Name: "detect-nsfw", Usage: "flags newly added pictures as private if they might be offensive (requires TensorFlow)", EnvVars: EnvVars("DETECT_NSFW"), }}, { Flag: &cli.IntFlag{ Name: "face-size", Usage: "minimum size of faces in `PIXELS` (20-10000)", Value: face.SizeThreshold, EnvVars: EnvVars("FACE_SIZE"), }}, { Flag: &cli.Float64Flag{ Name: "face-score", Usage: "minimum face `QUALITY` score (1-100)", Value: face.ScoreThreshold, EnvVars: EnvVars("FACE_SCORE"), }}, { Flag: &cli.Float64SliceFlag{ Name: "face-angle", Usage: "face detection `ANGLE` in radians (repeatable)", Value: cli.NewFloat64Slice(face.DefaultAngles...), EnvVars: EnvVars("FACE_ANGLE"), }}, { Flag: &cli.IntFlag{ Name: "face-overlap", Usage: "face area overlap threshold in `PERCENT` (1-100)", Value: face.OverlapThreshold, EnvVars: EnvVars("FACE_OVERLAP"), }}, { Flag: &cli.IntFlag{ Name: "face-cluster-size", Usage: "minimum size of automatically clustered faces in `PIXELS` (20-10000)", Value: face.ClusterSizeThreshold, EnvVars: EnvVars("FACE_CLUSTER_SIZE"), }}, { Flag: &cli.IntFlag{ Name: "face-cluster-score", Usage: "minimum `QUALITY` score of automatically clustered faces (1-100)", Value: face.ClusterScoreThreshold, EnvVars: EnvVars("FACE_CLUSTER_SCORE"), }}, { Flag: &cli.IntFlag{ Name: "face-cluster-core", Usage: "`NUMBER` of faces forming a cluster core (1-100)", Value: face.ClusterCore, EnvVars: EnvVars("FACE_CLUSTER_CORE"), }}, { Flag: &cli.Float64Flag{ Name: "face-cluster-dist", Usage: "similarity `DISTANCE` of faces forming a cluster core (0.1-1.5)", Value: face.ClusterDist, EnvVars: EnvVars("FACE_CLUSTER_DIST"), }}, { Flag: &cli.Float64Flag{ Name: "face-match-dist", Usage: "similarity `OFFSET` for matching faces with existing clusters (0.1-1.5)", Value: face.MatchDist, EnvVars: EnvVars("FACE_MATCH_DIST"), }}, { Flag: &cli.StringFlag{ Name: "pid-filename", Usage: "process id `FILENAME` *daemon-mode only*", EnvVars: EnvVars("PID_FILENAME"), TakesFile: true, }}, { Flag: &cli.StringFlag{ Name: "log-filename", Usage: "server log `FILENAME` *daemon-mode only*", Value: "", EnvVars: EnvVars("LOG_FILENAME"), TakesFile: true, }}, }