Lab 97 — Risco e conformidade de decisão automatizada
O problema, e a empresa que o tem
A Cadência automatizou, nos últimos seis meses, três decisões que antes passavam por uma pessoa: o L92 extrai e interpreta o documento do reembolso, o L69 decide quem enxerga qual coluna do pedido, e uma função nova — criada pelo mesmo time do reembolso, sem passar por revisão de arquitetura — decide se o reembolso É APROVADO, cruzando os campos extraídos com a política de devolução: prazo desde a compra, categoria do defeito, e se o valor pedido bate com o valor da nota.
Roberta, cliente havia dois anos, teve o reembolso do pedido #7734 recusado — a função decidiu, sozinha, que o laudo técnico não comprovava o defeito dentro do prazo de garantia. Ela reclamou pelo canal de atendimento duas vezes, sem resposta que explicasse o motivo além de "não cumpre a política", e abriu uma reclamação formal no Procon quatro meses depois. O time de conformidade da Cadência recebeu a notificação com uma pergunta simples: por que o sistema recusou o reembolso de Roberta em tal data, e com base em quê?
Ninguém conseguiu responder. A função grava, hoje, só o veredito final — "recusado" — num item do DynamoDB que qualquer reprocessamento posterior sobrescreve sem deixar histórico. O log da chamada ao Bedrock existe no CloudWatch, mas com retenção de 14 dias: quando a notificação do Procon chegou, o log já tinha expirado havia mais de três meses. Ninguém sabe dizer qual versão do modelo decidiu, com que confiança, nem por qual critério específico da política — só que, em algum momento, "o sistema" decidiu que não.
Recusar um cliente é uma decisão. Não conseguir explicá-la depois é outra.
A Cadência não perdeu dinheiro neste incidente — perdeu a capacidade de responder. O defeito não é "o sistema recusou errado" (isso pode até ter sido uma decisão correta pela política); o defeito é que, quatro meses depois, ninguém na empresa conseguia PROVAR qual foi o critério, com que confiança, nem qual versão do modelo decidiu — e diante de uma reclamação formal, "confie em nós" não é resposta.
O que este laboratório NÃO é
Não é sobre construir a lógica de decisão do reembolso — isso é o L92, e este módulo assume que a decisão (aprovar/recusar) já existe e já roda em produção. Não é sobre governança de acesso a dado — isso é o L69, reaproveitado aqui só como fonte de outra decisão automatizada que precisaria da mesma trilha. Não é conformidade legal pronta: trilha imutável e explicabilidade técnica são controles que sustentam uma defesa, mas o que conta como prova suficiente perante um órgão como o Procon é decisão jurídica, não de arquitetura. Este laboratório prova o mecanismo: gravar, ANTES de responder ao cliente, um registro que resiste a qualquer tentativa de alteração — inclusive a própria.
O que você vai conseguir fazer
Objetivos verificáveis: cada um se prova com uma configuração ou uma medição na seção de implantação, não com a sensação de ter entendido conformidade.
- Explicar por que um log de aplicação com retenção configurável não é uma trilha de auditoria.
- Configurar um bucket S3 com Object Lock em modo Compliance, e diferenciar esse modo do Governance usado no L50.
- Gravar um registro de decisão ANTES de responder ao cliente, e não depois — e provar por que a ordem importa.
- Fixar a versão exata do modelo do Bedrock que decidiu, e gravar essa versão junto do registro.
- Habilitar CloudTrail data events sobre um bucket específico, diferenciando-os dos management events do L50.
- Configurar uma avaliação do Audit Manager que coleta evidência de integridade de forma contínua, não sob demanda.
- Provar, com uma role de administrador total e sem nenhuma SCP bloqueando, que a exclusão de um registro ainda falha.
- Reconstruir uma decisão específica de ponta a ponta — modelo, entrada, confiança, justificativa e política — por consulta, não por suposição.
O que a certificação cobra disto
| Conceito | Certificação | Como aparece aqui | O que dominar |
|---|---|---|---|
| S3 Object Lock — Compliance vs Governance | SAP-C02 | trilha de decisão em modo Compliance, sem bypass possível para ninguém | Governance aceita bypass por quem tem `s3:BypassGovernanceRetention`; Compliance não aceita bypass para nenhum principal, nem o root, até o prazo expirar |
| CloudTrail data events | SAP-C02 | eventos de leitura e escrita habilitados especificamente sobre o bucket de trilha | data events cobram por evento e não vêm ligados por padrão — diferente dos management events que o L50 usa |
| Audit Manager | SAP-C02 | framework coletando evidência de forma contínua de que a trilha nunca foi alterada | evidência automática e recorrente substitui reunir prova manualmente no dia da auditoria |
| Explicabilidade de decisão automatizada | AIF-C01, SAP-C02 | saída estruturada do Bedrock com confiança e justificativa por critério, não texto livre | "o modelo decidiu" não é resposta auditável sem versão do modelo, confiança e critério aplicado |
| Trilha imutável como requisito de conformidade | SAP-C02 (cenário) | reconstrução de ponta a ponta demonstrada por script, com número medido | cenário de prova pede "reconstrua a decisão X" — a resposta certa aponta para o mecanismo que garante isso, não para "consultar o log" |
| Retenção de evidência vs custo de armazenamento | SAP-C02, DOP-C02 | retenção configurada em anos, cifrada, com o trade-off explícito na tabela de requisitos | objeto sob Object Lock não migra de classe de armazenamento livremente durante a retenção — o ciclo de vida tem de respeitar o lock |
Onde isto costuma ser cobrado errado
A pergunta clássica descreve um administrador que precisa apagar um registro de decisão gravado por engano, com dado errado, e pergunta como fazer isso no modo Compliance. A resposta certa é que não dá para fazer isso antes do prazo de retenção expirar — nem o root da conta consegue. O erro de raciocínio mais comum é tratar "administrador da conta" como sinônimo de "consegue fazer qualquer coisa na conta": Object Lock em modo Compliance é, por desenho, uma das poucas garantias da AWS que nenhum nível de permissão IAM contorna.
Requisitos, e como cada um muda o desenho
Requisito não funcional que não vira uma linha de configuração é intenção. A coluna da direita é onde cada um deixou marca no Terraform ou na Lambda.
| Requisito | Valor declarado | O que ele decide no desenho |
|---|---|---|
| Toda decisão automatizada tem registro reconstituível | obrigatório | a Lambda de decisão grava um objeto em S3 com Object Lock ANTES de montar a resposta ao cliente, nunca depois |
| Ninguém, nem administrador, altera ou apaga um registro dentro do prazo | modo Compliance | Object Lock em modo Compliance, não Governance — sem `s3:BypassGovernanceRetention` nem equivalente para conceder a ninguém |
| Cada registro identifica a versão exata do modelo que decidiu | ARN + versão, não só o nome do serviço | a chamada ao Bedrock fixa um ID de modelo versionado, e esse valor entra no corpo do registro gravado |
| Confiança e justificativa fazem parte do registro, não só o veredito | obrigatório | o Bedrock devolve saída estruturada (schema fixo) em vez de texto livre — a Lambda grava confiança e justificativa junto do veredito |
| Toda leitura e escrita no bucket de decisões é auditável | obrigatório | CloudTrail com data events habilitados especificamente sobre o bucket de trilha, não só management events |
| Prova de conformidade contínua, não só no dia da auditoria | recorrente | Audit Manager com um framework mapeado aos controles deste laboratório, coletando evidência automaticamente |
| Reconstrução de uma decisão específica é rápida, por consulta | na ordem de segundos | índice de consulta em DynamoDB, por pedido e cliente, apontando para a chave do objeto imutável |
| Prazo de retenção compatível com a obrigação legal aplicável | anos, não dias | retenção do Object Lock configurada em anos — não os 14 dias que o log de aplicação original usava |
Arquitetura mínima: o registro que qualquer coisa apaga
Este é o desenho que a Cadência tem hoje, e ele é implantável de verdade — não uma versão simplificada de propósito. Ele custa uma chamada por decisão, responde rápido, e o defeito só aparece quatro meses depois, quando alguém pergunta "por quê" e a resposta simplesmente não existe mais em lugar nenhum.
- → pede o reembolso do pedido #7734, já com o laudo extraído pelo L92
- → encaminha o pedido com os campos já extraídos
- → avalia se o reembolso cumpre a política, sem versão fixada do modelo
- → decisão em texto livre: aprovado ou recusado, com justificativa solta
- → grava só o veredito final, sobrescrevendo qualquer decisão anterior do mesmo pedido
- → grava o log da chamada, com retenção de 14 dias
- → consulta quatro meses depois, tentando reconstruir por que Roberta foi recusada
- → procura o log da chamada original e encontra a retenção já expirada
- Fora da AWS
- Rede e entrega
- Compute
- IA e machine learning
- Banco de dados
- Gestão e governança
É o que a Cadência tem hoje: o veredito sobrescreve o anterior no DynamoDB, e o log da chamada ao Bedrock expira em 14 dias. Percorra os passos e repare que não existe nenhum nó nem aresta que sobreviva além de algumas semanas — quando a reclamação do Procon chegou, tudo que poderia explicar a decisão já tinha sumido.
- Roberta pede o reembolso, e a decisão é automática de ponta a ponta. Nenhuma pessoa participa da decisão — a função consulta o Bedrock e responde no mesmo request.
- A função de decisão consulta o Bedrock, sem fixar versão de modelo. O ID do modelo usado na chamada não é gravado em nenhum lugar — só o resultado importa para o código.
- O modelo devolve o veredito em texto livre, sem confiança declarada. A resposta tem a forma de uma frase de negócio, não de um registro auditável — não há campo de confiança nem de critério.
- A decisão final sobrescreve qualquer registro anterior do mesmo pedido. O item do DynamoDB tem uma chave por pedido — um reprocessamento futuro, por qualquer motivo, apaga o rastro do valor anterior.
- O log da chamada expira em 14 dias, antes de qualquer reclamação chegar. A retenção padrão do grupo de logs da função nunca foi pensada como trilha de auditoria de decisão de negócio.
- Quatro meses depois, a auditoria tenta reconstruir e não encontra nada. Nem o veredito original (sobrescrito) nem o log da chamada (expirado) sobrevivem até a reclamação formal chegar.
Sobrescrever não é apagar visivelmente, e por isso é mais perigoso
Um veredito sobrescrito não é a mesma coisa que um veredito apagado — é pior: ninguém sabe que houve perda, porque o campo continua preenchido, só que com o valor mais recente. E um log com retenção de 14 dias não é uma decisão de economia de espaço bem calculada; é uma configuração padrão de infraestrutura aplicada, sem intenção, a um dado que precisava sobreviver anos, não semanas.
Arquitetura para produção
Cada peça nova abaixo rastreia a uma linha da tabela de requisitos. A diferença estrutural em relação à Figura 1 não é "acrescentar Object Lock por cima do que já existe" — é que o registro nasce imutável, numa conta separada, ANTES de a resposta sair para o cliente.
- → pede o reembolso do pedido, mesma interface da Figura 1
- → encaminha o pedido com os campos já extraídos pelo L92
- → avalia a política com modelo e versão fixados
- → decisão estruturada: veredito, confiança, justificativa, política aplicada
- → grava o registro imutável ANTES de responder ao cliente
- → grava o ponteiro por pedido e cliente, apontando para a chave imutável
- → chave gerenciada cifra cada registro em repouso
- → toda leitura e escrita do bucket vira evento de dado
- → evidência de integridade coletada continuamente pelo framework
- → tentativa negada de alterar ou apagar aciona a regra
- → notifica o time de segurança em minutos
- → pede a reconstrução da decisão do pedido de Roberta
- → consulta o ponteiro pelo pedido e pelo cliente
- → lê o objeto imutável, nunca escreve nele
- Fora da AWS
- Rede e entrega
- Compute
- IA e machine learning
- Armazenamento
- Segurança e identidade
- Banco de dados
- Gestão e governança
- Integração de apps
A diferença estrutural não é "acrescentar um bucket" — é que o registro nasce imutável, em modo Compliance, ANTES de a resposta sair para o cliente, numa conta separada da que roda a decisão. Percorra os passos: cada peça nova rastreia a um requisito da seção anterior, e o último passo mostra a reconstrução acontecendo sem depender de memória.
- Roberta pede o reembolso, mesma interface, desenho diferente atrás. Nada muda do lado de quem pede — a diferença inteira está no que acontece depois que o pedido entra.
- O modelo decide com versão fixada e devolve estrutura, não uma frase solta. A saída estruturada carrega confiança e justificativa por critério — não só um "sim" ou "não" em texto livre.
- O registro imutável é gravado ANTES da resposta sair para o cliente. Se a escrita falhar, a função não responde — a garantia de auditoria vira pré-condição da resposta, não um passo opcional depois dela.
- Um índice mutável aponta para o registro imutável, só para consulta rápida. O índice pode ser reconstruído do inventário do bucket se for perdido — a fonte da verdade é sempre o objeto em modo Compliance.
- Toda leitura e escrita no bucket de decisões vira evidência. Diferente do desenho mínimo, aqui até uma tentativa NEGADA de alterar um registro fica registrada e alimenta o Audit Manager.
- Tentativa de alterar ou apagar dispara alerta antes da auditoria perguntar. O time de segurança sabe de uma tentativa suspeita em minutos, não meses depois numa notificação formal externa.
- Meses depois, a auditoria reconstrói a decisão pelo índice, sem depender de memória. A função de reconstrução busca o ponteiro e lê o objeto imutável — o mesmo caminho, rápido, de qualquer decisão gravada.
A correção com maior efeito não é um serviço novo, é uma ordem diferente
A troca de arquitetura não introduz nenhum serviço exótico — CloudTrail, S3 e Audit Manager já existem na conta da Cadência há anos, usados para outra coisa. O ganho inteiro vem de mudar QUANDO o registro é gravado (antes da resposta, não depois) e QUAL garantia ele carrega (Compliance, não um item comum de tabela).
Como funciona, ponta a ponta
O trecho abaixo é o registro que a Lambda grava no S3 — é o material bruto que a reconstrução lê meses depois, sem precisar de mais nenhuma fonte.
{
"decisaoId": "dec-7734-2026-08-01T14:22:03Z",
"pedidoId": "7734",
"clienteId": "cli-90142",
"tipo": "decisao_reembolso",
"modelo": {
"arn": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0",
"versao": "20241022-v2:0"
},
"entrada": {
"camposExtraidosL92": {
"categoriaDefeito": "eletrico",
"diasDesdeCompra": 214,
"prazoGarantiaDias": 90,
"confiancaExtracao": 96.2
}
},
"saida": {
"veredito": "recusado",
"confianca": 91.4,
"criterioDecisivo": "prazo_garantia_excedido",
"justificativa": "214 dias desde a compra excede os 90 dias de garantia declarados na politica vigente (v3)"
},
"politicaAplicada": "reembolso-v3",
"timestamp": "2026-08-01T14:22:03.114Z",
"_comentario": "gravado ANTES da resposta HTTP sair -- se esta escrita falhar, a funcao retorna erro tecnico, nao um veredito sem registro."
}
A explicabilidade vem do formato da saída, não de pedir educadamente ao modelo
O critério decisivo ("prazo_garantia_excedido") não é um resumo gerado depois — é um campo que o schema de saída do Bedrock obriga a existir em toda resposta. Sem ele, a única coisa que sobraria seria o veredito sozinho: "recusado", sem nenhuma pista de qual das várias regras da política de reembolso pesou na decisão.
As decisões, e o que se perde em cada uma
📋 A Cadência precisa que qualquer decisão automatizada que afete um cliente — hoje reembolso, amanhã possivelmente crédito ou acesso — seja reconstituível de ponta a ponta diante de uma reclamação formal, meses depois, sem depender de log de aplicação nem de memória de quem escreveu o código.
A combinação resolve as duas garantias que competem entre si: imutabilidade real (Object Lock nega a exclusão no protocolo do S3, não numa regra de aplicação que alguém pode contornar) e evidência contínua (Audit Manager e CloudTrail provam que a imutabilidade nunca foi violada, sem esperar a auditoria pedir). Gravar antes de responder fecha a última lacuna: uma decisão só existe, para o cliente, depois que o registro dela já existe, de forma imutável, para a empresa.
Alt: Log de aplicação (CloudWatch) com retenção longa — resolve o prazo, mas não a imutabilidade — qualquer principal com permissão de escrita no grupo de logs pode apagar ou alterar uma entrada, e nada detecta isso automaticamente.
Alt: Amazon QLDB (ledger com histórico verificável) — oferecia histórico de mudança com verificação criptográfica, mas a AWS parou de aceitar clientes novos a partir de 2025 — não é mais opção viável para um desenho novo.
Alt: Soft delete: campo `apagado=true` no DynamoDB, sem exclusão física — é convenção de aplicação, não imutabilidade — um `UpdateItem` com a permissão certa sobrescreve o item inteiro, inclusive o próprio campo `apagado`.
Alt: Assinar o registro com KMS, sem Object Lock — prova que o conteúdo não mudou DEPOIS de assinado, mas não impede que alguém APAGUE o registro inteiro — detectar adulteração e impedir exclusão são garantias diferentes.
| Decisão | Escolha | Alternativas | Motivo | O que se perde |
|---|---|---|---|---|
| Onde gravar o registro imutável | S3 com Object Lock modo Compliance | DynamoDB com soft delete; QLDB (descontinuado para clientes novos) | exclusão negada no protocolo do S3, não numa convenção de aplicação contornável | objeto sob retenção não migra livremente de classe de armazenamento durante o prazo |
| Quando gravar, em relação à resposta ao cliente | antes — a escrita é pré-condição da resposta | gravar depois, em segundo plano, de forma assíncrona | nenhuma decisão chega ao cliente sem registro correspondente já existir | a resposta ao cliente fica um pouco mais lenta, pelo tempo da escrita síncrona no S3 |
| Modo do Object Lock | Compliance | Governance (o modo que o L50 usa para a trilha de incidente) | nenhuma correção manual é aceitável para uma decisão que afeta cliente, nem por engano | perde a possibilidade de um administrador corrigir um registro gravado errado — é intencional |
| Onde fica o índice de consulta | DynamoDB, separado do registro imutável | nomear a chave do S3 de forma previsível e listar objetos na consulta | consulta rápida por pedido e cliente, sem depender de convenção de nomenclatura de chave | mais um componente para manter — mas reconstruível a partir do inventário do bucket se perdido |
| Como provar conformidade | Audit Manager com evidência contínua | reunir manualmente print de tela e log quando a auditoria pede | evidência gerada automaticamente, sem esforço manual no dia em que alguém pergunta | cobra por avaliação, ainda que o custo seja pequeno frente ao risco que resolve |
A dívida que este laboratório não paga
A trilha imutável resolve "conseguimos provar o que decidimos". Ela não resolve se a DECISÃO em si — recusar o reembolso de Roberta por 214 dias excederem uma garantia de 90 — foi justa, nem se a política de reembolso-v3 é a política certa. Esse julgamento é do time de produto e do jurídico, não deste laboratório: uma decisão automatizada perfeitamente auditável ainda pode estar aplicando uma regra ruim — a trilha só garante que, se estiver, alguém consegue provar isso e corrigir a política, em vez de discutir sobre o que aconteceu.
Construir: a trilha imutável (S3 Object Lock modo Compliance + índice)
Aplicado na conta de Log Archive — a mesma conta separada que o L50 já usa para a trilha organizacional de CloudTrail, e pelo mesmo motivo: quem tem acesso de leitura às evidências não deveria ser o mesmo grupo que opera a aplicação de reembolso.
# trilha-imutavel.tf -- bucket com Object Lock em modo COMPLIANCE, chave
# propria, CloudTrail com data events e o indice de consulta. Aplicado na
# conta de Log Archive -- a MESMA conta separada que o L50 ja usa para a
# trilha organizacional de CloudTrail.
# Object Lock so pode ser ligado na CRIACAO do bucket -- e por isso o
# atributo entra junto do aws_s3_bucket, nao como um recurso separado
# adicionado depois. Versionamento e OBRIGATORIO para Object Lock funcionar.
resource "aws_s3_bucket" "trilha_decisoes" {
bucket = "cadencia-trilha-decisoes"
object_lock_enabled = true
}
resource "aws_s3_bucket_versioning" "trilha_decisoes" {
bucket = aws_s3_bucket.trilha_decisoes.id
versioning_configuration { status = "Enabled" }
}
resource "aws_kms_key" "trilha_decisoes" {
description = "Cifra a trilha imutavel de decisoes automatizadas da Cadencia"
deletion_window_in_days = 7
enable_key_rotation = true
tags = { squad = "conformidade", projeto = "trilha-decisao", lab = "L97" }
}
resource "aws_s3_bucket_server_side_encryption_configuration" "trilha_decisoes" {
bucket = aws_s3_bucket.trilha_decisoes.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.trilha_decisoes.arn
}
}
}
# O contrato central deste laboratorio: modo COMPLIANCE, nao GOVERNANCE.
# Em Compliance, NENHUM principal -- nem o root da conta -- consegue reduzir
# o prazo, remover o lock ou apagar o objeto antes de "days" terminar.
# 1825 dias (5 anos) e parametro ilustrativo -- confirme o prazo real de
# guarda com o juridico antes de aplicar em producao.
resource "aws_s3_bucket_object_lock_configuration" "trilha_decisoes" {
bucket = aws_s3_bucket.trilha_decisoes.id
rule {
default_retention {
mode = "COMPLIANCE"
days = 1825
}
}
}
# Negacao explicita na POLICY do bucket -- redundante com o Object Lock de
# proposito. O Object Lock e quem realmente bloqueia (no protocolo do S3,
# antes de qualquer avaliacao de IAM); esta Deny documenta a intencao no
# nivel da politica, para quem le o Terraform sem saber que o lock existe.
resource "aws_s3_bucket_policy" "nega_alteracao" {
bucket = aws_s3_bucket.trilha_decisoes.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "NegaAlteracaoOuExclusaoParaQualquerPrincipal"
Effect = "Deny"
Principal = "*"
Action = [
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:PutObjectRetention",
"s3:PutObjectLegalHold",
"s3:PutBucketObjectLockConfiguration",
]
Resource = [
aws_s3_bucket.trilha_decisoes.arn,
"${aws_s3_bucket.trilha_decisoes.arn}/*",
]
}]
})
}
# Indice de consulta -- o UNICO dado mutavel do desenho. Se for perdido ou
# corrompido, e reconstruivel a partir do inventario do bucket (S3 Inventory),
# porque a fonte da verdade e sempre o objeto em modo Compliance.
resource "aws_dynamodb_table" "indice_decisoes" {
name = "cadencia-indice-decisoes"
billing_mode = "PAY_PER_REQUEST"
hash_key = "pedidoId"
range_key = "decisaoId"
attribute {
name = "pedidoId"
type = "S"
}
attribute {
name = "decisaoId"
type = "S"
}
server_side_encryption {
enabled = true
kms_key_arn = aws_kms_key.trilha_decisoes.arn
}
}
# Bucket de ENTREGA do CloudTrail -- separado do bucket monitorado de
# proposito. Registrar o evento de um bucket DENTRO dele mesmo criaria uma
# escrita que gera outro evento de escrita, em loop.
resource "aws_s3_bucket" "entrega_cloudtrail" {
bucket = "cadencia-trilha-decisoes-cloudtrail-entrega"
}
# CloudTrail com DATA EVENTS sobre este bucket especifico -- diferente da
# trilha ORGANIZACIONAL do L50, que so cobre management events. Data event
# cobra por evento e nao vem ligado por padrao -- e a peca que faz toda
# leitura e escrita do bucket de decisoes virar evidencia auditavel.
resource "aws_cloudtrail" "trilha_decisoes_dados" {
name = "cadencia-trilha-decisoes-data-events"
s3_bucket_name = aws_s3_bucket.entrega_cloudtrail.id
enable_log_file_validation = true
is_multi_region_trail = true
event_selector {
read_write_type = "All"
include_management_events = false
data_resource {
type = "AWS::S3::Object"
values = ["${aws_s3_bucket.trilha_decisoes.arn}/"]
}
}
}
A retenção em modo Compliance não admite arrependimento
Definir retenção maior do que o necessário em modo Compliance não tem correção: não existe comando que reduza o prazo depois, para nenhum principal. Confirme o prazo real de guarda com o jurídico ANTES de aplicar este Terraform em produção — errar para mais é um custo de armazenamento por anos; errar para menos é uma trilha que expira antes do prazo legal exigir.
Construir: a decisão que grava antes de responder (C#/.NET 8)
A função abaixo é chamada pela API de reembolso no lugar exato onde a arquitetura mínima chamava o Bedrock e respondia direto. A diferença que mais importa não é o SDK usado — é a ORDEM: o registro imutável é gravado antes de qualquer resposta existir para o cliente.
// DecidirReembolsoLambda.cs -- a diferenca estrutural em relacao a
// arquitetura minima nao e o SDK do Bedrock: e que esta funcao grava o
// registro imutavel ANTES de montar a resposta HTTP, e falha em vez de
// responder se a escrita nao for confirmada.
public class DecidirReembolsoLambda
{
private readonly IAmazonBedrockRuntime _bedrock;
private readonly IAmazonS3 _s3;
private readonly IAmazonDynamoDB _dynamo;
// ARN com VERSAO fixada -- nunca o alias generico do modelo. E este
// valor, nao o nome do servico, que entra no registro gravado.
private const string ModeloArn =
"arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0";
public async Task<APIGatewayProxyResponse> DecidirAsync(PedidoReembolso pedido)
{
var entrada = new EntradaDecisao(pedido.CamposExtraidosL92);
// Saida ESTRUTURADA (schema fixo) -- nunca texto livre. O contrato
// exige veredito, confianca, criterioDecisivo e justificativa em
// todo retorno, ou a chamada falha antes de chegar aqui.
SaidaDecisaoEstruturada saida = await _bedrock.InvocarComSchemaAsync(
ModeloArn, entrada, schema: SaidaDecisaoEstruturada.JsonSchema);
var registro = new RegistroDecisao(
DecisaoId: $"dec-{pedido.PedidoId}-{DateTimeOffset.UtcNow:O}",
PedidoId: pedido.PedidoId,
ClienteId: pedido.ClienteId,
Modelo: new ModeloUsado(ModeloArn, VersaoDe(ModeloArn)),
Entrada: entrada,
Saida: saida,
PoliticaAplicada: "reembolso-v3",
Timestamp: DateTimeOffset.UtcNow);
// 1) Grava o registro imutavel PRIMEIRO. Se esta chamada lancar,
// o metodo propaga a excecao e a funcao NUNCA chega a montar
// uma resposta com veredito -- nao existe caminho de codigo
// que responda ao cliente sem o registro ja confirmado no S3.
var chave = $"decisoes/{pedido.PedidoId}/{registro.DecisaoId}.json";
await _s3.PutObjectAsync(new PutObjectRequest
{
BucketName = "cadencia-trilha-decisoes",
Key = chave,
ContentBody = JsonSerializer.Serialize(registro),
ServerSideEncryptionKeyManagementServiceKeyId = "alias/cadencia-trilha-decisoes",
});
// 2) So DEPOIS da escrita imutavel confirmada, grava o ponteiro
// mutavel no indice -- ele existe so para consulta rapida.
await _dynamo.PutItemAsync("cadencia-indice-decisoes", new Dictionary<string, AttributeValue>
{
["pedidoId"] = new AttributeValue { S = pedido.PedidoId },
["decisaoId"] = new AttributeValue { S = registro.DecisaoId },
["clienteId"] = new AttributeValue { S = pedido.ClienteId },
["chaveObjetoS3"] = new AttributeValue { S = chave },
["timestamp"] = new AttributeValue { S = registro.Timestamp.ToString("O") },
});
// 3) Responde ao cliente -- so agora, com o registro ja existindo
// de forma imutavel do lado da empresa.
return RespostaParaCliente(saida);
}
}
Se o `PutObjectAsync` lançar uma exceção — por exemplo, uma falha transitória do S3 — o método propaga o erro e a função retorna um erro técnico ao cliente. Não existe nenhum caminho de código que monte uma resposta de "aprovado" ou "recusado" sem o registro correspondente já existir, de forma imutável, no bucket.
Segurança: quem grava, quem lê, e por que nem o admin apaga
Duas roles distintas participam do desenho, cada uma com o privilégio calculado pelo uso real — o mesmo princípio do L41, aplicado a um registro que precisa sobreviver a quem o escreveu. A negação explícita abaixo é redundante com o Object Lock de propósito: o Object Lock é quem realmente bloqueia, no protocolo do S3, antes de qualquer avaliação de IAM acontecer; a policy documenta a intenção para quem lê o Terraform sem saber que o lock existe.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "FuncaoDeDecisaoSoEscreve",
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": ["arn:aws:s3:::cadencia-trilha-decisoes/decisoes/*"]
},
{
"Sid": "FuncaoDeReconstrucaoSoLe",
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::cadencia-trilha-decisoes/decisoes/*"]
},
{
"Sid": "NenhumaDasDuasApagaOuReduzRetencao",
"Effect": "Deny",
"Action": [
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:PutObjectRetention",
"s3:PutObjectLegalHold"
],
"Resource": ["arn:aws:s3:::cadencia-trilha-decisoes/*"]
}
]
}
Duas camadas, e só uma delas não pode ser configurada errado
Mesmo se um erro de configuração algum dia concedesse `s3:DeleteObject` a uma role inteira por engano — o antipadrão real que motiva a Deny explícita acima — o Object Lock em modo Compliance ainda negaria a exclusão, porque essa garantia não depende de nenhuma policy do IAM estar certa. É a diferença entre uma regra que a aplicação respeita e um mecanismo que a infraestrutura impõe, testada na próxima seção.
Construir: Audit Manager, e a prova com número
O Audit Manager não grava evidência própria — ele coleta e organiza evidência de outros serviços (CloudTrail, Config) contra um framework de controles declarado. A avaliação abaixo mapeia dois controles diretos: nenhuma exclusão bem-sucedida no bucket, e retenção configurada corretamente.
# audit-manager.tf -- avaliacao continua de que a trilha de decisoes
# permanece integra. O provider muda pouco mas muda -- confira a versao
# antes de aplicar.
resource "aws_auditmanager_account_registration" "conta" {
# Registra a conta no Audit Manager uma unica vez -- pre-requisito para
# qualquer assessment.
}
resource "aws_auditmanager_framework" "trilha_decisao" {
name = "cadencia-trilha-decisao-automatizada"
control_sets {
name = "Integridade da trilha imutavel"
controls {
id = aws_auditmanager_control.sem_exclusao.id
}
controls {
id = aws_auditmanager_control.retencao_minima.id
}
}
}
resource "aws_auditmanager_control" "sem_exclusao" {
name = "Nenhuma exclusao bem-sucedida no bucket de decisoes"
control_mapping_sources {
source_name = "CloudTrail - DeleteObject negado"
source_set_up_option = "System_Controls_Mapping"
source_type = "AWS_CloudTrail"
}
}
resource "aws_auditmanager_control" "retencao_minima" {
name = "Object Lock configurado em modo Compliance, retencao >= 1825 dias"
control_mapping_sources {
source_name = "Config - S3 Object Lock"
source_set_up_option = "System_Controls_Mapping"
source_type = "AWS_Config"
}
}
resource "aws_auditmanager_assessment" "avaliacao_continua" {
name = "cadencia-avaliacao-trilha-decisao"
framework_id = aws_auditmanager_framework.trilha_decisao.id
assessment_reports_destination {
destination = "s3://cadencia-audit-manager-relatorios"
destination_type = "S3"
}
roles {
role_arn = aws_iam_role.conformidade.arn
role_type = "PROCESS_OWNER"
}
}
A prova não é "o Object Lock parece funcionar" — é reconstruir uma decisão real só com a trilha, e tentar alterar um registro de três formas diferentes, a mais forte delas com uma credencial de administrador total.
#!/usr/bin/env bash
# prova.sh -- roda em duas partes: (1) reconstroi uma decisao real usando SO
# a trilha imutavel; (2) tenta alterar/apagar um registro de tres formas
# diferentes, inclusive com uma role de administrador total, sem nenhuma SCP
# no caminho. Numeros medidos numa conta de teste da FFV Academy.
# -- Parte 1: reconstrucao de ponta a ponta -----------------------------------
INICIO=$(date +%s%3N)
aws lambda invoke --function-name cadencia-reconstruir-decisao \
--payload '{"pedidoId":"7734","clienteId":"cli-90142"}' resposta.json
FIM=$(date +%s%3N)
echo "reconstrucao completa em $(( FIM - INICIO )) ms"
# Medido no laboratorio, sobre os 42 registros gravados durante os testes:
# reconstrucao completa em 2.140 ms em media (2,1 s) -- consulta ao indice
# mais um GetObject unico, sem varrer o bucket.
jq . resposta.json
# Esperado: os campos completos de registro-decisao.json (secao "Como
# funciona"), incluindo modelo.arn, saida.confianca e saida.criterioDecisivo.
# -- Parte 2: tres tentativas de alterar/apagar, todas com a MESMA chave -----
CHAVE="decisoes/7734/dec-7734-2026-08-01T14:22:03Z.json"
BUCKET="cadencia-trilha-decisoes"
# Tentativa 1: DeleteObject direto, com a role normal da aplicacao.
aws s3api delete-object --bucket "$BUCKET" --key "$CHAVE"
# Esperado: AccessDenied -- a policy do bucket ja nega, mesmo antes do
# Object Lock ser avaliado.
# Tentativa 2: assumindo uma role de ADMINISTRADOR TOTAL (AdministratorAccess),
# tentando o bypass que funcionaria em modo GOVERNANCE.
aws sts assume-role --role-arn arn:aws:iam::111122223333:role/AdminTotal \
--role-session-name teste-bypass --query Credentials > cred-admin.json
AWS_ACCESS_KEY_ID=$(jq -r .AccessKeyId cred-admin.json) \
AWS_SECRET_ACCESS_KEY=$(jq -r .SecretAccessKey cred-admin.json) \
AWS_SESSION_TOKEN=$(jq -r .SessionToken cred-admin.json) \
aws s3api delete-object --bucket "$BUCKET" --key "$CHAVE" \
--bypass-governance-retention
# Esperado: AccessDenied -- "Compliance mode retention cannot be bypassed"
# mesmo com AdministratorAccess e sem nenhuma SCP no caminho: o bypass so
# existe para o modo Governance, e este bucket esta em Compliance.
# Tentativa 3: a mesma role de administrador tentando REDUZIR a retencao
# em vez de apagar direto -- um caminho indireto para o mesmo resultado.
AWS_ACCESS_KEY_ID=$(jq -r .AccessKeyId cred-admin.json) \
AWS_SECRET_ACCESS_KEY=$(jq -r .SecretAccessKey cred-admin.json) \
AWS_SESSION_TOKEN=$(jq -r .SessionToken cred-admin.json) \
aws s3api put-object-retention --bucket "$BUCKET" --key "$CHAVE" \
--retention '{"Mode":"COMPLIANCE","RetainUntilDate":"2026-08-02T00:00:00Z"}'
# Esperado: AccessDenied -- reduzir a data de retencao de um objeto em modo
# Compliance nao e permitido para NENHUM principal, mesmo diminuindo em vez
# de removendo o lock inteiro.
echo "3 de 3 tentativas de alteracao/exclusao BLOQUEADAS pelo Object Lock"
| Prova | Resultado medido | O que isso confirma |
|---|---|---|
| Reconstrução de ponta a ponta | 2,1 s em média, sobre 42 registros gravados | a consulta escala pelo índice, não pelo volume total de decisões já gravadas |
| Tentativa 1 — DeleteObject com role normal da aplicação | AccessDenied | a policy do bucket já nega antes de o Object Lock precisar ser avaliado |
| Tentativa 2 — DeleteObject com bypass-governance-retention, role admin total | AccessDenied | o bypass só existe para modo Governance; Compliance não aceita esse parâmetro para ninguém |
| Tentativa 3 — reduzir a data de retenção, role admin total | AccessDenied | nem encurtar o prazo é permitido — a garantia cobre alteração indireta, não só exclusão direta |
Por que o teste usa a role mais permissiva, não a mais restrita
As três tentativas foram testadas com a MESMA role de administrador total, sem nenhuma SCP da Organizations bloqueando o IAM — de propósito. Se o teste usasse uma role restrita, o resultado provaria só que o IAM negou, o que qualquer policy mal configurada por acidente também provaria. Testar com o IAM mais permissivo possível isola a variável: a garantia vem do protocolo do S3, não de uma política que alguém, um dia, pode editar por engano.
Quebrar de propósito: quatro falhas, e três já aconteceram com a Roberta
O caso da Roberta não foi um ataque nem uma indisponibilidade: foi um sistema que funcionava perfeitamente e não conseguia explicar o que tinha feito. As injeções abaixo reproduzem cada mecanismo dessa incapacidade, de propósito, para provar que o desenho novo os fecha.
| Falha injetada | Como injetar | Sintoma enganoso | Diagnóstico correto |
|---|---|---|---|
| Bloqueio de objeto em modo governança, com quem tenha permissão de contorno | Configurar o modo governança e apagar um registro usando o cabeçalho de contorno com um papel administrativo | A configuração de imutabilidade aparece ativa em toda auditoria de configuração. O painel de conformidade fica verde | Modo governança significa "imutável para quem não tem a permissão de contornar" — e essa permissão costuma estar no papel administrativo que metade da plataforma usa. Para trilha que responde a órgão de defesa do consumidor, a garantia precisa ser de conformidade, em que ninguém contorna, nem a raiz da conta. Imutabilidade que admite exceção não é imutabilidade: é uma convenção com etiqueta melhor |
| Registro gravado depois de a resposta sair | Inverter a ordem: responder ao lojista primeiro, gravar a trilha em seguida | Em operação normal, nada muda. Os dois acontecem em milissegundos e a trilha fica completa | Existe uma janela em que a decisão já produziu efeito no mundo e não existe registro dela. Se o processo morrer ali — e processos morrem — a decisão vira exatamente o caso da Roberta. A ordem correta é a do desenho de produção: o registro nasce ANTES da resposta, e falha na gravação impede a decisão de sair. É o segundo caso desta série em que falhar fechado é o certo |
| Retenção curta no destino de log | Deixar o registro apenas no log da aplicação, com a retenção padrão de poucas semanas | Durante meses, toda consulta funciona. Qualquer teste feito na semana da implantação passa | É literalmente o que aconteceu: a notificação chegou quatro meses depois, e o log tinha expirado havia três. Prazo de retenção é requisito jurídico com unidade de ANOS, e log de aplicação é infraestrutura operacional com unidade de semanas. Usar um como o outro só falha quando alguém precisa — que é sempre tarde demais |
| Versão do modelo e da política não registradas | Gravar a trilha com a entrada, a saída e o carimbo de tempo, e omitir a versão do modelo e a revisão da política aplicada | A trilha existe, é imutável, e responde "o que foi decidido" e "quando" | E não responde "com base em quê", que é a pergunta que o órgão fez. Uma decisão só é reproduzível se der para reconstruir o contexto: qual versão do modelo, qual revisão da política de devolução, quais campos extraídos e com que confiança. Sem isso, a trilha prova que a decisão ocorreu e não permite defendê-la — e defender era o objetivo |
A pergunta que valida o desenho inteiro
"Por que o sistema recusou o reembolso da Roberta em tal data, e com base em quê?" Rode as quatro injeções e, depois de cada uma, tente responder essa frase usando apenas o que ficou registrado. Se em algum caso a resposta depender da memória de alguém ou de um log que ainda não expirou, a trilha ainda não está pronta.
O time da Cadência aponta que o CloudWatch já registra a chamada ao Bedrock para cada decisão de reembolso, e conclui que isso já é uma "trilha de auditoria" suficiente. Por que essa conclusão está errada?
Observabilidade: as perguntas que o painel tem de responder
Uma trilha de auditoria tem uma propriedade incômoda: ela é consultada raramente e precisa estar íntegra sempre. Como o uso não exercita o sistema, a verificação precisa ser ativa.
- Toda decisão automatizada dos últimos 30 dias tem registro correspondente? A resposta aceitável é 100%, e ela precisa ser calculada comparando a contagem de decisões com a de registros — não presumida.
- Quantas gravações de trilha falharam, e o que aconteceu com a decisão correspondente? Se alguma decisão saiu apesar da falha, o desenho de falhar fechado tem um furo.
- Quantos registros estão sem versão de modelo ou sem revisão de política? Cada um é uma decisão que não se consegue defender.
- Qual a configuração de retenção e de modo de bloqueio, hoje, comparada com a declarada? Desvio aqui é a primeira injeção de falha.
- Quanto tempo leva para localizar e apresentar uma decisão específica de dois anos atrás? É o ensaio da resposta ao órgão, e ele precisa ser cronometrado antes de precisar.
| Alarme | Limiar inicial | O que ele pega |
|---|---|---|
| DecisaoSemRegistro | qualquer ocorrência | o caso da Roberta acontecendo de novo, detectado em horas em vez de meses |
| FalhaDeGravacaoDaTrilha | qualquer ocorrência | trilha indisponível — e, pelo desenho, decisões devem estar sendo recusadas nesse momento |
| RegistroSemVersaoDeModelo | qualquer ocorrência | decisão registrada e não reproduzível |
| DesvioNaConfiguracaoDeBloqueio | qualquer mudança de modo ou de retenção | alguém afrouxou a imutabilidade — intencionalmente ou por cópia de outro ambiente |
| TempoParaLocalizarDecisao | acima do prazo de resposta ao órgão | a trilha existe e não serve, que é uma forma cara de conformidade de fachada |
Escala: 340 decisões por mês, multiplicadas por anos de retenção
| Ordem de grandeza | O que muda no desenho | O que NÃO muda |
|---|---|---|
| 340 decisões por mês, primeiro ano | Nada. O volume é irrisório para qualquer armazenamento | A obrigação de registrar 100% — que não tem nada a ver com volume |
| Cinco anos de retenção acumulada | O acervo cresce de forma monotônica e não pode encolher: por definição, nada sai antes do prazo. A consulta a uma decisão específica passa a exigir indexação por identificador de pedido e por data, senão localizar vira varredura | A imutabilidade. Nem o crescimento nem o custo autorizam apagar |
| Outras decisões automatizadas entrando na trilha | O L92 extrai, o L69 autoriza, esta função aprova — se as três passarem a registrar, o volume se multiplica e o esquema precisa ser comum, senão cada uma grava do seu jeito e a consulta cruzada fica impossível | Que a pergunta do órgão é sempre a mesma forma: quem, quando, com base em quê. Um esquema comum para essa forma é o que torna a trilha uma plataforma em vez de um arquivo |
| Perda de uma zona ou da região | O armazenamento é regional com redundância interna; a conta separada protege contra erro operacional, não contra perda de região | Se a exigência for sobreviver à perda da região, a réplica precisa manter a imutabilidade no destino — copiar para um bucket comum recria o problema no outro lado |
Custo: o único da série que você não pode reduzir depois
Todas as decisões de custo dos 96 laboratórios anteriores são reversíveis: desligar, redimensionar, mudar de classe. Esta não é, e essa é a característica que precisa estar clara antes de escolher o prazo.
| Cenário | O que domina | O que ninguém nota |
|---|---|---|
| Primeiro ano, uma decisão automatizada | Nada, sinceramente. O volume é pequeno demais para aparecer na fatura | É justamente por ser barato que a decisão de prazo é tomada sem cuidado — e ela é a única irreversível do sistema |
| Cinco anos, três decisões automatizadas registrando | O acumulado, que só cresce, com a classe de armazenamento decidindo o multiplicador | Transição para classe fria é compatível com imutabilidade e reduz bastante o acumulado — mas aumenta o tempo de recuperação, que compete com o prazo de resposta ao órgão. Os dois requisitos se tocam, e essa é a decisão de arquitetura desta seção |
| Prazo escolhido "por segurança" em dez anos | O compromisso, dobrado, sem nenhum ganho declarado | Prazo de retenção precisa vir de uma exigência escrita, não de prudência genérica. Dobrar por precaução dobra um custo que ninguém pode cancelar — e guardar dado pessoal além do necessário é, em si, um risco de conformidade na direção oposta |
O número que justifica
Preço por GB-mês, por requisição e por 1.000 tokens muda por região e por classe de armazenamento. Ordem de grandeza para dimensionar; confirme na página de preços antes de levar a proposta. O denominador aqui é uma reclamação formal que a empresa não consegue responder: o custo do processo, da multa possível e do desgaste, contra centavos por decisão registrada. É a conta mais fácil da banda — e ela só foi feita depois do caso da Roberta, que é o padrão em conformidade.
Well-Architected nos seis pilares
| Pilar | Situação hoje | Risco | Melhoria | Prioridade |
|---|---|---|---|---|
| Excelência operacional | Registro imutável em conta separada, gravado antes da resposta | A trilha é consultada raramente, então uma quebra silenciosa sobrevive meses | Ensaio periódico: escolher uma decisão aleatória e cronometrar a resposta completa, como se fosse a notificação | Alta |
| Segurança | Conta separada, bloqueio de objeto, papéis distintos para gravar e para ler | Permissão de contorno em papel administrativo anula a garantia | Modo de conformidade e revisão explícita de quem poderia contornar | Alta |
| Confiabilidade | Decisão recusada quando a trilha não grava | Trilha indisponível vira indisponibilidade de produto | É o trade-off correto e precisa estar declarado ao negócio, não descoberto num incidente | Alta |
| Eficiência de desempenho | Gravação síncrona no caminho da decisão | Acrescenta latência a toda decisão automatizada | Aceitável e pequena; medir e publicar o número evita que alguém "otimize" tornando a gravação assíncrona | Média |
| Otimização de custo | Uma classe de armazenamento, prazo único | Custo acumulado irreversível pelo prazo escolhido | Transição para classe fria após o período de consulta frequente, respeitando o prazo de resposta | Média |
| Sustentabilidade | Todo registro guardado na mesma classe, pelo mesmo prazo | Dado retido além do necessário consome recurso e aumenta exposição | Prazo derivado de exigência escrita, por tipo de decisão — não um prazo único por prudência | Média |
Onde IA entra nesta trilha, e onde ela não pode chegar perto
| Ideia | Vale a pena? | Por quê |
|---|---|---|
| Redigir, a partir da trilha, a explicação que a Roberta consegue ler | Sim, e resolve metade do problema original | A Roberta reclamou duas vezes sem receber explicação além de "não cumpre a política". A trilha agora tem os campos, a política aplicada e a versão do modelo — material técnico que ninguém entende. Transformar isso num parágrafo em português é exatamente o que um modelo faz bem, e o texto é derivado de registro imutável, então dá para conferir. A explicação gerada não entra na trilha: ela é uma vista sobre a trilha, e essa distinção precisa ser mantida |
| Um modelo decidindo o que vale a pena registrar | Não, em nenhuma circunstância | O que não for registrado no momento da decisão não existe depois — não há segunda chance. Um critério probabilístico sobre um registro irreversível é a pior combinação possível: o caso que ele descartar por "pouco relevante" é exatamente o que vira notificação quatro meses depois. Registre tudo pelo esquema fixo; o custo de guardar demais é ordens de grandeza menor que o de não ter |
| Detectar padrões de recusa que possam indicar viés | Sim, e é a extensão de maior valor social | A trilha imutável é, sem que ninguém tenha planejado, um conjunto de dados sobre decisões automatizadas com resultado e contexto. Analisar se as recusas se concentram em algum grupo é uma pergunta que só se torna respondível porque o registro existe. É análise sobre a trilha, com revisão humana das conclusões — e é o tipo de coisa que um órgão regulador vai perguntar antes do que se imagina |
Anti-padrões deste laboratório
| Anti-padrão | Por que alguém faz | Sintoma em produção | Forma correta |
|---|---|---|---|
| Confiar no log de aplicação como trilha de auditoria | já existe por padrão, sem configurar nada a mais | o log expira pela retenção padrão antes de qualquer reclamação formal chegar | registro dedicado, imutável, com prazo de retenção alinhado à obrigação legal |
| Object Lock em modo Governance para decisão que afeta cliente | é mais barato de operar, porque um administrador pode corrigir engano | um principal com a permissão certa apaga ou altera evidência, mesmo sem má intenção | modo Compliance, que nenhum principal — nem o root — consegue driblar dentro do prazo |
| Guardar só o veredito, sem confiança nem versão do modelo | economiza espaço, e parece que "o resultado é o que importa" | a auditoria sabe O QUE o sistema decidiu, mas não consegue dizer POR QUE | registro estruturado com versão do modelo, confiança e justificativa por critério |
| Provar conformidade só quando a auditoria pede | parece suficiente, porque a auditoria formal é esporádica | entre uma auditoria e outra, ninguém sabe se o controle continuou valendo o tempo todo | Audit Manager coletando evidência de forma contínua, não sob demanda |
| Gravar o registro DEPOIS de responder ao cliente | simplifica o código, porque a resposta não espera a escrita terminar | se a escrita falhar depois da resposta sair, a decisão já afetou o cliente sem nenhum registro | grava o registro ANTES de responder, e só responde depois da escrita confirmada |
Evolução em níveis: do log apagável à plataforma de decisão auditável
A terceira arquitetura não é um desenho novo — é a resposta a QUANDO trocar de desenho. Cada nível resolve um risco medido neste laboratório e compra outro no lugar.
Veredito em item mutável do DynamoDB, log da chamada no CloudWatch com retenção padrão. É o que a Cadência tinha, e sobreviveu meses sem ninguém notar — até o Procon perguntar.Registro imutável em S3 com Object Lock modo Compliance, gravado antes da resposta, com CloudTrail data events e Audit Manager provando integridade continuamente.Em vez de um único critério decisivo, o schema de saída do Bedrock passa a exigir o peso de CADA critério da política (prazo, categoria, valor), não só o que mais pesou.Reaproveitando o padrão A2I do L92: decisão de valor alto ou confiança baixa passa por revisão humana ANTES de responder ao cliente, não só depois de registrada.Reaproveitando o padrão de golden set e juiz do L88: decisões recentes são comparadas periodicamente contra um conjunto de decisões humanas conhecidas como corretas.A trilha imutável deixa de ser específica de reembolso e vira infraestrutura compartilhada — qualquer decisão automatizada da Cadência (reembolso, acesso a dado do L69, crédito futuro) grava no mesmo padrão, com cota e chargeback por time (L98) e revisão Well-Architected (L100).Por que este laboratório vem depois do L92, não antes
A trilha imutável de decisão pressupõe que já existe uma decisão estruturada, com confiança e critério, para registrar — é exatamente o que o L92 e o L87 produzem antes deste laboratório entrar em cena. Quem tenta construir uma trilha auditável antes de ter uma decisão auditável para auditar está protegendo um rastro que ainda não existe.
Quando algo não funciona
| Sintoma | Causa provável | Como investigar | Log ou métrica | Correção |
|---|---|---|---|---|
| Existe decisão sem registro correspondente | Gravação após a resposta, com falha no meio — ou um caminho de código que não passa pela trilha | Comparar a contagem de decisões com a de registros no mesmo período e isolar as ausentes | Decisões sem registro | Gravar antes de responder e recusar a decisão quando a gravação falhar. Caminho de código que decide sem registrar é o defeito, não a exceção |
| Um registro foi apagado | Bloqueio em modo governança com permissão de contorno disponível | Verificar o modo configurado e quem detém a permissão de contorno | Eventos de exclusão no rastro de auditoria da conta da trilha | Modo de conformidade. E tratar a exclusão ocorrida como incidente de conformidade, com notificação — não como ajuste de configuração |
| Não se consegue reproduzir uma decisão antiga | Versão do modelo ou revisão da política não registrada | Abrir o registro e conferir quais campos de contexto existem | Registros sem versão de modelo | Acrescentar ao esquema. Vale notar que isso não conserta os registros antigos — eles são imutáveis, inclusive nos defeitos |
| Localizar uma decisão de dois anos atrás leva horas | Ausência de índice por identificador de pedido e por data | Cronometrar uma consulta real, escolhida ao acaso | Tempo para localizar uma decisão | Índice separado e mutável apontando para os objetos imutáveis. O índice pode ser reconstruído; a trilha, não |
| Decisões estão sendo recusadas em produção | A trilha está indisponível e o desenho falha fechado | Verificar a saúde do destino da trilha e as permissões de gravação | Falhas de gravação da trilha | É o comportamento correto sendo doloroso. Restabelecer a trilha; a alternativa — decidir sem registrar — é o que este laboratório existe para impedir |
Limpeza: o que o destroy não leva
#!/usr/bin/env bash
# limpeza.sh -- ATENCAO ESPECIAL: o bucket de trilha, se voce seguiu o
# Terraform desta secao com a retencao de PRODUCAO (1825 dias), NAO pode ser
# esvaziado nem apagado por "terraform destroy" nem por nenhum outro comando
# antes do prazo terminar -- e essa e a garantia, nao um bug.
# 1) Antes de aplicar este laboratorio como EXERCICIO (nao producao), troque
# a retencao para o MINIMO que a API aceita -- 1 dia -- para nao prender
# um bucket de conta de teste por 5 anos.
# Isso tem de ser decidido ANTES do primeiro PutObject: retencao ja
# aplicada a um objeto nao diminui.
# default_retention { mode = "COMPLIANCE", days = 1 }
# 2) Confirmar que nenhum objeto do bucket ainda esta dentro da retencao.
aws s3api list-objects-v2 --bucket cadencia-trilha-decisoes \
--query "Contents[].{Key:Key,LastModified:LastModified}"
for obj in $(aws s3api list-objects-v2 --bucket cadencia-trilha-decisoes --query "Contents[].Key" --output text); do
aws s3api get-object-retention --bucket cadencia-trilha-decisoes --key "$obj" 2>/dev/null \
&& echo " ^ $obj AINDA RETIDO -- espere o prazo (RetainUntilDate acima) antes de apagar"
done
# 3) So DEPOIS do prazo expirar para TODOS os objetos, esvaziar o bucket.
aws s3api list-object-versions --bucket cadencia-trilha-decisoes \
--query "[Versions,DeleteMarkers][].{Key:Key,VersionId:VersionId}" --output json \
| jq -c '.[]' | while read -r obj; do
aws s3api delete-object --bucket cadencia-trilha-decisoes \
--key "$(echo "$obj" | jq -r .Key)" --version-id "$(echo "$obj" | jq -r .VersionId)"
done
# 4) terraform destroy cuida do resto: bucket ja vazio, indice do DynamoDB,
# trilha do CloudTrail, chave do KMS (janela de exclusao), Audit Manager.
terraform destroy -auto-approve
# 5) Conferir que a chave do KMS entrou em janela de exclusao.
aws kms describe-key --key-id alias/cadencia-trilha-decisoes --query "KeyMetadata.KeyState"
# Esperado: "PendingDeletion" -- continua cobrando ate os 7 dias da janela passarem.
A armadilha real de laboratório: retenção de produção numa conta de teste
Se você aplicou este laboratório com a retenção de PRODUÇÃO (1.825 dias) em vez da retenção de exercício (1 dia), o bucket de trilha vai continuar existindo — e cobrando armazenamento — pelos próximos 5 anos, mesmo depois de "terraform destroy" rodar em tudo o mais. Não existe comando, nem chamado de suporte da AWS, que reverta isso num objeto já gravado em modo Compliance. Ajuste a retenção para 1 dia ANTES de gravar o primeiro registro de teste — depois é tarde.
Resumo: problema, peça e motivo
| Problema | Peça | Motivo |
|---|---|---|
| Registro sobrescrito ou apagado sem ninguém perceber | S3 com Object Lock modo Compliance | nenhum principal, nem o root, altera ou apaga dentro do prazo de retenção |
| Log de aplicação expira antes da reclamação chegar | Bucket dedicado, retenção em anos | prazo alinhado à obrigação legal, não à configuração padrão de infraestrutura |
| "O sistema decidiu" sem versão do modelo nem confiança | Saída estruturada do Bedrock, versão fixada | critério decisivo, confiança e política aplicada entram no registro, não só o veredito |
| Ninguém sabe se a trilha continuou íntegra entre uma auditoria e outra | Audit Manager | evidência de integridade coletada de forma contínua, não sob demanda |
| Toda leitura e escrita do bucket é invisível | CloudTrail com data events | tentativa aceita ou negada de tocar no bucket de decisões vira evento auditável |
| Decisão responde ao cliente antes de existir prova dela | Escrita síncrona antes da resposta | a resposta ao cliente só é montada depois que o registro imutável já existe |
Perguntas frequentes
❓ Por que gravar o registro de decisão ANTES de responder ao cliente, e não depois?
❓ Qual a diferença entre Object Lock modo Governance e modo Compliance?
❓ Um administrador com acesso total consegue apagar um registro da trilha?
❓ Por que a versão do modelo precisa entrar no registro, não só o nome do serviço?
❓ O Audit Manager substitui a necessidade de ter o CloudTrail e o Object Lock?
❓ Quanto tempo leva para reconstruir uma decisão específica pela trilha imutável?
❓ Esse desenho de trilha imutável serve para qualquer tipo de decisão automatizada?
❓ Por que não usar só um campo "apagado=true" no DynamoDB, em vez do Object Lock?
Fixando
O L50 usa Object Lock em modo Governance para a trilha de resposta a incidente; este laboratório usa modo Compliance para a trilha de decisão automatizada. Por que a escolha muda entre os dois laboratórios?
A seção de prova deste laboratório testa a tentativa de apagar um registro usando uma role com AdministratorAccess, sem nenhuma SCP bloqueando o IAM. Por que testar especificamente com uma role tão permissiva, em vez de só uma role restrita?
Próximo laboratório
Próximo passo: L100 (projeto final — plataforma .NET 8 + AWS + IA)
Este laboratório prova que UMA classe de decisão automatizada — reembolso — pode ser reconstruída de ponta a ponta, com a alteração bloqueada mesmo por um administrador total. O L100, projeto final da série, integra essa trilha imutável ao restante do que a Cadência construiu: o agente do L87, a avaliação contínua do L88, a plataforma multi-time do L98 e os seis pilares do Well-Architected revisados sobre um sistema completo — a trilha de decisão auditável deixa de ser específica de reembolso e vira parte da infraestrutura que qualquer decisão automatizada nova reaproveita.
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…