From 9da292a02f2faf2e69c7a4dc95e03af7fc37cb9e Mon Sep 17 00:00:00 2001 From: patote85 <47055922+patote85@users.noreply.github.com> Date: Sun, 18 Oct 2026 12:45:38 -0300 Subject: [PATCH 1/2] =?UTF-8?q?docs(adr):=20ADR-002=20auth=20Cognito=20JWT?= =?UTF-8?q?,=20ADR-003=20HttpApi+CDK=20Python,=20ADR-004=20Bend/JEV=20gate?= =?UTF-8?q?=20+=20=C3=ADndice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/adr/ADR-002-auth-cognito-jwt-httpapi.md | 42 +++++++++++++++ docs/adr/ADR-003-httpapi-lambda-cdk-python.md | 43 +++++++++++++++ docs/adr/ADR-004-bend-jev-gate-nao-runtime.md | 53 +++++++++++++++++++ docs/adr/README.md | 21 ++++++++ 4 files changed, 159 insertions(+) create mode 100644 docs/adr/ADR-002-auth-cognito-jwt-httpapi.md create mode 100644 docs/adr/ADR-003-httpapi-lambda-cdk-python.md create mode 100644 docs/adr/ADR-004-bend-jev-gate-nao-runtime.md create mode 100644 docs/adr/README.md diff --git a/docs/adr/ADR-002-auth-cognito-jwt-httpapi.md b/docs/adr/ADR-002-auth-cognito-jwt-httpapi.md new file mode 100644 index 0000000..25a6eb8 --- /dev/null +++ b/docs/adr/ADR-002-auth-cognito-jwt-httpapi.md @@ -0,0 +1,42 @@ +# ADR-002 — Auth padrão: Cognito JWT authorizer no HttpApi + +- **Status:** Proposed +- **Data:** 2026-10-18 +- **Escopo:** todos os repos serverless do Rodrigo (padrão da squad) +- **Decisor:** Chefe via CAB (aprovação) / Arquiteto (proposta) + +## Contexto + +A decisão de auth existe hoje só dentro da ADR-001 (PR3, CAB 2026-09-20) e só para a exclusão Pix. Na main do canônico (`cdk/app.py`, PR #6): Cognito User Pool + `HttpJwtAuthorizer` com issuer do pool e audience = app client; `POST /solicitar-exclusao-cliente`, `GET /status-exclusao/{cliente_id}` e `POST /confirmar-pagamento` com authorizer; `GET /health` público; sem WAF/ACM; CORS restrito a localhost (ou context `corsOrigins`). Em `aws-crud-api-lambda-dynamodb` (`template.yaml`) **não há auth** ("add auth later"). Sem um padrão escrito, cada repo novo reabre a discussão. + +## Decisão + +1. Toda API HttpApi usa **JWT authorizer nativo apontando para um Cognito User Pool**. +2. **Todas as rotas protegidas, exceto `GET /health`** (probe/smoke). Rota pública nova = exceção de exposição pública → sobe ao Rodrigo via Chefe. +3. **Um User Pool simples por ambiente/lab**, por stack: login por e-mail, app client sem secret (`USER_PASSWORD` + `USER_SRP`), `RemovalPolicy.DESTROY` só em lab. +4. **Sem WAF por enquanto.** Proteção de lab = JWT + throttle do stage (100 rps / burst 200 no canônico) + CORS sem `*`. +5. SECURITY.md só descreve o que o IaC tem. + +## Consequências / trade-offs + +- (+) Validação de token no API Gateway, sem código de auth na Lambda; custo de Cognito baixo em lab. +- (+) Um padrão só: issuer + audience, fácil de revisar em PR. +- (−) Autorização fina (escopos/claims por rota) fica para a app ou para ADR futura. +- (−) `self_sign_up_enabled=True` no canônico: qualquer um com o client id cria usuário. Aceito em lab; **produção exige revisão** (desligar sign-up ou ADR nova). +- (−) Sem WAF: sem rate limit por IP nem regras gerenciadas. Revisitar antes de qualquer exposição pública real. +- Dívida: `aws-crud-api-lambda-dynamodb` está fora do padrão (sem auth). Adequar em PR próprio quando o Chefe pedir. + +## Princípios do pstack aplicados + +- **Encode Lessons in Structure:** a regra "toda rota tem authorizer, exceto /health" deve virar teste de synth (no estilo de `tests/test_cdk_cors_ban.py`), não só esta prosa. Mudou: follow-up pro Dev listado em Pendente. +- **Boundary Discipline:** auth validada na borda (API Gateway), a Lambda confia no evento. Mudou: nenhum código de JWT na app. +- **Subtract Before You Add:** WAF, domínio custom e Lambda authorizer ficaram fora. Mudou: escopo do padrão = o que já está na main. + +## Alternativas descartadas + +- **Lambda authorizer próprio:** mais código, latência e custo, sem ganho em lab. +- **IAM auth (SigV4):** bom para máquina-a-máquina, ruim para cliente web/mobile. +- **API key / usage plans:** não existe no HttpApi e não é autenticação. +- **WAF já agora:** custo fixo mensal por Web ACL sem tráfego real que justifique. + +**Pendente:** teste de synth "authorizer em toda rota exceto /health" (Dev, quando o Chefe pedir). diff --git a/docs/adr/ADR-003-httpapi-lambda-cdk-python.md b/docs/adr/ADR-003-httpapi-lambda-cdk-python.md new file mode 100644 index 0000000..476e012 --- /dev/null +++ b/docs/adr/ADR-003-httpapi-lambda-cdk-python.md @@ -0,0 +1,43 @@ +# ADR-003 — Padrão serverless: API Gateway HttpApi + Lambda, IaC em CDK Python + +- **Status:** Proposed +- **Data:** 2026-10-18 +- **Escopo:** todos os repos serverless do Rodrigo (padrão da squad) +- **Decisor:** Chefe via CAB (aprovação) / Arquiteto (proposta) + +## Contexto + +As duas APIs AWS já usam **HttpApi (v2)**, não REST API: o canônico `aws-lambda-api-gateway-python` (`aws_apigatewayv2.HttpApi` em `cdk/app.py`) e `aws-crud-api-lambda-dynamodb` (`AWS::Serverless::HttpApi` em `template.yaml`, comentário "cheaper & simpler than REST API"). O IaC diverge: o canônico é **CDK Python** (ADR-001 PR2, PR #5; CI `make check` + `cdk synth` sem deploy, PR #9); o CRUD é **SAM** (`sam validate --lint`, PR #2). A ADR-001 já fixou "Terraform: não" para a exclusão Pix. Falta o padrão geral. + +## Decisão + +1. API pública/síncrona = **API Gateway HttpApi + Lambda (Python 3.12)** com integração proxy. +2. IaC padrão de app serverless = **CDK Python**, um stack por bounded context, no mesmo repo do código (`cdk/` + `lambda/` + `tests/`). +3. CI obrigatório sem credenciais AWS: `make check` = lint + typecheck + testes + `cdk synth`. Deploy só com CAB. +4. Throttle no stage e CORS sem `*` (já testado por `test_cdk_cors_ban.py`). +5. SAM e Terraform não entram em repo novo de app serverless. O CRUD em SAM fica como está (demo/referência) até o Chefe pedir migração. + +## Consequências / trade-offs + +- (+) **Custo:** HttpApi é bem mais barato por milhão de requests que REST API (ordem de ~70% menos, pela tabela pública da AWS; conferir a região antes de citar valor). +- (+) **Latência e simplicidade:** menos overhead, JWT authorizer nativo (ADR-002), CORS nativo. +- (+) CDK Python: mesma linguagem da Lambda, grants tipados (`grant_read_write_data`), `cdk synth` testável no CI. +- (−) **Perde features do REST API:** usage plans e API keys, **WAF associado direto ao stage**, request validation por modelo, caching, transformação de request/response (VTL), endpoint privado/edge. Validação de payload fica na Lambda (Powertools / tipos). +- (−) Precisa WAF ou usage plan? Reabrir com ADR nova (REST API ou CloudFront + WAF na frente). +- (−) CDK exige Node no CI (`npx aws-cdk@2`) e bootstrap da conta no primeiro deploy (Infra). +- Dívida: dois sabores de IaC convivendo (CDK no canônico, SAM no CRUD). + +## Princípios do pstack aplicados + +- **Laziness Protocol / Subtract Before You Add:** um IaC e um tipo de API só; não manter SAM, CDK e Terraform lado a lado em repo novo. Mudou: SAM e Terraform saem do padrão. +- **Prove It Works:** `cdk synth` no CI prova o IaC sem deploy. Mudou: o `synth` entra no `make check` como gate obrigatório. +- **Encode Lessons in Structure:** CORS sem `*` já é teste; o padrão de IaC deve virar check de CI, não só ADR. Mudou: follow-up de check em Pendente. + +## Alternativas descartadas + +- **REST API (v1):** mais features, mas custo e latência maiores sem uso real de usage plans/WAF/VTL hoje. +- **SAM:** ótimo para demo, mas mistura YAML e Python e não tem grants tipados. Fica só no CRUD legado. +- **Terraform:** bom para plataforma, mas para app serverless separa IaC do código e repete o problema da ADR-001 (IaC órfã). +- **Lambda Function URL:** sem authorizer JWT, throttle por rota nem CORS gerenciado no mesmo nível. + +**Pendente:** decidir se o CRUD migra para CDK (Chefe) e criar o check de "IaC padrão" no template de repo (Dev/Infra). diff --git a/docs/adr/ADR-004-bend-jev-gate-nao-runtime.md b/docs/adr/ADR-004-bend-jev-gate-nao-runtime.md new file mode 100644 index 0000000..3a656ce --- /dev/null +++ b/docs/adr/ADR-004-bend-jev-gate-nao-runtime.md @@ -0,0 +1,53 @@ +# ADR-004 — Bend (e JEV) são gate de verificação no CI, não runtime em produção + +- **Status:** Proposed +- **Data:** 2026-10-18 +- **Escopo:** todos os repos que usam Bend ou JEV +- **Decisor:** Chefe via CAB (aprovação) / Arquiteto (proposta) + +## Contexto + +`patote85/crud-api-bend` reimplementa em Bend 2 o contrato HTTP de `aws-crud-api-lambda-dynamodb` e diz no README que "**não** é um port de Lambda, DynamoDB ou API Gateway". O que o repo tem de fato: + +- **Leis e provas:** `LAWS.bend` + `PROOF.bend` (18 leis: create/get/delete/options, dois items, JSON, persist, envelope da lista, PUT parcial). `AGENTS.md`: "Do not weaken or delete a law to make a proof pass. The human owns LAWS.bend." +- **CI:** `.github/workflows/proof.yml` instala Bend e roda `test.sh` (`bend PROOF.bend` + demo em processo) em push/PR. "Sem TCP no CI." +- **Runtime de brinquedo:** processo único TCP `:8080`, HTTP mínimo, canal tamanho 1, store em `store.json` gravado a cada pedido, sem TLS, sem auth, `price` inteiro, `id` contador. + +JEV não aparece em `crud-api-bend`; entra aqui pela mesma lógica de gate. + +Ou seja: o valor do repo está nas leis verificadas, não no servidor. + +## Decisão + +1. **Bend é gate de CI:** leis do contrato em `LAWS.bend`, provas em `PROOF.bend`, rodadas em todo PR. Prova falhou, PR não passa o CAB. +2. **Bend não roda em produção.** A runtime de produção continua sendo HttpApi + Lambda Python + DynamoDB (ADR-003). O servidor Bend é só demo local. +3. **JEV entra no mesmo papel:** juiz tipado no CI/review, nunca no caminho de request. Chave só via secret-request (`JEV_API_KEY`). +4. **Leis são do humano:** enfraquecer ou apagar lei para passar prova é proibido e vale como ressalva no CAB. + +## Por quê (o que não dá pra ver no código) + +- O servidor Bend não tem o que produção exige: TLS, auth (ADR-002), concorrência real (canal tamanho 1), persistência durável e multi-instância (`store.json` local), observabilidade, nem caminho de deploy gerenciado na AWS. +- A força do Bend é provar propriedades do contrato (tipos dependentes, `match` exaustivo) **antes do merge**. Isso é verificação, e cabe no CI sem custo de cloud. +- Usar como runtime criaria um segundo stack para operar, sem SLA nem suporte da AWS, e trocaria "provar o contrato" por "manter um servidor HTTP feito à mão". + +## Consequências / trade-offs + +- (+) Gate formal barato (GitHub Actions, sem AWS) e contrato com prova, não só teste de exemplo. +- (−) Duas implementações do mesmo contrato (Python e Bend) podem divergir; a lei só prova o modelo Bend, não a Lambda. +- (−) Toolchain instalada via `curl | sh` no CI, sem versão fixada (risco de supply chain e de quebra). Fixar versão/checksum é follow-up. +- (−) Bend 2 é novo: curva de aprendizado e ecossistema pequeno. + +## Princípios do pstack aplicados + +- **Encode Lessons in Structure:** a regra de negócio vira lei verificável em `LAWS.bend`, não prosa. Mudou: o contrato do CRUD está em lei + prova no CI. +- **Prove It Works:** `test.sh` roda prova + demo a cada PR. Mudou: "pronto" = prova verde, não autodeclaração. +- **Type System Discipline:** tipos dependentes tornam estados ilegais irrepresentáveis no modelo. Mudou: `id`/`price` como `Nat`. +- **Subtract Before You Add:** nada de runtime Bend na AWS. Mudou: zero infra nova. + +## Alternativas descartadas + +- **Bend como runtime (container/ECS ou custom runtime Lambda):** ver "Por quê"; custo de operar sem ganho. +- **Só pytest, sem Bend:** perde a prova formal; continua valendo para a Lambda, mas não substitui as leis. +- **JEV no caminho de request:** latência, custo por chamada e dependência externa em produção. + +**Pendente:** fixar a versão do Bend no `proof.yml`; ligar as leis ao contrato da Lambda (ex.: mesmo conjunto de casos em `test_http_contract.py`). Dev, quando o Chefe pedir. diff --git a/docs/adr/README.md b/docs/adr/README.md new file mode 100644 index 0000000..1bca0a1 --- /dev/null +++ b/docs/adr/README.md @@ -0,0 +1,21 @@ +# Índice de ADRs (atualizado 2026-10-18) + +ADRs ficam em `docs/adr/` do repo canônico `patote85/aws-lambda-api-gateway-python`. Só ganha ADR o que vira padrão pra mais de um sistema ou um porquê que não aparece no código. + +## ADRs + +| ADR | Título | Status | Escopo | +|-----|--------|--------|--------| +| [ADR-001](./ADR-001-unificar-exclusao-pix.md) | Unificar exclusão Pix (CDK + Lambda) | Accepted (CAB 2026-09-20) | Exclusão Pix | +| [ADR-002](./ADR-002-auth-cognito-jwt-httpapi.md) | Auth padrão: Cognito JWT authorizer no HttpApi | Proposed | Todos os repos | +| [ADR-003](./ADR-003-httpapi-lambda-cdk-python.md) | HttpApi + Lambda, IaC em CDK Python | Proposed | Todos os repos | +| [ADR-004](./ADR-004-bend-jev-gate-nao-runtime.md) | Bend (e JEV) como gate no CI, não runtime | Proposed | Repos com Bend/JEV | + +## Decisões registradas sem ADR (já estão no código ou são fato consumado) + +| Decisão | Onde está | Link | +|---------|-----------|------| +| `aws-crud-api-lambda-dynamodb`: modelo de dados (tabela `Items`, PK `id`, on-demand) e CI (pytest + moto, `sam validate --lint`, `make check` = lint + mypy + testes + sam validate) | `template.yaml`, `Makefile`, `tests/` | [PR #1 pytest+moto](https://github.com/patote85/aws-crud-api-lambda-dynamodb/pull/1), [PR #2 sam validate](https://github.com/patote85/aws-crud-api-lambda-dynamodb/pull/2), [PR #3 make check](https://github.com/patote85/aws-crud-api-lambda-dynamodb/pull/3) | +| `aws-api-gateway-cdk` arquivado; stack migrado pro canônico (ADR-001) | banner "Arquivado" no README do repo antigo | [aws-api-gateway-cdk PR #1](https://github.com/patote85/aws-api-gateway-cdk/pull/1) (banner), [canônico PR #5](https://github.com/patote85/aws-lambda-api-gateway-python/pull/5) (migração do CDK) | + +Nota: o CI "`make check` + `cdk synth` (sem deploy)" é o [PR #9 do canônico](https://github.com/patote85/aws-lambda-api-gateway-python/pull/9), não do CRUD. O CRUD usa SAM, não CDK (ver ADR-003). From d2cd80e4cff3c7ed7f5f0353e366b919902aa3b3 Mon Sep 17 00:00:00 2001 From: patote85 Date: Sun, 18 Oct 2026 12:48:40 -0300 Subject: [PATCH 2/2] docs(adr): mark ADR-002..004 Accepted (CAB 2026-10-18) --- docs/adr/ADR-002-auth-cognito-jwt-httpapi.md | 2 +- docs/adr/ADR-003-httpapi-lambda-cdk-python.md | 2 +- docs/adr/ADR-004-bend-jev-gate-nao-runtime.md | 2 +- docs/adr/README.md | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/adr/ADR-002-auth-cognito-jwt-httpapi.md b/docs/adr/ADR-002-auth-cognito-jwt-httpapi.md index 25a6eb8..afba7fc 100644 --- a/docs/adr/ADR-002-auth-cognito-jwt-httpapi.md +++ b/docs/adr/ADR-002-auth-cognito-jwt-httpapi.md @@ -1,6 +1,6 @@ # ADR-002 — Auth padrão: Cognito JWT authorizer no HttpApi -- **Status:** Proposed +- **Status:** Accepted (CAB 2026-10-18) - **Data:** 2026-10-18 - **Escopo:** todos os repos serverless do Rodrigo (padrão da squad) - **Decisor:** Chefe via CAB (aprovação) / Arquiteto (proposta) diff --git a/docs/adr/ADR-003-httpapi-lambda-cdk-python.md b/docs/adr/ADR-003-httpapi-lambda-cdk-python.md index 476e012..7afe817 100644 --- a/docs/adr/ADR-003-httpapi-lambda-cdk-python.md +++ b/docs/adr/ADR-003-httpapi-lambda-cdk-python.md @@ -1,6 +1,6 @@ # ADR-003 — Padrão serverless: API Gateway HttpApi + Lambda, IaC em CDK Python -- **Status:** Proposed +- **Status:** Accepted (CAB 2026-10-18) - **Data:** 2026-10-18 - **Escopo:** todos os repos serverless do Rodrigo (padrão da squad) - **Decisor:** Chefe via CAB (aprovação) / Arquiteto (proposta) diff --git a/docs/adr/ADR-004-bend-jev-gate-nao-runtime.md b/docs/adr/ADR-004-bend-jev-gate-nao-runtime.md index 3a656ce..e0b926d 100644 --- a/docs/adr/ADR-004-bend-jev-gate-nao-runtime.md +++ b/docs/adr/ADR-004-bend-jev-gate-nao-runtime.md @@ -1,6 +1,6 @@ # ADR-004 — Bend (e JEV) são gate de verificação no CI, não runtime em produção -- **Status:** Proposed +- **Status:** Accepted (CAB 2026-10-18) - **Data:** 2026-10-18 - **Escopo:** todos os repos que usam Bend ou JEV - **Decisor:** Chefe via CAB (aprovação) / Arquiteto (proposta) diff --git a/docs/adr/README.md b/docs/adr/README.md index 1bca0a1..6031301 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -7,9 +7,9 @@ ADRs ficam em `docs/adr/` do repo canônico `patote85/aws-lambda-api-gateway-pyt | ADR | Título | Status | Escopo | |-----|--------|--------|--------| | [ADR-001](./ADR-001-unificar-exclusao-pix.md) | Unificar exclusão Pix (CDK + Lambda) | Accepted (CAB 2026-09-20) | Exclusão Pix | -| [ADR-002](./ADR-002-auth-cognito-jwt-httpapi.md) | Auth padrão: Cognito JWT authorizer no HttpApi | Proposed | Todos os repos | -| [ADR-003](./ADR-003-httpapi-lambda-cdk-python.md) | HttpApi + Lambda, IaC em CDK Python | Proposed | Todos os repos | -| [ADR-004](./ADR-004-bend-jev-gate-nao-runtime.md) | Bend (e JEV) como gate no CI, não runtime | Proposed | Repos com Bend/JEV | +| [ADR-002](./ADR-002-auth-cognito-jwt-httpapi.md) | Auth padrão: Cognito JWT authorizer no HttpApi | Accepted (CAB 2026-10-18) | Todos os repos | +| [ADR-003](./ADR-003-httpapi-lambda-cdk-python.md) | HttpApi + Lambda, IaC em CDK Python | Accepted (CAB 2026-10-18) | Todos os repos | +| [ADR-004](./ADR-004-bend-jev-gate-nao-runtime.md) | Bend (e JEV) como gate no CI, não runtime | Accepted (CAB 2026-10-18) | Repos com Bend/JEV | ## Decisões registradas sem ADR (já estão no código ou são fato consumado)