# Flask settings # CRITICAL: Change SECRET_KEY in production! Used for sessions, cookies, and CSRF tokens. # Generate a secure key with: python -c "import secrets; print(secrets.token_hex(32))" # The same key must be used across restarts and all app replicas. SECRET_KEY=your-secret-key-here FLASK_ENV=production FLASK_DEBUG=false # Database settings DATABASE_URL=postgresql+psycopg2://timetracker:timetracker@db:5432/timetracker POSTGRES_DB=timetracker POSTGRES_USER=timetracker POSTGRES_PASSWORD=timetracker POSTGRES_HOST=db # Docker Compose: nginx host port mappings (used by docker-compose.yml) # Override when 80/443 are in use (e.g. behind Nginx Proxy Manager, Traefik, Caddy). # Example: HTTP_PORT=8180 HTTPS_PORT=8443 # HTTP_PORT=80 # HTTPS_PORT=443 # Session settings SESSION_COOKIE_SECURE=false SESSION_COOKIE_HTTPONLY=true PERMANENT_SESSION_LIFETIME=86400 # Application settings TZ=Europe/Rome CURRENCY=EUR ROUNDING_MINUTES=1 # Seeds Settings.single_active_timer on first settings row; admin System Settings overrides thereafter. SINGLE_ACTIVE_TIMER=true IDLE_TIMEOUT_MINUTES=30 # Smart in-app notifications (session toasts; see docs/features/SMART_NOTIFICATIONS.md) # SMART_NOTIFY_MAX_PER_DAY=2 # SMART_NOTIFY_NO_TRACKING_AFTER=16:00 # SMART_NOTIFY_SUMMARY_AT=18:00 # SMART_NOTIFY_LONG_TIMER_HOURS=4 # SMART_NOTIFY_SCHEDULER_SLOT_MINUTES=30 # AI helper (server-side; API keys are never sent to clients) # Default install has no bundled LLM. Leave AI_ENABLED=false unless you use an external API or enable Ollama. AI_ENABLED=false # --- Optional: bundled Ollama (same machine as Docker Compose) --- # Start the stack with: docker compose --profile ai up -d # Then set: # AI_ENABLED=true # AI_PROVIDER=ollama # AI_BASE_URL=http://ollama:11434 # AI_MODEL=llama3.1 # OLLAMA_KEEP_ALIVE=5m # how long Ollama keeps the model resident in memory # --- Optional: hosted OpenAI-compatible API only (no Ollama profile) --- # AI_ENABLED=true # AI_PROVIDER=openai_compatible # AI_BASE_URL=https://api.example.com # AI_MODEL=... # AI_API_KEY=... AI_PROVIDER=ollama AI_BASE_URL=http://ollama:11434 AI_MODEL=llama3.1 # AI_API_KEY= # only needed when AI_PROVIDER=openai_compatible AI_TIMEOUT_SECONDS=60 AI_CONTEXT_LIMIT=40 # API token rate limits (per token; Redis recommended for multi-worker) # API_TOKEN_RATE_LIMIT_PER_MINUTE=100 # API_TOKEN_RATE_LIMIT_PER_HOUR=1000 # User management ALLOW_SELF_REGISTER=true # Comma-separated admin usernames. Only the first username is automatically created during database initialization. # The default admin has no password—set it on first login (enter username + any password 8+ chars). # Additional admin usernames must either self-register (if ALLOW_SELF_REGISTER=true) or be created manually. # Example: ADMIN_USERNAMES=admin,manager - only "admin" is created automatically; "manager" must self-register or be created manually. ADMIN_USERNAMES=admin # Authentication # Options: none | local | oidc | ldap | both | all # none = No password authentication (username only) # local = Password authentication required # oidc = OIDC/Single Sign-On only # ldap = LDAP bind only # both = OIDC + local password (backwards compatible) # all = local + OIDC + LDAP AUTH_METHOD=local # Security hardening (recommended) # Settings secrets encryption-at-rest (mail passwords, OAuth secrets, Peppol token, AI key, 2FA secret). # Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" # SETTINGS_ENCRYPTION_KEY= # Or mount from file: # SETTINGS_ENCRYPTION_KEY_FILE=/run/secrets/timetracker_settings_key # # Password reset link lifetime (seconds) # PASSWORD_RESET_TOKEN_MAX_AGE_SECONDS=3600 # # Require TOTP 2FA for admin accounts (admins will be prompted to enroll) # REQUIRE_2FA_FOR_ADMINS=false # LDAP Authentication (used when AUTH_METHOD=ldap or all) # LDAP_HOST=ldap.example.com # LDAP_PORT=389 # LDAP_USE_SSL=false # LDAP_USE_TLS=false # LDAP_BIND_DN=cn=serviceaccount,dc=example,dc=com # LDAP_BIND_PASSWORD=secret # LDAP_BASE_DN=dc=example,dc=com # LDAP_USER_DN=ou=users # LDAP_USER_OBJECT_CLASS=inetOrgPerson # LDAP_USER_LOGIN_ATTR=uid # LDAP_USER_EMAIL_ATTR=mail # LDAP_USER_FNAME_ATTR=givenName # LDAP_USER_LNAME_ATTR=sn # LDAP_GROUP_DN=ou=groups # LDAP_GROUP_OBJECT_CLASS=groupOfNames # LDAP_ADMIN_GROUP=timetracker-admins # LDAP_REQUIRED_GROUP=timetracker-users # LDAP_TLS_CA_CERT_FILE=/certs/ldap-ca.crt # LDAP_TIMEOUT=10 # OIDC (used when AUTH_METHOD=oidc, both, or all) # OIDC_ISSUER=https://login.microsoftonline.com//v2.0 # OIDC_CLIENT_ID= # OIDC_CLIENT_SECRET= # OIDC_REDIRECT_URI=https://yourapp.example.com/auth/oidc/callback # OIDC_SCOPES=openid profile email # OIDC_USERNAME_CLAIM=preferred_username # OIDC_FULL_NAME_CLAIM=name # OIDC_EMAIL_CLAIM=email # OIDC_GROUPS_CLAIM=groups # OIDC_ADMIN_GROUP= # OIDC_ADMIN_EMAILS= # Optional: RP-Initiated Logout. Only set if your provider supports end_session_endpoint. # If unset, logout will be local only (recommended for providers like Authelia). # If set, TimeTracker will redirect to the provider's logout endpoint. # OIDC_POST_LOGOUT_REDIRECT_URI=https://yourapp.example.com/ # Backup settings BACKUP_RETENTION_DAYS=30 BACKUP_TIME=02:00 # Optional: override where backup archives are stored (default: /backups) # BACKUP_FOLDER=/data/uploads/backups # Email settings (Flask-Mail) # Configure these to enable email notifications and features # MAIL_SERVER=smtp.gmail.com # MAIL_PORT=587 # MAIL_USE_TLS=true # MAIL_USE_SSL=false # MAIL_USERNAME=your-email@gmail.com # MAIL_PASSWORD=your-app-password # MAIL_DEFAULT_SENDER=noreply@yourdomain.com # MAIL_MAX_EMAILS=100 # Peppol e-invoicing (optional) # Requires an access point provider. This app uses a generic HTTP adapter. # Enable: # PEPPOL_ENABLED=true # # Sender identifiers (your company in Peppol) # Example endpoint scheme ids: 0088 (GLN), 9906 (VAT), 0208 (LEI) depending on your country/AP rules # PEPPOL_SENDER_ENDPOINT_ID= # PEPPOL_SENDER_SCHEME_ID= # Optional: # PEPPOL_SENDER_COUNTRY=BE # # Access point adapter (HTTP JSON endpoint your AP exposes) # PEPPOL_ACCESS_POINT_URL= # PEPPOL_ACCESS_POINT_TOKEN= # PEPPOL_ACCESS_POINT_TIMEOUT=30 # PEPPOL_PROVIDER=generic # File upload settings MAX_CONTENT_LENGTH=16777216 UPLOAD_FOLDER=/data/uploads # CSRF protection # IMPORTANT: Keep CSRF enabled in production for security # Only disable for development/testing if needed WTF_CSRF_ENABLED=true WTF_CSRF_TIME_LIMIT=3600 # CSRF SSL Strict Mode # Set to false if accessing via HTTP (localhost or IP address) # Set to true only when using HTTPS in production WTF_CSRF_SSL_STRICT=false # CSRF Cookie Settings # Only set these if you need to access the app via IP address or have cookie issues # CSRF_COOKIE_SECURE=false # Set to false for HTTP access # CSRF_COOKIE_SAMESITE=Lax # Options: Strict, Lax, None # CSRF_COOKIE_DOMAIN= # Leave empty for single domain, set for subdomains # Session Cookie Settings for IP Address Access # If accessing via IP address (e.g., 192.168.1.100), use these settings: # SESSION_COOKIE_SAMESITE=Lax # Change to 'None' only if needed for cross-site # SESSION_COOKIE_SECURE=false # Must be false for HTTP # TROUBLESHOOTING CSRF issues ("CSRF token missing or invalid" errors): # 1. SECRET_KEY changed? All CSRF tokens become invalid when SECRET_KEY changes # 2. Cookies blocked? Check browser settings and allow cookies from your domain # 3. Behind a proxy? Ensure proxy forwards cookies and doesn't strip them # 4. Token expired? Increase WTF_CSRF_TIME_LIMIT (in seconds) # 5. Multiple app instances? All must use the SAME SECRET_KEY # 6. Clock skew? Ensure server time is synchronized (use NTP) # 7. Accessing via IP? Set WTF_CSRF_SSL_STRICT=false and SESSION_COOKIE_SECURE=false # 8. Still broken? Try: docker-compose restart app # 9. For testing only: Set WTF_CSRF_ENABLED=false (NOT for production!) # See docs/CSRF_CONFIGURATION.md for detailed troubleshooting # Logging LOG_LEVEL=INFO LOG_FILE=/data/logs/timetracker.log # Analytics and Monitoring # All analytics features are optional and disabled by default # Sentry Error Monitoring (optional) # Get your DSN from https://sentry.io/settings/projects/ # SENTRY_DSN= # SENTRY_TRACES_RATE=0.0 # OTEL OTLP export (optional sink) # Example endpoint: https://otlp-gateway-prod-eu-west-2.grafana.net/otlp # OTEL_EXPORTER_OTLP_ENDPOINT= # OTEL_EXPORTER_OTLP_TOKEN= # # OpenTelemetry SDK (traces + metrics to OTLP; uses same endpoint/token as above) # When unset, tracing and metrics default to enabled if OTLP credentials are present. # ENABLE_TRACING=true # ENABLE_METRICS=true # Optional: OTLP metrics export interval in milliseconds (default 60000) # OTEL_METRICS_EXPORT_INTERVAL_MS=60000 # # Tests only: in-memory tracing without network (pytest) # OTEL_ENABLE_IN_TESTS=1 # Telemetry (optional, opt-in, anonymous) # Sends anonymous installation data to Grafana OTLP (version/platform/install heartbeat) # Requires OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_TOKEN # Default: false (disabled) # See docs/privacy.md for details # ENABLE_TELEMETRY=true # TELE_SALT=8f4a7b2e9c1d6f3a5e8b4c7d2a9f6e3b1c8d5a7f2e9b4c6d3a8f5e1b7c4d9a2f # APP_VERSION= # Automatically read from setup.py, override only if needed