kisenon
Agent-Safe Change Control

Bifurcações mascaradas

Dê a um agente (ou a um humano) o formato da produção e nada da sua PII — políticas de mascaramento, a biblioteca de funções embutidas e como branches e sandboxes mascarados permanecem selados até o mascaramento ser confirmado.

MASK é a trilha do Controle de Mudanças Seguro para Agentes para dados sensíveis: ela entrega a um agente uma bifurcação com o formato da produção e nada da sua PII. Ela funciona de duas maneiras — branches mascarados (para humanos: dev, CI, contratados) e sandboxes mascarados (para agentes). Ambos são cobertos abaixo.

O mascaramento de dados anonimiza colunas sensíveis no momento em que um branch é criado. Você anexa uma política de mascaramento à chamada de criação do branch, e as colunas correspondentes do novo branch são reescritas de forma irreversível — com hash, anuladas, truncadas ou substituídas — antes de o branch algum dia ficar acessível. O branch pai nunca é modificado.

Você recorreria a isso para entregar dados com o formato de produção ao desenvolvimento, ao CI ou a um contratado sem entregar a PII contida neles: formatos de tabela reais, contagens de linhas reais, e-mails falsos.

Como funciona

Uma política de mascaramento é um conjunto nomeado de regras com escopo de projeto. Cada regra corresponde a colunas por padrão e nomeia uma função de mascaramento embutida:

CampoSignificado
schema_patternPadrão de nome de esquema (% ou * = qualquer, _ = um caractere). O padrão é *.
table_patternPadrão de nome de tabela.
column_patternPadrão de nome de coluna.
masking_fnUma das funções embutidas abaixo.
fn_argsArgumentos da função (apenas mask_constant recebe um: value).

No momento da criação do branch, quando um masking_policy_id é fornecido:

  1. O branch faz fork do seu pai normalmente (copy-on-write — instantâneo).
  2. O branch entra no estado masking. Nenhum endpoint é provisionado e o proxy recusa conexões a ele: não há nenhuma janela em que os dados pré-mascaramento possam ser lidos.
  3. Um worker de mascaramento se conecta ao compute do branch como um role de menor privilégio, descobre o esquema, faz corresponder suas regras contra ele e executa cada reescrita em uma única transação.
  4. O branch aterrissa em ready e seus endpoints sobem — agora servindo apenas dados mascarados. Em qualquer falha, o branch aterrissa em failed e permanece selado; exclua-o e tente novamente.

As entradas das regras são tratadas como dados, nunca como SQL: os identificadores são citados e os valores dos argumentos são vinculados como parâmetros na execução, de modo que um nome de coluna ou constante hostil não consiga escapar da reescrita.

Funções embutidas

FunçãoEfeito
mask_emailmd5(value)@masked.invalid
mask_nameName_ + um prefixo de hash de 8 caracteres
mask_nullNULL (a coluna precisa permitir nulos)
mask_constantUm valor fixo que você fornece (fn_args.value)
mask_ssn_partial***-**-1234 — mantém os últimos 4 dígitos
mask_credit_card****-****-****-1234 — mantém os últimos 4 dígitos
mask_ip0.0.0.0
mask_date_yearMantém o ano, trunca para 1º de janeiro
mask_hashmd5(value)
mask_shuffleEmbaralhamento baseado em hash (o embaralhamento completo de caracteres está planejado)
mask_phoneUm número sintético +1-555-XXXX
mask_uuidUm novo UUID aleatório

O catálogo também é servido pela API:

curl -s https://api.kisenon.com/v1/masking-functions \
  -H "Authorization: Bearer $KISENON_API_KEY"

Gerenciando políticas

No console, abra Project settings → Data masking para criar uma política: dê um nome a ela, adicione regras (colunas com padrão + um menu suspenso de função) e salve. A mesma superfície existe na API:

curl -s -X POST \
  https://api.kisenon.com/v1/projects/$PROJECT_ID/masking-policies \
  -H "Authorization: Bearer $KISENON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "dev-safe",
    "rules": [
      {"table_pattern": "users", "column_pattern": "email", "masking_fn": "mask_email"},
      {"table_pattern": "users", "column_pattern": "phone", "masking_fn": "mask_null"},
      {"table_pattern": "%", "column_pattern": "%ssn%", "masking_fn": "mask_ssn_partial"}
    ]
  }'

Depois crie um branch mascarado adicionando a política a uma chamada normal de criação de branch:

curl -s -X POST \
  https://api.kisenon.com/v1/projects/$PROJECT_ID/branches \
  -H "Authorization: Bearer $KISENON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "masked-dev", "masking_policy_id": "'$POLICY_ID'"}'

O branch reporta state: "masking" enquanto a reescrita roda; observe-o mudar para ready no console (ao vivo, via o stream de eventos do projeto) ou fazendo polling em GET /v1/branches/{id}.

Sandboxes mascarados (para agentes)

As mesmas políticas mascaram a bifurcação do sandbox de um agente, de modo que um agente trabalha contra o formato da produção e nunca vê sua PII. Anexe uma política na criação:

keon sandbox create --project <id> --masking-policy <policy-id>

O mascaramento roda antes de o sandbox ativar. O sandbox reporta masked: true e seu masking_policy_id, e enquanto ele ainda está creating um objeto de progresso masking (state, phase, rules_total, rules_completed, rows_affected_so_far, error) mostra a reescrita avançando.

Defina um piso por projeto para que todo sandbox seja mascarado por padrão:

keon projects update <id> --sandbox-masking-policy <policy-id>   # or: none

Um --masking-policy por requisição substitui o padrão do projeto, mas ele nunca pode optar por sair de um mandato — uma defesa contra um agente convencer com conversa a sair do mascaramento. Definir o mandato é apenas para proprietário/administrador; chaves de capacidade de agente são recusadas (403).

À prova de falhas por construção. Os endpoints de uma bifurcação mascarada nascem em um estado de mascaramento — o proxy recusa conexões (SQLSTATE 57P05) até a reescrita ser confirmada. Se o passo de mascaramento falha ou não está conectado, a criação falha fechada: nenhuma URL é jamais emitida para uma bifurcação não mascarada. O worker de mascaramento se conecta como o role de menor privilégio kisenon_masker (nunca um superusuário), não detém nenhum RBAC de cluster, e sua senha por execução nunca é persistida ou registrada.

Duas ressalvas que vale conhecer:

  • DML contra valores mascarados não corresponderá no pai no replay. Uma instrução como UPDATE … WHERE email = 'a1b2@masked.invalid' tem como alvo um valor mascarado que não existe em main. O trabalho de faixa de esquema é independente de valor e promove de forma limpa; DML dependente de valor não é o trabalho para uma bifurcação mascarada.
  • Chaves estrangeiras permanecem impostas. O mascaramento desabilita triggers de usuário (ALTER TABLE … DISABLE TRIGGER USER), não triggers de integridade referencial. Mascarar uma coluna dentro de uma relação de chave estrangeira pode, portanto, atingir um erro de RI e fazer a bifurcação falhar fechada em vez de quebrar silenciosamente a referência.
  • a v1 mascara apenas o banco de dados main.

Bom saber

  • O mascaramento é único, na criação. Editar uma política nunca afeta branches que já foram criados com ela — eles mantêm o formato de dados com que nasceram. Recrie o branch para aplicar as novas regras.
  • Uma política em uso não pode ser excluída. Excluir uma política com a qual um branch ativo foi criado retorna 409 policy_in_use; exclua esses branches primeiro.
  • Regras sem correspondência são ignoradas, não fatais. Se o seu esquema derivou e um padrão não corresponde a nada, o branch ainda assim é concluído — verifique os padrões da regra se uma coluna que você esperava mascarada ainda tem o formato de dados real.
  • Incompatibilidades de tipo fazem o branch falhar. Uma regra que corresponde a uma coluna que sua função não consegue reescrever (digamos mask_email em um integer) aborta todo o mascaramento — o branch aterrissa em failed e nunca é exposto pela metade mascarado.