04 · DATABASE MANAGER
Acesso à produção com pedido, aprovação de outra pessoa e prazo.
A sua organização define, banco a banco, quem aprova, em que horário uma mudança entra e por quanto tempo um acesso vale. A plataforma faz essa regra valer: a senha nasce no Agente, o dado sensível volta mascarado e o guardrail recusa o DELETE sem WHERE.
Abre uma conversa no WhatsApp. Prefere e-mail? [email protected]
O que faz
O pedido
Quem precisa de acesso informa no pedido o banco, as operações — SELECT, INSERT, UPDATE, DELETE —, as tabelas ou o schema e o motivo, que é obrigatório. Se o pedido mistura leitura e escrita, ele é tratado inteiro como escrita.
Aprovadores nomeados banco a banco
Cada banco tem três listas de aprovadores: acesso, comando e migration. Se uma dessas listas está vazia, o banco recusa todo pedido daquele tipo com NO_NAMED_APPROVERS. O banco pode exigir duas aprovações. Ninguém aprova o próprio pedido, seja qual for a sua permissão, e o papel de quem aprova é consultado na organização na hora, e não lido do token.
Por quanto tempo vale
A sua organização define, para cada banco, um prazo padrão e um teto, separados para leitura e para escrita — por exemplo, escrita por 2 horas, com teto de 8. A contagem começa na aprovação. Independentemente da configuração, nenhum acesso passa de 30 dias, e um pedido que ninguém decide expira em 7.
Uma senha que ninguém vê
Com o provisionamento ligado, a aprovação cria no PostgreSQL uma role exclusiva daquele acesso, com apenas o que foi pedido e com VALID UNTIL no fim do prazo. A senha nasce no Agente e não sai de lá; quem conecta passa pelo proxy, e o Agente autentica no lugar da pessoa.
O acesso termina em três camadas
O próprio PostgreSQL recusa a role no fim do prazo, mesmo com o Heimdall inteiro fora do ar. O proxy pergunta à API, a cada conexão nova, se o acesso ainda vale — e, numa sessão aberta, o primeiro comando depois de uma revogação é recusado. Por fim, uma varredura remove a role por meio do Agente.
Dentro da sessão
Cada resultado volta com as colunas sensíveis mascaradas, e cada statement passa pelos guardrails da sua organização antes de chegar ao banco. As duas seções seguintes mostram como: masking e guardrail.
Mudança de schema
Uma migration passa por submissão, análise e aprovação de outra pessoa, e é agendada dentro da janela de horário do banco, num fuso IANA. Quem a executa é o Agente, dentro da sua rede. A janela é conferida na aprovação, no agendamento e no instante de rodar.
Na emergência: o break-glass
Quem está de plantão entra sem esperar aprovação, por até 4 horas. É uma permissão própria, que nenhum papel tem por padrão. Os aprovadores são avisados antes, e, se o aviso falhar, a entrada é recusada. O masking e os guardrails continuam valendo, exceto o guardrail que a sua organização marcou como contornável, que passa a avisar em vez de recusar. Depois, outra pessoa revisa o evento.
O que fica registrado
O Heimdall registra cada pedido, aprovação, recusa, revogação e break-glass, além do texto de cada comando SQL executado pelo proxy, com o tipo, o desfecho, as linhas afetadas e a duração. Registra também cada classificação de coluna e cada regra de masking criada ou apagada.
Como funciona
Um acesso, do pedido ao fim do prazo:
- A pessoa faz o pedido pelo CLI. Os aprovadores nomeados daquele banco são avisados.
- Outra pessoa aprova. A API confere a lista, o papel e o prazo, e grava a decisão.
- A API coloca o trabalho na fila. O Agente vinculado ao
environmentdaquele banco o retira da fila e cria a role, com a credencial administrativa que só ele tem. - A pessoa conecta pela borda, com o certificado pessoal dela. O proxy pergunta à API se o acesso vale e autentica no banco com a senha que nunca saiu do Agente.
- A cada statement, o proxy confere os
guardrailsantes de mandá-lo ao banco. A cada resultado, troca o valor das colunas mascaradas antes de devolvê-lo. Nada muda no banco, e o resultado não vai para a API. - O que um
guardrailrecusou vira um comando: se outra pessoa aprovar, ele passa uma vez, na sessão de quem pediu. - No fim do prazo, o PostgreSQL recusa a role, e o Agente a remove.
heimdall db access request pg-orders-orders --operations SELECT,UPDATE --tables public.orders,public.customers --reason "corrigir pedidos com total errado" --task-key OPS-101
✓ Access request submitted for database 'pg-orders-orders'
Operations: SELECT, UPDATE
Reason: corrigir pedidos com total errado
Task: OPS-101
Expires: 2026-10-10T08:13:58.978413Z
Duration: this database's default for those operations
('heimdall db expiry show --database pg-orders-orders' shows the defaults and the ceilings.)
Status: pending approval
An approver decides it with 'heimdall db access approve <grant-id>'.
heimdall db access approve 05512517-3276-4918-af87-79ef234fbd9d
✓ Access request 05512517-3276-4918-af87-79ef234fbd9d approved
Status: active
The requester can connect now with 'heimdall db connect <database>'.
NO BANCO: A ROLE, COM VALID UNTIL, E SÓ O QUE FOI PEDIDO
psql -h 127.0.0.1 -U orders_admin -d orders -tA -c "select rolname, rolvaliduntil from pg_roles where rolname like 'hd\_%'" -c "select table_name, privilege_type from information_schema.role_table_grants where grantee like 'hd\_%' order by 1, 2"
hd_pg_orders_orders_7cyjmwi7_hdm|2026-10-10 08:14:06+00
customers|SELECT
customers|UPDATE
orders|SELECT
orders|UPDATE
heimdall db access request e heimdall db access approve, e a role criada no banco.masking pela coluna de origem
Quem consulta a produção pelo proxy recebe [CPF] no lugar do CPF. A regra segue a coluna de onde o valor veio, e não o nome com que ele chega.
Primeiro classificar, depois mascarar
Classificar uma coluna é registrar o que ela guarda — dado pessoal, financeiro, de saúde, credencial — num vocabulário fechado, que a sua organização pode estender. Mascarar é outra coisa: é criar a regra que troca o valor. Cada regra indica uma coluna, uma estratégia e um escopo — a organização inteira, uma instância, um banco, um schema ou uma tabela — e só vale dentro desse escopo.
Pela coluna de origem
Para cada campo do resultado, o PostgreSQL informa de que tabela e de que coluna ele veio, e o proxy aplica a regra dessa coluna. Renomear com AS ou passar por subquery, CTE ou view não muda a origem. Um campo que não vem de coluna nenhuma — expressão, função, UNION, a linha inteira — sai com a estratégia mais restritiva em vigor na sessão, mesmo um count(*): como não sabe o que uma função revela, o proxy não deixa que ela revele nada.
Os outros caminhos
Numa sessão mascarada, COPY … TO STDOUT é recusado, e a mensagem orienta a ler as linhas com SELECT. A mensagem de erro do servidor chega só com o código, porque o texto dela pode citar o valor. Copiar a coluna para uma tabela temporária não adianta: a leitura volta mascarada. E a decisão não depende da ferramenta: o proxy pede ao banco a descrição de cada resultado antes de executar a consulta, e uma linha sem decisão derruba a conexão em vez de passar.
Onde acontece
No Agente, no caminho de volta. Nada muda no banco — nenhuma view nem coluna a mais —, e a API não vê nem o valor original nem o mascarado.
NOTICE: Heimdall authenticated this connection as PostgreSQL role "hd_pg_orders_orders_7cyjmwi7_hdm" under access grant 05512517-3276-4918-af87-79ef234fbd9d; any credentials you supplied were not used and were not sent to the database.
psql (18.6, server 17.11)
Type "help" for help.
=> select cpf as documento, upper(email) as e, name from customers limit 1;
documento | e | name
-----------+---------+-----------
[CPF] | [EMAIL] | Ana Souza
(1 row)
cpf com outro nome e email dentro de uma função, os dois mascarados.O que chega a quem consulta
| ESTRATÉGIA | O QUE CHEGA |
|---|---|
full |
[REDACTED], ou *** num valor de até três caracteres |
partial |
os dois primeiros e os dois últimos caracteres: an***st |
redact |
um marcador conforme o formato do valor: [EMAIL], [CPF], [PHONE], [CREDIT_CARD] ou [REDACTED] |
hash |
os 16 primeiros caracteres do SHA-256, sem sal: serve para comparar dois valores, mas não para esconder um valor com poucas possibilidades, como um CPF |
Quando duas regras casam com a mesma coluna, vence a mais restritiva.
Até onde vai
O masking protege contra a exposição acidental e contra o que a ferramenta mostra: a tela, o arquivo exportado, a captura de tela que vai parar num ticket. Não protege contra quem tem o acesso e escreve SQL para extrair o valor. Para quem não pode ver uma coluna, o controle é o escopo do acesso: o pedido nomeia as tabelas, e um acesso só de SELECT não copia o valor para outra coluna. O masking vale na sessão que passa pelo proxy — heimdall db connect, heimdall tunnel e o break-glass; já a aplicação, com o service user dela, e a migration conectam direto ao banco.
guardrail que recusa antes de chegar ao banco
O DELETE sem WHERE não chega ao banco. Para rodar o que um guardrail recusou, outra pessoa precisa aprovar aquele statement, que então passa uma vez.
A regra é sua
Nenhum guardrail vem instalado. A sua organização escreve cada um e o atribui à organização inteira, a uma instância ou a um banco. As regras cobrem tipos de statement proibidos, como DROP TABLE, TRUNCATE e DROP SCHEMA; UPDATE e DELETE só com WHERE; índice só com CONCURRENTLY; coluna NOT NULL só com DEFAULT; nenhum DROP COLUMN; e um teto de linhas, acima do qual uma tabela não é apagada nem reescrita inteira.
Recusar, avisar ou registrar
Cada guardrail tem uma severidade: block recusa, warn deixa passar e registra, audit_only só registra. Ele é avaliado em três lugares: no proxy, a cada statement da sessão, antes mesmo de conferir o acesso; na submissão de uma migration, que é recusada; e na submissão de um comando, onde não recusa, mas deixa uma anotação para quem aprova.
Lido como o banco lê
O statement é lido sem os comentários e sem o conteúdo dos literais, com as regras de espaço e de quebra de linha do próprio PostgreSQL: um WHERE dentro de um comentário não conta, e um \r não esconde um DROP. EXPLAIN ANALYZE é julgado pelo que executa, e uma escrita dentro de um WITH conta como escrita. O que não dá para julgar pelo texto — DO, CALL, EXECUTE, COPY, MERGE — é recusado por todo guardrail block, e só roda com aprovação.
A recusa ensina o caminho
A recusa nomeia o guardrail e traz pronto o comando que pede a aprovação. A submissão não recusa: ela anota cada guardrail que o texto cruza — inclusive num statement escondido depois de um inofensivo —, e é isso que quem aprova lê antes de decidir. A anotação não muda depois de gravada.
Aprovado, passa uma vez
Aprovar não executa: quem pediu roda o statement na própria sessão, em até 4 horas. Ele passa uma única vez, e a comparação é feita pelo que o servidor executaria, não pelo texto: mover o WHERE para dentro de um comentário transforma o statement em outro comando. A segunda execução é recusada de novo. E a aprovação não amplia o acesso: as tabelas e as operações continuam sendo as do pedido.
UPDATE sem WHERE recusado, submetido com os guardrails que cruza, executado uma vez depois da aprovação e recusado na segunda.1 · NA SESSÃO, A RECUSA
NOTICE: Heimdall authenticated this connection as PostgreSQL role "hd_pg_orders_orders_7cyjmwi7_hdm" under access grant 05512517-3276-4918-af87-79ef234fbd9d; any credentials you supplied were not used and were not sent to the database.
psql (18.6, server 17.11)
Type "help" for help.
=> UPDATE customers SET name = 'cliente de teste';
ERROR: HEIMDALL GUARDRAIL: UPDATE without WHERE clause is blocked by guardrail "DELETE and UPDATE need a WHERE" [require-where]. To run it, get it approved first, then run it again: heimdall db command submit 'b4bc0108-1814-47df-a9a9-7e591423dae6' --reason '<why this must run>' --sql 'UPDATE customers SET name = '\''cliente de teste'\'';'
2 · A SUBMISSÃO ANOTA PARA QUEM APROVA
heimdall db command submit 'b4bc0108-1814-47df-a9a9-7e591423dae6' --reason 'OPS-48: renomear todos os clientes de teste' --sql 'UPDATE customers SET name = '\''cliente de teste'\'';'
✓ Command 2fb2533e-b875-452f-bbd5-50f3f0bab8b8 submitted for database 'pg-orders-orders'
Status: pending_review
It runs only after somebody else approves it.
Guardrails it crosses, shown to the approver:
block: UPDATE without WHERE clause is blocked by guardrail "DELETE and UPDATE need a WHERE" [require-where]
3 · APROVADO, PASSA UMA VEZ
NOTICE: Heimdall authenticated this connection as PostgreSQL role "hd_pg_orders_orders_7cyjmwi7_hdm" under access grant 05512517-3276-4918-af87-79ef234fbd9d; any credentials you supplied were not used and were not sent to the database.
psql (18.6, server 17.11)
Type "help" for help.
=> UPDATE customers SET name = 'cliente de teste';
UPDATE 2
=> UPDATE customers SET name = 'cliente de teste';
ERROR: HEIMDALL GUARDRAIL: UPDATE without WHERE clause is blocked by guardrail "DELETE and UPDATE need a WHERE" [require-where]. To run it, get it approved first, then run it again: heimdall db command submit 'b4bc0108-1814-47df-a9a9-7e591423dae6' --reason '<why this must run>' --sql 'UPDATE customers SET name = '\''cliente de teste'\'';'
Até onde vai
O guardrail julga o texto, sem consultar o catálogo do banco: se uma função que escreve for chamada dentro de um SELECT, quem responde por ela é o privilégio da role, que tem só o que foi pedido. Ele vale na sessão que passa pelo proxy e na submissão de migration e de comando; a aplicação, com o service user dela, não passa por ele. No break-glass, um guardrail que a sua organização marcou como contornável avisa em vez de recusar.
Onde o dado fica
Tudo roda na sua infraestrutura. Mesmo assim, a divisão abaixo importa dentro da sua empresa, porque mostra quem, no seu time, tem acesso a quê.
| NO AGENTE | NA API, QUE É SUA |
|---|---|
A credencial administrativa de cada instância, no store de secrets. A senha de cada role de acesso, gerada ali. A senha de cada | O nome de cada banco, schema, tabela e coluna, com a estimativa de linhas e a classificação de cada coluna. O nome de cada role. As regras de |
Quem aprova o quê
| ATO | QUEM |
|---|---|
Pedir acesso, submeter migration ou comando |
todo membro |
Aprovar acesso, comando e migration |
owner e admin, por padrão — nunca quem pediu. A lista nomeada decide quem é avisado e se o pedido pode ser feito |
Escrever guardrail, aprovadores, janelas e prazos |
owner e admin |
Classificar colunas e escrever regras de masking |
owner e admin |
Invocar break-glass |
só quem uma policy nomear, por exemplo o grupo de plantão. Um banco pode proibi-lo |
Revisar um break-glass |
owner e admin — nunca quem invocou |
| Revogar um acesso aberto | owner e admin |
Com os outros módulos
-
03
Secret Manager
A credencial administrativa de cada banco é um
secretno Agente; a senha de cadaservice usernasce e é rotacionada lá. -
05
Acesso a aplicações internas
A sessão de banco chega pela mesma borda; quem conecta precisa da concessão de rede e da aprovação do banco.
-
07
Incident Manager
Um
break-glassinvocado com um incidente aberto sobre o banco fica vinculado a ele, sem que ninguém precise digitar o número. O incidente nomeia a entrada, mas nunca a concede.
Converse com quem constrói o Heimdall.
Conte como a sua engenharia guarda secrets e acessa o banco hoje. Respondemos dizendo o que o Heimdall colocaria sob regra primeiro e como a implantação chegaria lá.
Abre uma conversa no WhatsApp. Prefere e-mail? [email protected]