> ## Documentation Index
> Fetch the complete documentation index at: https://docs.userkit.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Arquitetura

> Onde cada peça roda, e qual delas chega a ver uma credencial.

Três peças.

* **A API**, em `https://api.userkit.dev` — dona dos dados e de todas as regras.
* **O painel**, em `app.userkit.dev` — onde o seu time administra a organização.
* **Seu produto** — o que você estiver construindo. Fala com a API usando uma chave
  de API da organização a partir do seu servidor, e uma chave publicável a partir
  das suas páginas.

## O painel nunca entrega um token ao navegador

```
navegador ──cookie httpOnly──▶ servidor do painel ──Bearer──▶ API ──▶ Postgres
```

As telas do painel nunca carregam uma credencial: o token de sessão fica num cookie
`httpOnly` e é o servidor do painel que anexa o header `Authorization`. Nenhum XSS
consegue ler aquele token, e os endpoints que *respondem* com uma credencial —
login, dois fatores, cadastro — gravam o valor direto no cookie em vez de entregá-lo
ao script.

Vale como padrão para o seu próprio front: o que o navegador não segura, um ataque
no navegador não rouba.

## Seu produto, esse sim, fala direto com a API

O plano de clientes tem a postura oposta, de propósito.

```
seu servidor ──Bearer uk_sk_…──▶  /v1/contacts, /v1/me
suas páginas ──chave publicável──▶  /v1/boot, /v1/contact-auth/*
seus usuários ──Bearer uk_ct_…──▶  /v1/contact/me
```

`/v1/boot`, `/v1/contact-auth/*` e `/v1/contact/*` respondem a **qualquer** origem,
porque são chamados do seu domínio por definição. CORS não é o portão ali: a
[lista de origens permitidas](/pt-br/guides/api-keys#a-lista-de-origens-permitidas)
da chave publicável é, e nenhum cookie viaja nessas requisições.

Todo o resto continua na allowlist estreita.

## Autorização é uma consulta

Um usuário pertence a uma organização por um **vínculo**. O vínculo carrega um
**papel**. Um papel é um conjunto de **permissões**.

O *catálogo* de permissões vive em Go — o conjunto de coisas que o sistema sabe
fazer é um fato do código. *Quem tem o quê* vive no banco, então uma organização
pode definir os próprios papéis sem esperar um deploy.

Resolver uma sessão resolve tudo isso numa ida só: identidade, organização ativa,
papel, permissões. O JOIN do vínculo **é** a autorização, o que tem duas
consequências que vale internalizar:

* revogar um vínculo invalida aquela sessão imediatamente, sem cache para esperar;
* um código de organização que alguém cole na URL não casa com linha nenhuma, a
  menos que a pessoa seja membro.

<Note>
  A organização viaja na URL (`/{org_code}/…`) e no header `X-Organization-Id`. Ela
  **identifica**; não autoriza. Trocar de organização é navegação, não mutação.
</Note>

No painel, `can(session, PERMISSIONS.x)` esconde o que um papel não pode fazer.
Isso é gentileza. `requirePermission` na API é a aplicação da regra.

## Ambientes atravessam tudo

Toda organização tem exatamente dois: **live** e **test**, criados junto com ela e
não criáveis nem deletáveis. As tabelas do plano de clientes carregam
`environment_id` ao lado de `organization_id`.

Uma credencial de máquina resolve o ambiente a partir da linha da chave armazenada,
em toda requisição. Não existe parâmetro de ambiente nessa superfície, nunca — o
que faz de "apontei o staging para produção" algo que não pode acontecer.

O painel é a exceção, e deliberadamente: staff é autorizado na organização inteira,
então as leituras do painel recebem `?environment=live|test` como um parâmetro
explícito de **visão**.

## Capacidades vêm na resposta da sessão

`GET /v1/session` carrega `two_factor_available` e `uploads_available`. Elas
descrevem o **servidor**, não a sua organização: um cliente as lê para esconder o
que a plataforma não pode fazer naquele momento, em vez de descobrir por um `501`
no meio de um fluxo.

`GET /health` responde o estado das dependências. Só o banco muda o código de
status.

## Um barramento interno de eventos

Mutações de domínio publicam num outbox **na mesma transação das linhas que
descrevem**, então um evento nunca descreve uma escrita que sofreu rollback. Um
commit cutuca a fila para drenar; uma varredura periódica é o que garante a drenagem
quando toda cutucada se perde.

Veja [Eventos de domínio](/pt-br/guides/events).
