Central system SMTP settings
The system SMTP singleton provides one installation-wide adapter configuration. It has no merchant owner. Only an AdminUser whose freshly loaded database row has system_admin=true may read or change it; ordinary merchant administrators cannot grant or infer this role.
Web administration
Open the account menu and choose Systemeinstellungen, or visit /admin/system_settings. The menu entry is shown only to system administrators; ordinary tenant administrators receive 403 on the direct route. An operator must provision the first system administrator against a verified existing account; there is no self-service role grant or automatic elevation of tenant administrators.
Enter the SMTP server, port, TLS mode, own EHLO hostname, username and allowed sender. Enter a new password only when setting or replacing it. Saving with an empty password keeps the existing secret. The explicit delete checkbox also disables global email. The form never returns a saved or submitted password, including on validation errors.
The source panel distinguishes stored system configuration from environment fallback. The change log shows the latest twenty global updates and the responsible account, without secret values. A stale form receives a conflict and reloads current values for review instead of overwriting another administrator’s changes.
Saving validates settings but sends no message. The email job uses this configuration and checks the global and merchant enable flags again before each further dispatch. A connection already in progress is not interrupted by changing settings. The allowed sender must match the merchant’s email sender configuration.
Backend interface
Billing::SystemSettings#read(actor:, env: ENV) returns only:
-
smtp_enabled,smtp_host,smtp_port,smtp_helo_domain,smtp_username,smtp_allowed_from, andsmtp_tls_mode; -
smtp_password_presentas a boolean; -
lock_versionandsource(databaseorenvironment_fallback).
Billing::SystemSettings#update!(actor:, attributes:, request_id:, env: ENV) requires the exact lock_version returned by the form. An absent singleton uses revision 0; its first successful save creates revision 1. Stale revisions return 409 SystemSettingsRevisionConflict.
An omitted or blank smtp_password preserves the current encrypted password. On the first database save it preserves the effective environment credential when one exists. A nonblank value replaces it. clear_smtp_password=true clears it and forces SMTP disabled; clear plus a new password is rejected. Read DTOs, model JSON/inspection, validation errors and audits never expose plaintext or ciphertext.
Runtime precedence
Billing::SystemSettings#delivery_runtime returns {enabled:, adapter:, source:} without opening a network session. When the singleton is absent, the existing environment-based SMTP adapter remains the compatibility fallback only when the global BILLING_INVOICE_EMAIL_DELIVERY_ENABLED kill switch is exactly true. The adapter is not constructed while that switch is off. Once row 1 exists, database state is authoritative: a persisted disabled row disables SMTP even when environment variables remain configured.
Disabled empty settings are a valid explicit override. Enabling requires a complete valid host, port, HELO domain, username, allowed sender and encrypted password. Validation constructs the existing SMTP adapter with an injected non-network factory.
Encryption and audit
Passwords are encrypted with authenticated AES-256-GCM through Rails ActiveSupport::MessageEncryptor. The key is derived from the deployment’s Rails key generator and is not stored in the settings row. Rotation of the deployment secret requires a separately planned credential migration or explicit password re-entry.
The setting write and its immutable AuditEvent share one database transaction. Audit rows have merchant_id=NULL, actor admin:<id>, the changed nonsecret field names, and only the password action set, preserved, copied_from_environment, or cleared. They contain no values, password, ciphertext, adapter inspection, or SMTP response.
Access and verification
The global controller uses authenticated sessions, CSRF protection and non-cacheable responses. There is no public REST endpoint for role grants or SMTP credentials. Database configuration becomes authoritative after the first save, including an explicitly disabled configuration. No real SMTP connection is part of saving or testing this settings workflow.
Independent backend, web form and job integration verification: 22 tests, 222 assertions, no failures/errors/skips.
Backend verification
BILLING_DB_PORT=55440 sh bin/test-local test/services/billing/system_settings_test.rb 10 runs, 80 assertions, 0 failures, 0 errors, 0 skips
The tests cover fresh read/write authorization, revoked and ordinary admins, environment fallback, database precedence, revision 0→1 creation, stale revision conflicts, encrypted persistence, exact ciphertext preservation, environment-password migration, explicit clear, disabled blank settings, validation without a session, sanitized serialization/audit, and transactional rollback when audit persistence fails.