# Runbook — Re-provisionar Partner API keys após ETL/re-migração do Core

Data: 2026-07-03.

## O problema (mapeado para não repetir)

Toda **re-migração/ETL do Core** (`mon_core`) **ZERA a tabela `api_keys`**. Foi o
que aconteceu em 2026-07-01: o `mon_core` foi re-migrado do backup do legado
Autbank, que **não carrega Partner API keys** (é feature nossa), e as migrations
fazem `DROP + CREATE` de tabelas legadas incompatíveis (gotcha #1 do
`pix/CLAUDE.md`, mesmo princípio no Core). Resultado observado em 2026-07-03:

- `mon_core.api_keys` = **0 linhas** (mas `partners` intacta — 2 ativos).
- Todo `POST /api/partner/v1/oauth/token` (client_credentials) →
  `{"error":"invalid_client","error_description":"Client authentication failed"}`,
  porque o OAuth casa `WHERE client_id = ? AND client_secret_hash = sha256(secret)`
  e não há linha. O código está **correto**; é perda de dado.

## A fonte de verdade que SOBREVIVE

Os secrets ficam no **AWS Secrets Manager** e não são tocados pelo ETL:

```
monetarie/<env>/partner/<slug>/api_credentials
```

Cada um traz `client_id`, `client_secret` (`sk_` + 64 hex), `partner_id` e
(quando aplicável) `permissions`. Como o `client_secret_hash` armazenado é
`sha256(client_secret)`, dá para recriar a linha `api_keys` com o **mesmo**
`client_id`/`secret` — as credenciais do cliente voltam a funcionar **sem
redistribuir nada**.

## O procedimento (idempotente)

**Rodar SEMPRE após qualquer ETL/re-migração do Core**, com a VPN ativa:

```bash
# mostra o que faria, sem aplicar:
DRY_RUN=1 scripts/partner_api_reprovision.sh

# aplica (idempotente — chaves já existentes viram no-op "JA_EXISTE"):
scripts/partner_api_reprovision.sh
```

O script (`scripts/partner_api_reprovision.sh`):

1. Lista os secrets `monetarie/<env>/partner/*/api_credentials`.
2. Para cada um, computa o `sha256` do secret **localmente** e monta um insert
   idempotente (por `client_id`).
3. Aplica via `ecs execute-command` no `core-api` (rpc → `Repo.insert`).
4. **Verifica end-to-end**: `curl` no token endpoint com as credenciais reais →
   espera `HTTP 200` + `access_token`.

### Segurança (regra #10)

O **plaintext do `client_secret` NUNCA** é escrito em arquivo, log ou enviado ao
nó — só o `sha256` (que é o que fica armazenado) vai ao `core-api`. O plaintext
só é usado, em memória, no `curl` de verificação (o mesmo caminho do cliente).

## Caveat de permissões

Se o secret **não** trouxer `permissions` (ex.: `herbeth-santana`), o script
aplica o set padrão de 13 scopes de partner (`DEFAULT_PERMS` no script). Se o
cliente deve ter um escopo menor, ajuste depois via a tela de Parceiros ou
`ApiKeys.update`/`status_changeset`.

## Prevenção adicional (opcional)

Alternativa a re-provisionar depois: fazer `pg_dump`/`restore` só da tabela
`api_keys` antes/depois do ETL. O script acima é o caminho recomendado porque a
fonte de verdade (Secrets Manager) é independente do backup do legado.

Ver memória `monetarie-partner-api-keys-wiped`.
