No Studio Cloud, os recursos corporativos são liberados por uma assinatura Enterprise. Instalações auto-hospedadas não têm assinatura, então elas são liberadas por configuração de ambiente.
Há duas partes para fazer isso certo, e pular a segunda é o motivo mais comum de os recursos parecerem não fazer nada:
- Habilite os recursos com
ENTERPRISE_ENABLED. - Dê a eles uma organização à qual se aplicar. Whitelabeling, redação de PII, grupos de permissões, data drains e escopo de auditoria leem todos suas configurações da organização proprietária do workspace. Uma instalação em que todos trabalham em workspaces pessoais não tem organização de onde tirar essas configurações.
Habilite o conjunto de recursos
Defina a chave principal e sua variável equivalente no cliente. As duas são obrigatórias — o valor do servidor decide o acesso, e o valor NEXT_PUBLIC_ decide o que a interface de configurações mostra.
ENTERPRISE_ENABLED=true
NEXT_PUBLIC_ENTERPRISE_ENABLED=trueIsso liga organizações, grupos de permissões, SSO, whitelabeling, logs de auditoria, políticas de sessão, retenção de dados, data drains, workspace forks, o direito ao Sandbox e a inbox. Os sandboxes continuam indisponíveis até que o provider remoto e a base dedicada de Function estejam configurados.
Desligando um recurso
Cada recurso mantém sua própria flag, e uma flag definida explicitamente sempre vence a chave principal. Para rodar o conjunto sem data drains:
ENTERPRISE_ENABLED=true
NEXT_PUBLIC_ENTERPRISE_ENABLED=true
DATA_DRAINS_ENABLED=false
NEXT_PUBLIC_DATA_DRAINS_ENABLED=falseAs flags individuais também funcionam por conta própria, se você preferir habilitar um recurso por vez e deixar a chave principal sem definir.
| Recurso | Variável do servidor | Variável do cliente |
|---|---|---|
| Tudo abaixo | ENTERPRISE_ENABLED | NEXT_PUBLIC_ENTERPRISE_ENABLED |
| Organizações | ORGANIZATIONS_ENABLED | NEXT_PUBLIC_ORGANIZATIONS_ENABLED |
| Grupos de permissões | ACCESS_CONTROL_ENABLED | NEXT_PUBLIC_ACCESS_CONTROL_ENABLED |
| Login com SAML e OIDC | SSO_ENABLED | NEXT_PUBLIC_SSO_ENABLED |
| Marca personalizada | WHITELABELING_ENABLED | NEXT_PUBLIC_WHITELABELING_ENABLED |
| Logs de auditoria | AUDIT_LOGS_ENABLED | NEXT_PUBLIC_AUDIT_LOGS_ENABLED |
| Políticas de sessão | SESSION_POLICIES_ENABLED | NEXT_PUBLIC_SESSION_POLICIES_ENABLED |
| Exclusão por retenção de dados | DATA_RETENTION_ENABLED | NEXT_PUBLIC_DATA_RETENTION_ENABLED |
| Data drains | DATA_DRAINS_ENABLED | NEXT_PUBLIC_DATA_DRAINS_ENABLED |
| Workspace forks | FORKING_ENABLED | — |
| Inbox do Studio Mailer | INBOX_ENABLED | NEXT_PUBLIC_INBOX_ENABLED |
| Sandboxes | SANDBOXES_ENABLED | NEXT_PUBLIC_SANDBOXES_ENABLED |
Os sandboxes também precisam de um provider de execução remota e de uma imagem base dedicada de Function. Construa e configure essa base antes de habilitar a interface; sandboxes personalizados do workspace instalam seus pacotes em cima dela.
Para o E2B:
E2B_API_KEY=... \
bun run apps/core-api/scripts/build-function-e2b-template.ts \
--name studio-function
SANDBOX_PROVIDER=e2b
E2B_ENABLED=true
E2B_API_KEY=...
E2B_FUNCTION_TEMPLATE_ID=<studio-function-template>:<studio-function-build-id>
E2B_FUNCTION_TEMPLATE_GENERATION=<release-epoch-ms>
SANDBOXES_ENABLED=true
NEXT_PUBLIC_SANDBOXES_ENABLED=trueO builder usa a base code-interpreter-v1 mantida pelo E2B, atribui uma nova
geração de release e imprime os dois valores de runtime. O --generation
continua disponível para automação de release, e o --base-template aceita uma
sobreposição de base imutável quando uma instalação deliberadamente mantém a sua.
Para o Daytona, use o ID de snapshot imutável impresso pelo builder. A API key precisa de
write:snapshots para construir e de write:sandboxes para executar:
DAYTONA_API_KEY=... \
bun run apps/core-api/scripts/build-function-daytona-snapshot.ts \
--name studio-function-2026-08-03 \
--parity-manifest /tmp/function-sandbox-manifest.json
SANDBOX_PROVIDER=daytona
DAYTONA_API_KEY=...
DAYTONA_FUNCTION_SNAPSHOT_ID=<snapshot-uuid>
SANDBOXES_ENABLED=true
NEXT_PUBLIC_SANDBOXES_ENABLED=trueSANDBOXES_ENABLED concede o direito de auto-hospedagem no lado do servidor.
NEXT_PUBLIC_SANDBOXES_ENABLED projeta a prontidão do provider para o navegador e
expõe o Shell mais o gerenciamento de Sandboxes personalizados. Defina a flag
pública somente depois que o provider selecionado tiver credenciais e uma base
imutável de Function válida configurada. O valor de linguagem do Function nunca
depende dessas flags, então um block Python salvo não pode ser silenciosamente
serializado ou executado como JavaScript.
JavaScript sem import ou require não usa esse provider remoto e continua
rodando na VM isolada local quando todas as flags de Sandbox estão desligadas.
Python, Shell, JavaScript com imports externos e Sandboxes personalizados
selecionados falham com um erro explícito de configuração até que a base remota
de Function esteja pronta.
As ferramentas function_execute e run_code do Chat usam uma imagem de shell
separada, inclusive para JavaScript sem imports. Se a instalação usa essas
ferramentas de código, configure também a imagem produzida pelo processo de
release da imagem de shell para o provider selecionado:
# E2B
MOTHERSHIP_E2B_TEMPLATE_ID=<mothership-shell-template-ref>
# Daytona
DAYTONA_SHELL_SNAPSHOT_ID=<mothership-shell-snapshot-ref>Esses valores são selecionados apenas para as chamadas de ferramentas de código
do Studio no workflow e do Chat no workspace. Eles nunca substituem nem servem
de fallback para E2B_FUNCTION_TEMPLATE_ID ou
DAYTONA_FUNCTION_SNAPSHOT_ID; os blocks Function e os sandboxes personalizados
do workspace continuam usando a base dedicada de Function.
Use o E2B como linha de base do release antes de construir ou promover o Daytona:
# 1. Verify the exact E2B Function build and capture its accepted package/runtime surface.
E2B_ENABLED=true \
E2B_API_KEY=... \
E2B_FUNCTION_TEMPLATE_ID=<studio-function-template>:<studio-function-build-id> \
E2B_FUNCTION_TEMPLATE_GENERATION=<release-epoch-ms> \
SANDBOX_PARITY_MANIFEST_OUT=/tmp/function-sandbox-manifest.json \
bun run apps/core-api/scripts/verify-sandbox-parity.ts
# 2. Pin Daytona's reconstructed packages to that accepted E2B manifest.
DAYTONA_API_KEY=... \
bun run apps/core-api/scripts/build-function-daytona-snapshot.ts \
--name studio-function-2026-08-03 \
--parity-manifest /tmp/function-sandbox-manifest.json
# 3. Verify the immutable Daytona snapshot against the same baseline before promotion.
SANDBOX_PROVIDER=daytona \
DAYTONA_API_KEY=... \
DAYTONA_FUNCTION_SNAPSHOT_ID=<snapshot-uuid> \
SANDBOX_PARITY_MANIFEST_BASELINE=/tmp/function-sandbox-manifest.json \
bun run apps/core-api/scripts/verify-sandbox-parity.tsE2B_FUNCTION_TEMPLATE_ID e DAYTONA_FUNCTION_SNAPSHOT_ID falham de forma
segura quando não estão definidos ou são mutáveis. O valor do E2B precisa ser
uma referência exata <template>:<build-id>, e
E2B_FUNCTION_TEMPLATE_GENERATION precisa ser o valor monotônico impresso pelo
mesmo build. O valor do Daytona precisa ser um ID de snapshot, não um nome. O Studio não
faz fallback para MOTHERSHIP_E2B_TEMPLATE_ID nem
DAYTONA_SHELL_SNAPSHOT_ID; as imagens de shell, Function, documento e Pi têm
contratos de pacote e cadências de release separados.
Atribua a cada base de Function do E2B promovida uma geração maior que a de qualquer instalação anterior. Um rollback é uma nova promoção e, portanto, também precisa de uma geração nova e maior; não reutilize a geração do release antigo.
A retenção de dados é o único recurso que exclui dados. Sua flag controla a passagem de limpeza, não a tela de configurações — as janelas de retenção são sempre configuráveis. Nada é excluído até você habilitá-la, e mesmo então somente conforme as janelas que você configurou explicitamente. O Studio nunca aplica os padrões do plano hospedado a uma instalação auto-hospedada.
Escolha um modelo de organização
Padrão 1: uma organização para toda a instância
Melhor quando todos na instalação pertencem à mesma empresa. Defina um nome e todo usuário entra nessa organização automaticamente no cadastro, com seus workspaces criados sob a propriedade da organização.
INSTANCE_ORG_NAME="Acme Inc"Opcionalmente, fixe o slug e o owner:
INSTANCE_ORG_SLUG=acme-inc
INSTANCE_ORG_OWNER_EMAIL=admin@acme.comA organização é criada na primeira vez que um usuário se cadastra. Se INSTANCE_ORG_OWNER_EMAIL não estiver definido, ou apontar para um usuário que ainda não existe, o primeiro usuário a se cadastrar se torna o owner; transfira a propriedade depois com a Admin API. O provisionamento é idempotente e seguro em múltiplas réplicas.
O modo de organização de instância só se aplica quando o billing está desativado. Com o billing ativado, as organizações são criadas pelo fluxo normal de assinatura e essas variáveis são ignoradas.
Instalações existentes
Usuários e workspaces criados antes de você definir INSTANCE_ORG_NAME ficam onde estão. Mova-os de uma vez com o script de backfill, que adiciona todo usuário à organização e anexa seus workspaces:
# Preview
DATABASE_URL=... INSTANCE_ORG_NAME="Acme Inc" \
bun run apps/core-api/scripts/consolidate-users-into-organization.ts
# Apply
DATABASE_URL=... INSTANCE_ORG_NAME="Acme Inc" \
bun run apps/core-api/scripts/consolidate-users-into-organization.ts --applyEle roda em modo de simulação, a menos que você passe --apply, e é seguro executar novamente. Usuários que já pertencem a outra organização são reportados e ignorados, já que um usuário só pode pertencer a uma.
Padrão 2: várias organizações que você mesmo gerencia
Melhor quando uma instalação atende vários times que não devem ver os dados uns dos outros. Deixe INSTANCE_ORG_NAME sem definir e provisione as organizações pela Admin API.
Defina uma chave de admin primeiro:
ADMIN_API_KEY=$(openssl rand -hex 32)Crie uma organização
O owner não pode já pertencer a outra organização.
curl -X POST https://studio.example.com/api/v1/admin/organizations \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Inc", "ownerId": "user_123", "slug": "acme-inc"}'Adicione membros
curl -X POST https://studio.example.com/api/v1/admin/organizations/$ORG_ID/members \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userId": "user_456", "role": "member"}'Mova um workspace para a organização
Recursos com escopo de organização só se aplicam a workspaces de propriedade da organização.
curl -X POST https://studio.example.com/api/v1/admin/dashboard/workspaces/$WORKSPACE_ID/move \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"destinationOrganizationId\": \"$ORG_ID\"}"Configure as configurações da organização
Marca, retenção e políticas de sessão podem ser definidas pela API em vez da interface.
curl -X PATCH https://studio.example.com/api/v1/admin/organizations/$ORG_ID/whitelabel \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"brandName": "Acme AI", "hidePoweredByStudio": true}'curl -X PATCH https://studio.example.com/api/v1/admin/organizations/$ORG_ID/data-retention \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"logRetentionHours": 2160}'curl -X PATCH https://studio.example.com/api/v1/admin/organizations/$ORG_ID/session-policy \
-H "x-admin-key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"maxSessionHours": 168, "idleTimeoutHours": 48}'Excluir uma organização exige repetir seu slug, porque a exclusão cascateia para membros, convites e grupos de permissões, e desanexa seus workspaces:
curl -X DELETE "https://studio.example.com/api/v1/admin/organizations/$ORG_ID?confirmSlug=acme-inc" \
-H "x-admin-key: $ADMIN_API_KEY"Verificando se funcionou
Se um recurso está habilitado mas nada aparece, confira estes pontos nesta ordem.
A seção de configurações não aparece. A variável equivalente NEXT_PUBLIC_ não está definida, ou a aplicação não foi reiniciada depois de adicioná-la. As variáveis de cliente são lidas no build e na inicialização.
A seção aparece, mas a API retorna 403. A variável do lado do servidor está faltando, enquanto a equivalente no cliente está definida. Defina as duas.
O recurso está ligado, mas não tem efeito dentro de um workspace. O workspace não pertence a uma organização. Verifique workspace_mode e organization_id:
SELECT id, name, workspace_mode, organization_id FROM workspace;Um workspace com personal ou organization_id nulo não vai receber marca, redação de PII, grupos de permissões nem drains. Use o script de backfill ou o endpoint de movimentação de workspace.
A retenção está configurada, mas nada é excluído. DATA_RETENTION_ENABLED não está definido. Configurar as janelas e rodar a passagem de limpeza são chaves separadas por design.