--- :site: :host: <%= ENV['HOST'] || 'localhost:3000' %> # Turns https/http on or off when generating links :ssl: <%= ENV['SSL'] == 'true' || false %> # IMPORTANT: After setting the secret, it should not be changed. # Be sure to create and store a backup in a secure offsite # location. Changing the secret can lead to unforeseen issues # like not being able to decrypt existing secrets. :secret: <%= ENV['SECRET'] || nil %> # API and UI Configuration :interface: :ui: # Controls whether the web interface is enabled # When false, only a basic explanation page is shown :enabled: <%= ENV['UI_ENABLED'] != 'false' %> # Header configuration # Controls branding and navigation in the site header :header: # Control switch to enable/disable header customization :enabled: <%= ENV['HEADER_ENABLED'] != 'false' %> # Branding configuration for logo and company name :branding: # Logo configuration :logo: # URL to logo image file :url: <%= ENV['LOGO_URL'] || 'DefaultLogo.vue' %> # Alt text for logo image :alt: <%= ENV['LOGO_ALT'] || 'Share a Secret One-Time' %> # Where the logo links to when clicked :href: <%= ENV['LOGO_LINK'] || '/' %> # Company name override (falls back to i18n if not set) :site_name: <%= ENV['SITE_NAME'] || 'One-Time Secret' %> # Navigation configuration :navigation: # Enable/disable header navigation entirely :enabled: <%= ENV['HEADER_NAV_ENABLED'] != 'false' %> # Footer link configuration # These links appear in the footer of each page :footer_links: # Master switch to enable/disable all footer links :enabled: <%= ENV['FOOTER_LINKS'] == 'true' || false %> # Organized groups of links :groups: - :name: legal :i18n_key: web.footer.legals :links: - :text: Terms of Service :i18n_key: terms-of-service # Replace with your own terms URL or use relative path like /terms :url: <%= ENV['TERMS_URL'] %> :external: <%= ENV['TERMS_EXTERNAL'] || false %> - :text: Privacy Policy :i18n_key: privacy-policy # Replace with your own privacy URL or use relative path like /privacy :url: <%= ENV['PRIVACY_URL'] %> :external: <%= ENV['PRIVACY_EXTERNAL'] || false %> - :name: resources :i18n_key: web.footer.resources :links: - :text: Status :i18n_key: status # Replace with your status page URL if you have one :url: <%= ENV['STATUS_URL'] %> :external: <%= ENV['STATUS_EXTERNAL'] || true %> :icon: signal - :text: About :i18n_key: web.COMMON.about # Replace with your about page URL :url: <%= ENV['ABOUT_URL'] %> :external: <%= ENV['ABOUT_EXTERNAL'] || false %> - :name: support :i18n_key: web.footer.support :links: - :text: Contact :i18n_key: web.footer.contact :url: <%= ENV['CONTACT_URL'] %> :external: false # Controls whether the API endpoints are available. When disabled, the API # is completely disabled. Requests to /api/* will return 404. :api: :enabled: <%= ENV['API_ENABLED'] != 'false' %> # Configuration options for secret management :secret_options: # Default Time-To-Live (TTL) for secrets in seconds # This value is used if no specific TTL is provided when creating a secret :default_ttl: <%= ENV['DEFAULT_TTL'] || nil %> # Available TTL options for secret creation (in seconds) # These options will be presented to users when they create a new secret # Format: String of integers representing seconds :ttl_options: <%= (ENV['TTL_OPTIONS'] || nil) %> # Settings for the passphrase field that protects access to secrets :passphrase: # Require users to enter a passphrase when creating secrets :required: <%= ENV['PASSPHRASE_REQUIRED'] == 'true' || false %> # Minimum number of characters required for passphrases :minimum_length: <%= ENV['PASSPHRASE_MIN_LENGTH'] || 8 %> # Maximum number of characters allowed for passphrases :maximum_length: <%= ENV['PASSPHRASE_MAX_LENGTH'] || 128 %> # Enforce complexity requirements (uppercase, lowercase, numbers, symbols) :enforce_complexity: <%= ENV['PASSPHRASE_ENFORCE_COMPLEXITY'] == 'true' || false %> # Settings for password generation (when users click "Generate Password") :password_generation: # Default length for generated passwords :default_length: <%= ENV['PASSWORD_GEN_LENGTH'] || 12 %> # Character sets to include in generated passwords :character_sets: # Include uppercase letters (A-Z) :uppercase: <%= ENV['PASSWORD_GEN_UPPERCASE'] != 'false' %> # Include lowercase letters (a-z) :lowercase: <%= ENV['PASSWORD_GEN_LOWERCASE'] != 'false' %> # Include numbers (0-9) :numbers: <%= ENV['PASSWORD_GEN_NUMBERS'] != 'false' %> # Include symbols (!@#$%^&*()_+-=[]{}|;:,.<>?) :symbols: <%= ENV['PASSWORD_GEN_SYMBOLS'] == 'true' || false %> # Exclude ambiguous characters (0, O, l, 1, I) to prevent confusion :exclude_ambiguous: <%= ENV['PASSWORD_GEN_EXCLUDE_AMBIGUOUS'] != 'false' %> # Registration and Authentication settings :authentication: # Can be disabled altogether, including API authentication. :enabled: <%= ENV['AUTH_ENABLED'] != 'false' %> # Allow users to create accounts. This can be disabled if you plan # to create accounts manually or enable during setup when accounts # can be created and then disabled to prevent any new users from # creating accounts. :signup: <%= ENV['AUTH_SIGNUP'] != 'false' %> # Generally if you allow registration, you allow signin. But there # are circumstances where it's helpful to turn off authentication # temporarily. :signin: <%= ENV['AUTH_SIGNIN'] != 'false' %> # By default, new accounts need to verify their email address before # they can sign in. This is a security measure to prevent spamming # and abuse of the system. If you're running a private instance or # an instance for your team or company, you can disable this feature # to make it easier for users to sign in. :autoverify: <%= ENV['AUTH_AUTOVERIFY'] == 'true' || false %> # When enabled, the homepage secret form is not available unless # the user is logged in. Similar to a disabled homepage, but still # shows the header with logo and navigation links. This allows for # a more restrictive mode where only authenticated users can create # secrets while maintaining site navigation and branding. :required: <%= ENV['AUTH_REQUIRED'] == 'true' %> # Email addresses listed below will be granted automatic # administrative privileges upon account creation. These # accounts have access to a page that show basic system stats. # The term "colonel" is used instead of "admin". "Colonel" (which # is pronounced "kernel") references both the protected core of a # Linux system and a military rank, symbolizing high-level access # permissions. This naming helps avoid basic, automated attacks # that target common admin URLs or role names. :colonels: - <%= ENV['COLONEL'] || 'CHANGEME@example.com' %> # A captcha-like feature that protects the feedback form # from bots and other tomfoolery. :authenticity: :type: <%= ENV['AUTHENTICITY_TYPE'] || 'altcha' %> :secret_key: <%= ENV['AUTHENTICITY_SECRET_KEY'] || '' %> # Links to documentation. For onetimesecret.com, this is # docs.onetimesecret.com. :support: :host: <%= ENV['SUPPORT_HOST'] || nil %> :plans: :enabled: <%= ENV['PLANS_ENABLED'] == 'true' || false %> :stripe_key: <%= ENV['STRIPE_KEY'] || nil %> :regions: :enabled: <%= ENV['REGIONS_ENABLED'] == 'true' || false %> :current_jurisdiction: <%= ENV['JURISDICTION'] || nil %> :domains: :enabled: <%= ENV['DOMAINS_ENABLED'] == 'true' || false %> # The default domain used for link URLs. When not set or empty, # site.host is used. :default: <%= ENV['DEFAULT_DOMAIN'] || nil %> # The cluster type determines how the application will support # multiple domains. The default is nil which means that the # application itself is responsible for handling multiple domains. :cluster: :type: <%= ENV['CLUSTER_TYPE'] || nil %> :api_key: <%= ENV['APPROXIMATED_API_KEY'] || nil %> :cluster_ip: <%= ENV['APPROXIMATED_PROXY_CLUSTER_IP'] || '' %> :cluster_host: <%= ENV['APPROXIMATED_PROXY_CLUSTER_HOST'] || '' %> :cluster_name: <%= ENV['APPROXIMATED_PROXY_CLUSTER_NAME'] || '' %> :vhost_target: <%= ENV['APPROXIMATED_PROXY_VHOST_TARGET'] || '' %> :features: :incoming: :enabled: false :email: CHANGEME@example.com :passphrase: abacus # Redis Configuration :redis: # Main Redis connection URI - Specify full connection string including auth # Format: redis://[:password@]host[:port]/[db-number] # Examples: # - redis://mypassword@localhost:6379/0 # Simple password auth # - redis://user:pass@localhost:6379/0 # Username/password auth # - redis://redis.example.com:6379/0 # No auth (development only) :uri: <%= ENV['REDIS_URL'] || 'redis://CHANGEME@127.0.0.1:6379/0' %> # Database Mapping Configuration # These mappings allow separating data across different Redis logical # databases (0-15). These can be arranged any way you like. # Note: If using a Redis provider with single-database limitation # (e.g., Upstash) set all values to 0. :dbs: :session: <%= ENV['REDIS_DBS_SESSION'] || 1 %> :custom_domain: <%= ENV['REDIS_DBS_CUSTOM_DOMAIN'] || 6 %> :customer: <%= ENV['REDIS_DBS_CUSTOMER'] || 6 %> :subdomain: <%= ENV['REDIS_DBS_SUBDOMAIN'] || 6 %> :metadata: <%= ENV['REDIS_DBS_METADATA'] || 7 %> :email_receipt: <%= ENV['REDIS_DBS_EMAIL_RECEIPT'] || 8 %> :secret: <%= ENV['REDIS_DBS_SECRET'] || 8 %> :rate_limit: <%= ENV['REDIS_DBS_RATE_LIMIT'] || 2 %> :feedback: <%= ENV['REDIS_DBS_FEEDBACK'] || 11 %> :exception_info: <%= ENV['REDIS_DBS_EXCEPTION_INFO'] || 12 %> :system_settings: <%= ENV['REDIS_DBS_SYSTEM_SETTINGS'] || 15 %> # Logging Configuration :logging: # HTTP request logs (Rack::CommonLogger) :http_requests: <%= ENV['LOG_HTTP_REQUESTS'] != 'false' %> # Sending Emails :emailer: # Local Development with Mailpit # ----------------------------- # Mailpit is a dev SMTP server that captures emails for testing # Install: brew install mailpit # Start: mailpit # Web UI: http://localhost:8025 # # :mode: smtp # Use SMTP mode for local testing # :from: secure@onetimesecret.com # Sender address # :fromname: OTS Support # Sender name # :host: 127.0.0.1 # Mailpit host # :port: 1025 # Mailpit default SMTP port # :user: ~ # No auth needed for Mailpit # :pass: ~ # No auth needed for Mailpit # :auth: false # Disable SMTP auth for Mailpit # :tls: false # Disable TLS for local testing # Production Settings (for reference) # ---------------------------------- :mode: <%= ENV['EMAILER_MODE'] || 'smtp' %> :from: <%= ENV['FROM_EMAIL'] || ENV['FROM'] || 'CHANGEME@example.com' %> :fromname: <%= ENV['FROMNAME'] || 'Support' %> :host: <%= ENV['SMTP_HOST'] || 'smtp.provider.com' %> :port: <%= ENV['SMTP_PORT'] || 587 %> :user: <%= ENV['SMTP_USERNAME'] %> :pass: <%= ENV['SMTP_PASSWORD'] %> :auth: <%= ENV['SMTP_AUTH'] || 'login' %> :tls: <%= ENV['SMTP_TLS'] %> :mail: :truemail: # Available validation types: :regex, :mx, :mx_blacklist, :smtp :default_validation_type: :regex # Required for :smtp validation :verifier_email: <%= ENV['VERIFIER_EMAIL'] || 'CHANGEME@example.com' %> #:verifier_domain: <%= ENV['VERIFIER_DOMAIN'] || 'example.com' %> #:connection_timeout: 2 #:response_timeout: 2 #:connection_attempts: 3 #:validation_type_for: # 'example.com': :regex # # Truemail will only validate email addresses that match the # domains listed in :allowed_domains. If the domain is not # listed, the email address will always be considered invalid. :allowed_domains_only: false # # Email addresses in this list will always be valid. #:allowed_emails: [] # # Email addresses in this list will always be invalid. #:blocked_emails: [] # # Addresses with these domains will always be valid #:allowed_domains: [] # # Addresses with these domains will always be invalid #:blocked_domains: [] # # Exclude these IP addresses from the MX lookup process. #:blocked_mx_ip_addresses: [] # # Name servers to use for MX et al record lookup. # Default is CloudFlare, Google, Oracle/OpenDNS servers. :dns: - 1.1.1.1 - 8.8.4.4 - 208.67.220.220 #:smtp_port: 25 # # End smtp validation after the first invalid response rather than # retrying, followed by trying the next server. Can reduce the time # time to validate an email address, but may not catch all issues. :smtp_fail_fast: false # # Parse the content of the SMTP error message to determine if the # email address is valid. This can be useful for some SMTP servers # that don't return exact answers. :smtp_safe_check: true # # Whether to disable the RFC MX lookup flow. When true, only DNS # validation will be performed on MX and Null MX records. :not_rfc_mx_lookup_flow: false # # Override default regular expression pattern for email addresses # and/or the content in SMTP error messages. #:email_pattern: /regex_pattern/ #:smtp_error_body_pattern: /regex_pattern/ # # Log to the console, a file, or both. The ruby process must have # write access to the log file. The log file will be created if it # does not exist. Log file rotation is not handled by the app. :logger: # One of: :error (default), :unrecognized_error, :recognized_error, :all. :tracking_event: :error :stdout: true # log_absolute_path: '/home/app/log/truemail.log' :internationalization: :enabled: <%= ENV['I18N_ENABLED'] == 'true' || false %> :default_locale: <%= ENV['I18N_DEFAULT_LOCALE'] || 'en' %> :fallback_locale: fr-CA: [fr_CA, fr_FR, en] fr: [fr_FR, fr_CA, en] de-AT: [de_AT, de, en] de: [de, de_AT, en] it: [it_IT, en] pt: [pt_BR, en] default: [en] # A list of ISO language codes (e.g., 'en' for English, 'es' # for Spanish, etc.). There is a corresponding file in src/locales # with the same name containing the translated text. If it's not # selected automatically, users are able to select their preferred # language by using the toggle in the footer or in the settings # modal if they're logged in. :locales: - bg - da_DK - de - de_AT - el_GR - en - es - fr_CA - fr_FR - it_IT - ja - ko - mi_NZ - nl - tr - uk - pl - pt_BR - sv_SE :incomplete: - ar - ca_ES - cs - he - hu - pt_PT - ru - sl_SI - vi - zh :diagnostics: # If this is false, the rest of the settings are ignored :enabled: <%= ENV['DIAGNOSTICS_ENABLED'] == 'true' || false %> :sentry: # Default Sentry configuration that applies to both frontend and # backend. Values set here are overridden by codebase-specific ones. # # `dsn` - Primary Sentry DSN. # `sampleRate` - Percentage of events to sample (0.0 to 1.0) # `maxBreadcrumbs` - Maximum number of breadcrumbs to capture # `logErrors` - Whether to log errors to console :defaults: :dsn: <%= ENV['SENTRY_DSN'] || nil %> :sampleRate: <%= ENV['SENTRY_SAMPLE_RATE'] || '0.10' %> :maxBreadcrumbs: <%= ENV['SENTRY_MAX_BREADCRUMBS'] || 5 %> :logErrors: <%= ENV['SENTRY_LOG_ERRORS'] != 'false' %> # Ruby backend-specific Sentry configuration # # `dsn` - Backend-specific Sentry DSN :backend: :dsn: <%= ENV['SENTRY_DSN_BACKEND'] || nil %> # Vue frontend-specific Sentry configuration # Options here map directly to @sentry/vue client options # These options are passed directly to @sentry/vue client initialization # and to maintain type safety, they must be typed in # src/types/diagnostics.ts DiagnosticsConfig interface. # # `dsn` - Frontend-specific Sentry DSN # `trackComponents` - Enable automatic instrumentation of Vue components :frontend: :dsn: <%= ENV['SENTRY_DSN_FRONTEND'] || nil %> :trackComponents: <%= ENV['SENTRY_VUE_TRACK_COMPONENTS'] != 'false' %> :limits: # This section defines rate limits for various events per user # per a rolling 20 minute period. Each key is an event name # and the value is the max count allowed. Changes require # restart of the app. :check_status: 10000 :create_secret: 1000 :create_account: 10 :update_account: 10 :email_recipient: 50 :send_feedback: 10 :authenticate_session: 5 :get_page: 1000 :dashboard: 1000 :failed_passphrase: 5 :show_metadata: 1000 :show_secret: 1000 :burn_secret: 1000 :destroy_account: 2 :forgot_password_request: 2 :forgot_password_reset: 3 :generate_apitoken: 10 :add_domain: 30 :remove_domain: 30 :list_domains: 100 :get_domain: 100 :verify_domain: 25 :report_exception: 50 :attempt_secret_access: 10 :update_branding: 50 :destroy_session: 5 :get_domain_brand: 1000 :get_domain_logo: 1000 :get_image: 1000 :remove_domain_logo: 20 :show_account: 100 :stripe_webhook: 25 :update_domain_brand: 50 :view_colonel: 100 :external_redirect: 100 :update_colonel_settings: 50 :development: # Development Mode Configuration # # There are two ways to run the frontend in development: # # 1. Behind a reverse proxy (e.g., Caddy, nginx) # - Set :enabled to true # - Leave :frontend_host empty # - Configure your reverse proxy to handle /dist/* routes # # 2. Using the built-in rack-proxy (new) # - Set :enabled to true # - Set :frontend_host to 'http://localhost:5173' # - The application will proxy /dist/* requests to the Vite dev server # # When development mode is enabled, the application expects a Vite # development server to be running. This allows live-reloading of frontend # changes without rebuilding: # # $ pnpm run dev # VITE v5.3.4 ready in 38 ms # # -> Local: http://localhost:5173/dist/ # -> Network: use --host to expose # -> press h + enter to show help # # When disabled (or in production), the application serves pre-built assets # from public/web/dist instead. # :enabled: <%= ['development', 'dev'].include?(ENV['RACK_ENV']) %> :debug: <%= ['true', '1', 'yes'].include?(ENV['ONETIME_DEBUG']) %> # Frontend host configuration: # - Set to 'http://localhost:5173' to use built-in proxy # - Leave empty when using a reverse proxy (like nginx, caddy, etc) :frontend_host: <%= ENV['FRONTEND_HOST'] || 'http://localhost:5173' %> # Settings for experimental features that are still being tested. # All of these features are turned off by default and may not work as expected. :experimental: # SECURITY FEATURE: Controls whether the application can run without a # site.secret (either in this file or via the SECRET env var). # # DEFAULT: false (application will fail to start if site.secret is nil) # # WARNING: Setting to true presents significant security risks: # - Secrets may be stored without proper encryption # - Unauthorized access to sensitive data becomes possible # - Secret link integrity cannot be guaranteed # # VALID USE CASES (temporary only): # 1. RECOVERY: You accidentally ran with nil secret and need to recover # existing secrets created during that time. Enable temporarily until # all affected secrets expire (max TTL period). # 2. MIGRATION: During a controlled migration between encryption schemes, # with proper security measures in place. # # BEHAVIOR WHEN TRUE: # - Application starts without failing # - Warning logs appear at startup # - When decrypting, CipherErrors with the real secret will cause # automatic retry with nil secret # :allow_nil_global_secret: <%= ENV['ALLOW_NIL_GLOBAL_SECRET'] == 'true' || false %> # SECURITY FEATURE: Support for rotating global secret # # FORMAT: Array of strings containing previous secret values # ["old_secret_1", "old_secret_2", "oldest_secret"] # # USAGE: When rotating secrets, add old values here while setting new primary # secret in site.secret or SECRET env var. The application will: # 1. Use current primary secret for all new encryptions # 2. Try each rotated secret (in order) for decryption when primary fails # # MAINTENANCE: Remove secrets from this list after the relevant secrets # have expired or has been successfully re-encrypted with the current # primary secret. An easy guideline is the maximum TTL in `ttl_options`. :rotated_secrets: [] # When set to true, the Rack::Builder#freeze_app freezes the middleware stack # after initialization to prevent any runtime modifications to the app # middleware chain. This is a security measure that prevents malicious # code from injecting new middleware. # # Effects: # - Freezes the middleware stack, preventing adding/removing after boot # - Freezes the app object passed to Rack::Builder # - Does not affect request/response object mutability # # Note: This setting can help make your application more secure by preventing # middleware chain manipulation at runtime. For most applications, this # should be enabled in production environments. :freeze_app: false # Middleware Configuration # Controls which security and performance middleware components are enabled :middleware: # Serve static files for frontend vue application :static_files: true # Sanitizes request parameters to ensure proper UTF-8 encoding # Prevents encoding-based attacks and malformed input :utf8_sanitizer: true # Protects against Cross-Site Request Forgery (CSRF) attacks # Validates that requests originate from the same site :http_origin: false # Escapes HTML entities in request parameters # Helps prevent XSS attacks via request parameters :escaped_params: false # Sets X-XSS-Protection header to enable browser XSS filtering # Modern browsers rely less on this as CSP becomes standard :xss_header: false # Prevents your site from being embedded in frames (clickjacking protection) # Sets X-Frame-Options header to SAMEORIGIN or DENY :frame_options: false # Blocks directory traversal attacks using "../" in paths # Critical for preventing unauthorized file access :path_traversal: false # Protects against cookie tossing attacks # Prevents session fixation via manipulated cookies :cookie_tossing: false # Prevents IP spoofing attacks by validating IP addresses # Useful when IP-based access controls are implemented :ip_spoofing: false # Forces all connections to use HTTPS via HSTS headers # Disable only for development or when behind a secure proxy :strict_transport: false # When enabled, adds Content-Security-Policy headers to the response. When # `development.enabled=true`, the headers will be less restrictive but still # prevent any content loading from other origins. In regular production mode, # a secure nonce will be included with the headers and unsafe-inline content # is completely blocked. The nonce can be accessed via the rack request object # `req.env['ots.nonce']`. The backend views grab it from there to add it to # all front-end script and style assets automatically. You'd only need to use # the none if you're adding new script or style dependencies. # When disabled, no csp headers are included in any environment. :csp: :enabled: <%= ENV['CSP_ENABLED'] == 'true' || false %>