K1 Krat One Structure Atlas ENV · lab
Fundação/Estrutura Hierárquica
Core #1 · Tenancy

Estrutura Hierárquica

Como o KRAT ONE organiza contas, times e clientes — e onde exatamente vive a fronteira de isolamento que separa os dados de um tenant do outro.

Fronteira RLS = Workspaceisolamento no Postgresmembership N:N
A

Os três níveis

Cada nível tem um papel distinto. Confundir os três é a origem da maioria dos bugs de multi-tenancy.

Account account · unidade de billing 1 assinatura Stripe
Workspace = "Território" workspace · FRONTEIRA DE ISOLAMENTO (RLS) tenant boundary
Client = "Conquista" client · marca gerenciada dimensão de negócio
·Contacts · Deals · Campaigns · Social profiles · SEO projects · Agents recursos, todos com client_id
Client · outra Conquista mesma fronteira, dados separados por client_id
Workspace · outro Território isolamento total: RLS nunca cruza workspace
B

Papel de cada nível

NívelTabelaResponsabilidadeIsolamentoExemplo
AccountaccountCobrança e plano. Um Stripe Customer, uma assinatura."Agência Aurora Ltda"
TerritórioworkspaceUnidade de trabalho e fronteira de segurança. RLS ativa aqui.RLS"Aurora · Time SP"
ConquistaclientMarca/negócio gerenciado. Dimensão dentro do território.client_id"Pizzaria do João"
Recursoscontact, deal…Todo dado de negócio carrega workspace_id + client_id.RLScontatos, campanhas…
Por que a fronteira é o Workspace e não o Client? Numa agência, um usuário trabalha em vários clientes o tempo todo — dashboards "todos os clientes" seriam um pesadelo se a RLS fosse por client. Client é uma dimensão dentro do território, não uma fronteira de segurança.
C

Onde as pessoas se conectam

Usuários são identidades globais; o vínculo com o território é a membership.

app_user global

A pessoa existe uma vez no sistema (e-mail único). Isso faz SSO e "convidar por e-mail" funcionarem limpo entre territórios.

idemailauth_provider_id

membership N:N

Conecta um user a um workspace com um role e um type (interno/externo). Um user pode estar em vários territórios com papéis diferentes.

workspace_iduser_idroletype

O escopo fino abaixo do território (quais Conquistas cada membro enxerga) é a tabela client_access — detalhada em Estrutura de Acessos.

D

Isolamento no banco (RLS)

Não confiamos só no código — o Postgres recusa dados de outro tenant.

-- a cada request, o gateway resolve o território ativo e injeta:
SET LOCAL app.workspace_id = '…';

-- toda tabela de negócio:
ALTER TABLE contact ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON contact
  USING ( workspace_id = current_setting('app.workspace_id')::uuid );
Mesmo um bug de query não vaza dados entre territórios: a política de RLS é o backstop final. Identidade vem do JWT (Supabase); o território ativo vem do SET LOCAL por request.