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

# Configuração do Vault para Self-Hosting

> Como configurar o Supabase Vault para deployments self-hosted do Switchbord — cobrindo requisitos multi-tenant, variáveis de ambiente do worker e solução de problemas de erros do vault.

# Configuração do Vault para Self-Hosting

O Switchbord armazena secrets por workspace no [Supabase Vault](https://supabase.com/docs/guides/database/vault) — uma extensão do Postgres que criptografa secrets no nível do banco de dados. Esta página cobre o que quem faz self-hosting precisa saber para fazer o vault funcionar corretamente, especialmente ao executar múltiplos workspaces.

***

## Visão geral: Dois Modos de Resolução de Secrets

O Switchbord suporta duas abordagens para fornecer secrets em runtime, como o token de acesso da Meta:

**Modo Vault (padrão, recomendado)**
Secrets são armazenados por workspace em `vault.secrets`, vinculados a cada workspace via `app_private.workspace_secret_bindings`. O worker os lê em runtime usando funções RPC do Supabase. Esta é a abordagem correta para qualquer deployment com mais de um workspace.

**Fallback por variável de ambiente (apenas single-workspace)**
Se `SELF_HOSTED=true` estiver definido e uma leitura do vault falhar, o worker recorre a `WHATSAPP_META_ACCESS_TOKEN` a partir do ambiente. Isso é uma conveniência para operadores executando um único workspace que desejam pular completamente a configuração do vault.

<Warning>
  `SELF_HOSTED=true` contorna as consultas ao vault e serve uma credencial compartilhada a todos os workspaces. Se seu deployment tiver mais de um workspace, isso fará com que as mensagens do workspace A sejam enviadas usando o token do workspace B. Não use o fallback por variável de ambiente em deployments multi-workspace.
</Warning>

***

## Por que Deployments Multi-Tenant Devem Usar o Vault

Quando o worker processa uma mensagem de saída, ele resolve o token de acesso da Meta procurando o workspace que é dono da conversa. No modo vault, ele chama `sb_read_workspace_secret(workspace_id, 'meta-access-token')` — uma consulta com escopo definido que retorna apenas o secret pertencente àquele workspace.

Com `SELF_HOSTED=true`, a consulta é completamente pulada e o valor da variável de ambiente é retornado independentemente de qual workspace está fazendo a requisição. Não há isolamento por workspace. Um segundo workspace adicionado depois usará silenciosamente o token do primeiro workspace, o que falhará ou — pior — terá sucesso com a conta errada.

<Info>
  Se você inicialmente fez o deployment com `SELF_HOSTED=true` para um único workspace e agora está adicionando um segundo workspace, você deve remover essa flag, configurar os secrets do vault para ambos os workspaces e reiniciar o worker antes que o segundo workspace funcione corretamente.
</Info>

***

## Como os Secrets Fluem pelo Sistema

<Steps>
  <Step title="O operador salva uma credencial em Settings">
    O operador abre Settings → Provider e insere um token de acesso da Meta (ou uma API key para um provider de IA). O app web do Switchbord envia isso para `/api/settings/secrets`.
  </Step>

  <Step title="A API armazena o secret no Supabase Vault">
    A rota da API chama `storeWorkspaceSecret(workspaceId, secretKind, value)`, que chama `vault.create_secret()` e insere uma linha em `app_private.workspace_secret_bindings` mapeando o workspace ID e o secret kind para o vault secret ID.
  </Step>

  <Step title="O worker lê o secret no momento do envio">
    Quando o worker está prestes a enviar uma mensagem, ele chama a função RPC do Supabase `sb_read_workspace_secret` com o workspace ID e o secret kind. A função é executada com privilégios `SECURITY DEFINER`, lê de `vault.secrets`, e retorna o valor decriptado. Nenhuma conexão direta com o banco de dados é necessária a partir do worker.
  </Step>
</Steps>

O worker nunca armazena secrets em cache entre requisições. Cada operação de envio dispara uma nova leitura do vault, de modo que rotacionar um secret em Settings tem efeito imediato.

***

## Requisitos de Configuração do Supabase

### Extensão Vault

O Supabase Vault é habilitado por padrão em todos os projetos do Supabase Cloud. Se você está executando uma instância self-hosted do Supabase, confirme que a extensão está habilitada:

```sql theme={null}
-- Execute no seu SQL editor do Supabase
SELECT * FROM pg_extension WHERE extname = 'supabase_vault';
```

Se a consulta não retornar nenhuma linha, habilite-a:

```sql theme={null}
CREATE EXTENSION IF NOT EXISTS supabase_vault SCHEMA vault;
```

<Info>
  As migrações do Switchbord também criam o schema `app_private` e a tabela `workspace_secret_bindings` automaticamente quando você executa `supabase db push` ou aplica migrações. Você não precisa criá-los manualmente.
</Info>

### Funções RPC

O worker usa duas funções wrapper RPC públicas que devem estar presentes no seu banco de dados:

* `sb_read_workspace_secret(p_workspace_id uuid, p_kind text)` — retorna o valor do secret decriptado para um workspace e secret kind
* `sb_resolve_workspace_by_verify_token(p_verify_token text)` — retorna o workspace ID que corresponde a um determinado verify token da Meta

Essas funções são criadas pelas migrações do Switchbord. Se você encontrar erros de `function does not exist`, execute novamente suas migrações contra o banco de dados de destino:

```bash theme={null}
supabase db push --db-url "$SUPABASE_DB_URL"
```

***

## Armazenando Secrets

Há duas formas de popular secrets no vault para um workspace:

**UI de Settings (recomendado)**
Abra Settings → Provider no app web do Switchbord e insira as credenciais diretamente. A UI chama a API de escrita do vault automaticamente. Nenhum acesso direto ao banco de dados é necessário.

**SQL editor do Supabase Dashboard**
Para configuração inicial ou carregamento em massa, você pode inserir secrets diretamente:

```sql theme={null}
-- Insira um secret e receba de volta o seu vault ID
SELECT vault.create_secret(
  'your-token-value-here',
  'meta-access-token for workspace abc123',
  null
) AS vault_secret_id;

-- Depois vincule-o a um workspace
INSERT INTO app_private.workspace_secret_bindings
  (workspace_id, secret_kind, vault_secret_id)
VALUES
  ('your-workspace-uuid', 'meta-access-token', 'the-vault-secret-id-from-above');
```

<Warning>
  Não insira secrets em texto plano em nenhuma tabela de schema público. Sempre use `vault.create_secret()` para que o valor seja criptografado antes do armazenamento.
</Warning>

***

## Variáveis de Ambiente do Worker

O worker precisa apenas de uma service role key do Supabase para ler do vault. Nenhuma conexão direta com o banco de dados é necessária para leituras.

**Obrigatórias:**

| Variável                               | Descrição                                                                |
| -------------------------------------- | ------------------------------------------------------------------------ |
| `NEXT_PUBLIC_SUPABASE_URL`             | A URL do seu projeto Supabase, ex.: `https://xxxx.supabase.co`           |
| `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY` | Chave anon do Supabase                                                   |
| `SUPABASE_SECRET_KEY`                  | Chave de service role do Supabase — usada para leituras do vault via RPC |
| `WHATSAPP_PLATFORM_DATA_MODE`          | Defina como `supabase`                                                   |
| `WORKER_POLL_INTERVAL_MS`              | Intervalo de polling em milissegundos, ex.: `5000`                       |

**Opcionais:**

| Variável                     | Descrição                                                                                                                                                                           |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SUPABASE_DB_URL`            | String de conexão direta com o Postgres. Necessária apenas se você estiver usando escritas no vault diretamente a partir do worker (não é o típico — o app web trata das escritas). |
| `SELF_HOSTED=true`           | Habilita o fallback por variável de ambiente para deployments single-workspace. Não defina isso se você tiver mais de um workspace.                                                 |
| `WHATSAPP_META_ACCESS_TOKEN` | Necessário apenas quando `SELF_HOSTED=true` está definido. O token de acesso da Meta usado como fallback.                                                                           |

<Tip>
  Se você está fazendo deploy no Railway, defina essas como service variables no painel de ambiente do seu serviço worker. O worker as capturará no próximo deploy sem qualquer alteração de código.
</Tip>

***

## Referência de Tipos de Secret

| Secret Kind              | Finalidade                                                                         |
| ------------------------ | ---------------------------------------------------------------------------------- |
| `meta-access-token`      | Token de acesso da Meta (WhatsApp Business API) para enviar mensagens              |
| `webhook-signing-secret` | Secret de assinatura de payload de webhook da Meta para verificação de requisições |
| `meta-verify-token`      | Token de verificação de webhook da Meta                                            |
| `openai-api-key`         | API key da OpenAI para geração de rascunhos (drafts) com IA                        |
| `anthropic-api-key`      | API key da Anthropic para geração de IA baseada em Claude                          |
| `openrouter-api-key`     | API key do OpenRouter para roteamento de IA multi-model                            |

***

## Solução de Problemas

### `vault_unavailable`

A extensão vault não está habilitada, ou o schema `app_private` está ausente.

* Confirme que a extensão vault existe (veja a verificação SQL acima)
* Execute novamente as migrações do Switchbord: `supabase db push`
* Confirme que o `SUPABASE_SECRET_KEY` do worker é uma service role key válida, não a chave anon

### `vault_read_error`

A função RPC existe, mas retornou um erro ao tentar ler um secret.

* Verifique se `sb_read_workspace_secret` foi criada com sucesso pelas suas migrações
* Confirme que `SUPABASE_SECRET_KEY` tem permissões de service role (a chave anon não pode chamar funções `SECURITY DEFINER` que verificam por `service_role`)
* No SQL editor do Supabase, verifique se a função existe: `\df sb_read_workspace_secret`

### `vault_secret_missing`

A função executou com sucesso, mas não encontrou vínculo para o workspace e secret kind solicitados. O secret nunca foi armazenado.

* Abra Settings → Provider para o workspace afetado e salve a credencial ausente
* Ou insira o vínculo manualmente usando o SQL acima
* Verifique se o workspace ID em `app_private.workspace_secret_bindings` corresponde ao workspace que está fazendo a requisição

### O worker envia mensagens com a conta errada

Este é um sintoma de `SELF_HOSTED=true` estar definido em um deployment multi-workspace. A credencial de fallback por variável de ambiente está sendo usada em vez dos secrets do vault por workspace. Remova `SELF_HOSTED=true` do ambiente do worker e garanta que todos os workspaces tenham seus secrets configurados no vault.

***

## Guias Relacionados

* [Vault Architecture](/pt-BR/security/vault-architecture) — internals, design de schema e detalhes de criptografia
* [Secrets and Encryption](/pt-BR/security/secrets-and-encryption) — política de segurança e controles de acesso
* [Meta Credential Setup](/pt-BR/operations/meta-credential-setup) — como obter as credenciais que você armazenará no vault
* [Webhooks](/pt-BR/operations/webhooks) — configuração do token de verificação de webhook da Meta
