Secure CORE and PROJECT credential management

Manage write-only encrypted project keys, controlled Core overrides and private credential provisioning through native KAS Settings.

Credential ownership

CORE credentials and internal encryption keys stay in protected _secrets.php, maintained through authorized private provisioning. PROJECT credentials belong in authenticated-encrypted KAS_KEYS rows in the installation database. Ordinary non-secret options remain in existing KAS Settings. Core updates distribute schema, definitions and application logic; they never distribute credential values or master keys.

Native Settings → Keys

Active project administrators with native privilege 5 or 6 manage credentials through the native Table View and Add/Edit forms. The list shows metadata and availability, never stored values. Names are immutable lowercase ASCII names of up to 96 characters: start with a letter, then use letters, digits, dots, underscores or hyphens. Arbitrary custom application names such as shipping.api are supported; internal platform names and private configuration collisions are reserved.

Enter a replacement value explicitly. A blank edit preserves existing ciphertext. Descriptions are optional and must contain no secrets. Disabled entries block both use and Core fallback. Removing an eligible override intentionally restores its Core default; removing a custom-only name makes it unavailable. Removal requires explicit confirmation of that behavior. The latest 50 metadata-only audit events appear on Edit; audit evidence survives deletion.

Every mutation revalidates the active administrator against KAS_ACCESS and checks session expiry, CSRF and record revision. Generic database writes, export and cloning cannot access the vault tables. Existing values never enter HTML or hidden fields. Native write-only inputs clear after success or failure, so a failed replacement requires re-entry. Credential pages and submissions use no-store responses and trusted HTTPS outside the existing local environment.

Authenticated encryption and private keys

The vault uses Sodium XChaCha20-Poly1305 with a fresh random 24-byte nonce and a dedicated random 32-byte master key per installation. Credential name, purpose, format version and key identifier are authenticated. Legacy password, URL and action-metadata encryption remain separate and are not vault primitives. Missing keys, corrupt ciphertext and storage failures produce safe errors rather than a different provider identity.

The existing protected private configuration array supplies project_keys with an active key ID and a keys map of IDs to base64-encoded 32-byte keys. Provision these once through an authorized private procedure outside the database and web-accessible files; never generate a new key on application startup. Do not reuse a shared platform key, SQL AES key, password or legacy URL-encryption key. Each independently hosted installation needs its own key and database.

Changing the active ID affects new encryptions. Retain older keys until an explicitly authorized re-encryption and verification procedure completes; the UI does not rotate or bulk-migrate keys automatically. Back up matching master keys separately from database ciphertext with restricted access. Losing those keys makes values unrecoverable. Restoring both database and key restores usable secrets, so cloning and restore operations require ownership review. Encryption at rest does not protect against application/server compromise during credential use.

Shared server-side lookup

require_once server_root('rp_framework/project_keys.php');
$credentials = new KasKeys($connection, $GLOBALS['kas_private_secrets'] ?? []);
$token = $credentials->resolve('shipping.api');
if ($token === null) {
    throw new RuntimeException('Shipping credentials are not configured.');
}
// Supply only to the owning adapter. Never echo, log or serialize the token.

The initialized database and private array are trusted application inputs. Never accept a browser-selected database or build an endpoint returning lookup results. KasKeys::runtime() uses the existing global initialized connection. metadata($session, $id) returns authorized metadata without ciphertext. KasKeyException reports safe failures; do not log raw inputs or stack arguments.

Supported messaging overrides

postmark.server_token overrides default canonical mailer configuration. Project tokens keep the application's authorized sender; Core-only address rewriting remains tied to the actual Core token. Explicit existing constructor configuration keeps precedence.

twilio.credentials is one complete JSON object containing account, token, project_id, from or messaging_service, and optional HTTPS callback_url. The account/token/sender/callback group never borrows a Core identity. Invoice SMS requires its existing project identity to match. Explicit invoice-owned SMS configuration retains precedence. Saving validates syntax and completeness, not provider ownership: no external request is made.

Matching Twilio callbacks resolve the account/token/URL/project together through a bounded installation-owned database read before signature verification; event writes still follow authentication. Disabled matching credentials are blocked. Provider subscriptions and private callback authentication require their own approved configuration. No message, consent or charge is authorized merely by saving a credential.

Meta app secrets and WhatsApp phone/WABA/token/callback mapping remain protected private configuration. Arbitrary Meta overrides are not enabled, because changing a phone or token independently does not establish ownership of a shared app or matching callback. Existing explicit credentials are not automatically migrated.

Adding integrations safely

Inspect the native adapter first. Custom-only application keys use the shared lookup without a Core mapping. For an eligible Core override, add an explicit source registry definition, complete validation and atomic resolution of related identities and callback configuration. Keep reserved platform secrets protected and test tampering, missing keys, disabled/fallback behavior, account isolation and authorization. Do not automatically make every private configuration name overridable or add another secret store, sending implementation or AJAX handler.