# Conector com o cliente — decisão FECHADA > Este doc guardava as opções em aberto. A decisão foi tomada: **opção A**. > Ficam registradas as alternativas e o porquê da escolha. ## O problema O Oracle da Scardua está em **rede privada** (`10.16.x`), inalcançável da internet. A API roda na VPS da FLS (pública). Algo precisa rodar as queries no Oracle **de dentro** da rede do cliente. Esse algo é este projeto. ## A decisão: opção A — conector que RECEBE Direção **VPS → conector**. A API trata o dado, cifra e faz `POST` aqui; o conector abre e grava no Oracle. - ✅ A VPS **não armazena nada** — é passagem - ✅ Simples: recebe e insere - ⚠️ O conector fica **exposto** — mitigado pelas travas abaixo ### As alternativas descartadas **B) Conector outbound (pull)** — o conector perguntaria pra VPS "tem trabalho?". Sem rota pública no cliente, mais firewall-friendly. Descartada porque exigiria a VPS manter fila de pendências (storage que ela não tem) e mais lógica de polling. **C) Túnel / VPN** — a VPS alcançaria o Oracle direto. Descartada porque poria a senha do Oracle na VPS e daria acesso **amplo** à rede do cliente. ## Travas de segurança (implementadas) Um endpoint público que escreve no banco de produção é alvo de alto valor. O que está no código hoje: | Trava | Onde | Estado | |---|---|---| | **TLS** | proxy na frente do conector | ⬜ depende da publicação | | **Token compartilhado** — header `X-Token`, comparado com `secrets.compare_digest` (não vaza por tempo) | `app/seguranca.py` | ✅ | | **Allowlist de IP** — só a VPS (`179.197.230.154`) | `app/seguranca.py` + `configs.json` | ✅ (vazio = desligado, ligar em produção) | | **Payload cifrado ponta a ponta** | `app/services/cripto.py` | ✅ | | **Validação de schema** | `PayloadCompras` em `app/schemas.py` | ✅ | | **Usuário Oracle de privilégio mínimo** | — | ⬜ fase 4 | | **Rate limiting** | — | ⬜ | ## A criptografia Envelope híbrido — **AES-256-GCM** nos dados, **RSA-OAEP-SHA256** na chave AES: ```json { "alg": "RSA-OAEP-256+A256GCM", "chave": "", "nonce": "", "dados": "" } ``` **Por que não RSA puro:** RSA-2048 com OAEP-SHA256 cifra no máximo ~190 bytes, e o payload de compras passa disso. Envelope é o padrão (mesmo do TLS, PGP e JWE). **O GCM autentica:** se o corpo for adulterado no caminho, o decrypt levanta `InvalidTag` em vez de devolver lixo. Decifrou = veio íntegro. **As chaves:** a **privada** fica só aqui, no `configs.json` (gitignored). A **pública** correspondente vai no `configs.json` da VPS. Hoje é um par de **homologação** — trocar por um definitivo antes de produção. ## Idempotência ⭐ O Holmes reentrega webhook. O `id_processo` (id do processo no Holmes) viaja no payload justamente pra ser a chave de deduplicação. Quando o `INSERT` entrar, **tem que ser `MERGE`** — senão cada reentrega vira linha duplicada. ## Durabilidade Escolhido: **erro sobe, Holmes reentrega.** O conector fora do ar → a API devolve erro → o Holmes reenvia depois. O Holmes é a fila; por isso a API **nunca** pode responder 200 pra algo que não chegou aqui. ⚠️ Depende da política de retry do Holmes, que **ainda não foi confirmada**. Se ele não reentregar, vai ser preciso persistência intermediária na VPS.