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

Os três Levels (produto)

Como falamos do sistema no dia-a-dia — quem opera em cada camada — e como cada Level mapeia na hierarquia técnica abaixo.

L1  KRAT ONE · Plataforma

Dona do software (master). Opera a rede de agências: métricas, impersona, governa. Acima da tenancy — não é um tenant.

super-adminMaster Console

L2  Agência

Assina o KRAT ONE para gerir seus clientes. É um account + workspace; a equipe interna são memberships (owner/admin/manager/member).

account+workspaceo app

L3  Cliente da agência

O cliente final (a Conquista). É um client + usuários externos (client_user via client_access). Vê só a conta dele, no portal branded.

client + client_usero portal
LevelQuem éTabela(s)Onde viveIsolamento
L1 PlataformaKRAT ONE (dona)— (super-admin)Master Consoleacima da RLS
L2 AgênciaEx.: HLDHubaccount · workspace · membership(internal)O appRLS workspace
L3 ClienteEx.: Pizzaria do Joãoclient · membership(external) · client_accessPortal brandedRLS + client_access

A hierarquia técnica abaixo (Account → Território → Conquista) é como os três Levels são implementados: L1 = a plataforma (acima); L2 = Território; L3 = Conquista + seu usuário externo.

A

Hierarquia técnica (account → território → conquista)

Como os Levels são implementados no multi-tenant. Confundir os três é a origem da maioria dos bugs.

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.