# Phase 5 — Tenant credentials and integration secrets

## Security baseline

Tenant credentials now use AES-256-GCM authenticated encryption. The
authenticated context binds every ciphertext to its tenant, optional app, and
secret name. Copying ciphertext to another tenant or modifying it causes
integrity validation to fail.

The runtime requires:

- `PLATFORM_SECRETS_KEYS`: JSON object mapping key versions to URL-safe
  base64-encoded 32-byte keys;
- `PLATFORM_SECRETS_ACTIVE_KEY_VERSION`: version used for new encryption.

There is no default production key. If the keyring is absent, credential APIs
fail closed with HTTP 503.

The current server loads `/var/www/html/.platform-secrets/secrets-keyring.json`.
This directory is outside the Flask and WordPress repositories and outside
their configured Apache DocumentRoot paths. The file must remain mode `0600`.
When root-level configuration management is available, move it to
`/etc/flask_server` and set `PLATFORM_SECRETS_KEYRING_FILE`.

## Rotation

Ciphertexts include their key version. Add a new key to the keyring and change
the active version to encrypt new/updated values while retaining old-key
decryption. A later operational command can re-encrypt existing records before
an old key is removed.

## Write-only API

- `GET /api/v1/platform/integrations/<app>/credentials`
- `PUT /api/v1/platform/integrations/<app>/credentials`
- `DELETE /api/v1/platform/integrations/<app>/credentials/<name>`

The GET endpoint returns names, key versions, and timestamps only. Secret
values are never returned to WordPress. Requests require a valid site JWT,
integration scopes, an active subscription, and entitlement to the target app.

## Pending before real credentials

- Configure the production keyring outside source control.
- Add an administrator UI with app-specific credential schemas.
- Add credential test actions that execute only inside the target app.
- Add re-encryption and master-key retirement commands.
