# UCM settings security

`vtiger_marvelpbx_ucm_settings` is owned by MarvelPBX. It stores one row (`id=1`) containing the HTTPS origin, API username, encrypted password, TLS policy, bounded timeouts, maximum audio bytes, bounded CDR pagination, enable flag, and timestamps.

The manifest contains no credential values. The password is encrypted with AES-256-GCM using a random 96-bit IV and authentication tag. Key material is derived at runtime from VTiger's installation-specific `application_unique_key` with a MarvelPBX-specific context. The key is never stored in the table, source, JavaScript, response, or log. Saving and loading fail closed when OpenSSL or a sufficiently strong installation key is unavailable.

No plaintext password is returned to a browser. Logs are restricted to internal record ID, resolver stage, result, HTTP status class, and elapsed milliseconds. Passwords, cookies, challenges, tokens, paths, filenames, phone numbers, session IDs, response bodies, and encryption keys are forbidden.

## Operator bootstrap while UI is deferred

Use only a maintenance shell with the full VTiger bootstrap loaded and filesystem/database backup already verified. Instantiate `MarvelPBX_RecordingSettingsRepository` with the application `$adb` and call `save()` with values supplied interactively or through protected process input. Do not place credentials in shell history, SQL files, command arguments, web-accessible scripts, or repository files. Confirm the database target before calling `save()`, then remove the one-time operator harness.

Start with `enabled=false`, `verify_tls=true`, conservative limits, and a least-privilege UCM API user. Enable only after validating encryption round-trip and network trust. Temporary `verify_tls=false` is a DEV-only diagnostic choice and must be explicitly recorded and reverted.

The repository uses parameterized statements and validates the HTTPS origin and safety bounds. `module.preupdate` accepts an absent table for legacy upgrades but rejects an unexpected existing shape. Postinstall/postupdate require the exact audited schema. Preuninstall removes only this owned table and the owned reporting view according to the documented uninstall policy.
