Aponte a MeetingBaaS para um object storage que é seu e cada gravação, transcrição, trecho de áudio e log vai para lá. Duas credenciais, uma verificação de acesso de verdade e um relato honesto do que você abre mão.

"Onde fica a gravação?" é a pergunta que trava negociações enterprise. Para um banco, um hospital ou qualquer um que opere sob um requisito de residência de dados, "nos nossos buckets, criptografada" não é uma resposta que dê para levar ao time de compliance.
Então agora você pode nos dar um bucket. Todo artefato que os seus bots produzirem daquele momento em diante, gravações, trechos de áudio, transcrições, capturas de tela e logs, é gravado em um armazenamento que é seu, com credenciais que você emitiu e pode revogar.
O Bring Your Own Storage está disponível em todos os planos, incluindo Pay as You Go. Não há restrição de upgrade para o recurso de armazenamento.
GET /v2/storage-config your current configuration, 404 if unset
PUT /v2/storage-config set or replace it
POST /v2/storage-config/test re-run the access check
DELETE /v2/storage-config back to MeetingBaaS storageOu em Configurações → Armazenamento no dashboard, que utiliza os mesmos quatro handlers por trás da autenticação de sessão.
Duas credenciais, separadas pelo local onde ficam fisicamente
Esta é a decisão de design que eu defenderia com mais afinco, então vou começar por ela.
Você fornece dois pares de chaves de acesso, não um:
| Permissões | Onde fica | |
|---|---|---|
| ingest | PutObject, PutObjectTagging, AbortMultipartUpload | Repassada ao bot de gravação. Sai da nossa rede. |
| service | GetObject, ListBucket, PutObject, DeleteObject | Somente no nosso servidor de API. Nunca sai. |
A chave de ingest é a única credencial que vai a algum lugar próximo à sua reunião. Ela é carregada em um pod que executa um navegador headless dentro de uma chamada, que é o lugar mais exposto em que qualquer credencial neste sistema pode estar. Ela pode escrever objetos. Não pode ler um de volta, não pode listar o que está lá e não pode deletar nada. Um pod comprometido pode adicionar lixo a um prefixo, e é essa a extensão total do dano.
A chave de service faz todo o resto: servir suas gravações de volta por meio de URLs assinadas, escrever transcrições quando elas chegam e deletar artefatos quando sua janela de retenção expira.

Vale ser preciso sobre algo aqui. A chave de service não é somente leitura, e descrevê-la dessa forma seria uma mentira que você eventualmente nos pegaria. Ela escreve e deleta, porque a API genuinamente faz as duas coisas. O que a separação oferece é que a credencial no local de risco é a mais fraca. Isso é uma redução real no raio de impacto e não é a mesma coisa que privilégio mínimo em todos os lugares, e eu prefiro dizer exatamente qual deles você está obtendo.
Configurando
{
"endpoint": "https://s3.eu-west-3.amazonaws.com",
"region": "eu-west-3",
"force_path_style": false,
"artifacts_bucket": "acme-meeting-artifacts",
"audio_chunks_bucket": "acme-meeting-audio-chunks",
"logs_bucket": "acme-meeting-logs",
"allow_transient_spill": false,
"ingest_access_key_id": "AKIAIOSFODNN7EXAMPLE",
"ingest_secret_access_key": "...",
"service_access_key_id": "AKIAI44QH8DHBEXAMPLE",
"service_secret_access_key": "..."
}Os três nomes de bucket podem ser todos o mesmo bucket. As chaves de objeto são prefixadas com o id do bot de qualquer forma, então nada colide. force_path_style é o que MinIO, Ceph e a maioria dos gateways self-hosted precisam; AWS e Scaleway funcionam sem ele.
O endpoint precisa ser HTTPS público sem credenciais ou query string nele. Os buckets devem ser privados, pois os artefatos são servidos por meio de URLs assinadas de curta duração, mas precisam ser acessíveis pela internet: os provedores de transcrição buscam o áudio diretamente de uma URL assinada em vez de fazer proxy por nós.
Permita a origem do dashboard no bucket de artefatos enquanto estiver lá. O dashboard busca artefatos e lê o JSON de transcrição em vez de criar links diretos para eles, então essas são leituras cross-origin contra o seu endpoint, e sem uma regra de CORS o navegador descarta a resposta antes que qualquer coisa possa ser renderizada. GET e HEAD da origem do seu dashboard é a regra completa. Vale saber que PutBucketCors fica um nível acima das permissões de objeto que a chave de service possui, então ela precisa de uma credencial de nível proprietário — a chave de service retornando Forbidden é o escopo funcionando, não um bug.
Ler a configuração de volta fornece os dois IDs de chave de acesso, para que você saiba quais credenciais estão em uso, e nenhum dos segredos. Jamais. Substituir uma configuração significa reinserir ambos os segredos, pois não podemos mostrar o que está lá.
A verificação de acesso, e por que ela não é uma formalidade
PUT verifica a configuração antes de armazená-la. Não apenas chamando ListBuckets e considerando encerrado.
A chave de ingest escreve um objeto marcador em cada bucket distinto. Em seguida, a chave de service lê esse objeto, escreve-o e o deleta.
Cada operação é exercida pela credencial que realmente a executará em produção, de modo que a verificação falha da mesma forma que a produção falharia. Todas as quatro são essenciais: o upload de ingest é como as gravações chegam, o upload de service é como as transcrições são escritas, a leitura é como os artefatos são servidos, e a deleção é como a retenção realmente expira qualquer coisa.
A parte genuinamente útil é separar a escrita da leitura. O marcador é escrito por uma credencial e precisa ser encontrado pela outra. Isso detecta a falha que ninguém testa: dois pares de chaves que são individualmente válidos, mas apontados para buckets diferentes ou contas diferentes. Todo health check de credencial única no mundo passa essa configuração, e então você perde uma gravação.
Sem isso, o primeiro sinal de um nome de bucket digitado incorretamente é um upload falho ao final de uma reunião real, depois que o disco local do pod já foi descartado. Quando a verificação falha, exibimos a mensagem do próprio provedor literalmente, em vez de um erro genérico, porque "a chave de ingest não pode escrever no bucket de artefatos" é o valor completo de executá-la.
Você pode executá-la novamente a qualquer momento com POST /v2/storage-config/test, que é o que você deseja após rotacionar chaves ou alterar uma política de bucket.
Uma coisa que ela deliberadamente não consegue detectar: CORS. A verificação é executada em nossos servidores, onde CORS não se aplica, portanto um bucket pode passar em todas as operações acima e ainda recusar entregar uma gravação a um navegador. Essa lacuna é nossa para fechar, e até que o façamos, a observação sobre permitir a origem do dashboard é o que está entre uma verificação verde e um dashboard que não exibe nada.
O invariante do qual tudo o mais deriva
Um bot sempre resolve para o armazenamento para o qual foi escrito, nunca para o que sua equipe está configurada hoje.
Quando um bot é despachado, a configuração de armazenamento em vigor naquele momento é registrada no registro do bot. Cada leitura, cada URL assinada e cada deleção para esse bot são resolvidas por meio do registro. Não pela equipe.
Isso parece um detalhe de implementação. É a razão pela qual o recurso é seguro de ativar, e quatro consequências derivam diretamente disso:
Alterar sua configuração insere uma nova linha e desativa a antiga. Nunca atualiza no lugar. Mutar a linha redirecionaria silenciosamente os artefatos de todos os bots existentes para um bucket onde eles não existem.
DELETE /v2/storage-config apenas altera um flag. Nada é deletado, em nenhum dos lados. Uma deleção definitiva removeria as credenciais que os bots mais antigos ainda precisam e deixaria todas as gravações da sua conta órfãs.
A resolução deliberadamente ignora se uma configuração está habilitada. Desabilitar impede que novos artefatos vão para o seu bucket. Não deve tornar órfãos os que já estão lá.
Uma configuração registrada que não pode ser encontrada gera um erro em vez de recorrer aos nossos buckets. Recorrer ao fallback reportaria seus artefatos como ausentes, e em uma deleção por retenção deixaria silenciosamente seus dados para trás enquanto reportaria sucesso. Falha explícita é o comportamento correto.
A versão prática: configure, altere ou remova quando quiser. Gravações mais antigas continuam funcionando ao longo de tudo isso. A única coisa que torna artefatos antigos ilegíveis é revogar nosso acesso no seu lado, o que é uma decisão sua e funciona exatamente como você esperaria.
A troca que você está realmente fazendo
allow_transient_spill tem como padrão false para equipes em seu próprio armazenamento, o que é o oposto do padrão da nossa plataforma. Aqui está o que isso significa, completamente, porque é o único lugar onde esse recurso tem um custo para você.
Normalmente, quando um upload falha, espelhamos o artefato em nosso próprio volume de rede e um job de reconciliação o envia posteriormente. Esse caminho copia toda a reunião: o vídeo, o áudio, a saída de separação de interlocutores, cada fragmento. Para nossa infraestrutura, em nossa conta.
Que é exatamente o que um cliente de residência de dados se inscreveu para evitar. Pior ainda, clientes de residência atingem esse caminho mais do que clientes da plataforma, porque o bucket deles está fora da nossa rede e geralmente em um provedor completamente diferente.
Então, com allow_transient_spill: false, o fallback é desativado em todos os tipos de bot e no consumidor. Um upload que esgota suas tentativas é reportado como falho, e o artefato é perdido.
Essa é a troca: durabilidade pela residência de dados. Compensamos parcialmente isso dando ao cliente de upload do bot seis tentativas em vez das três padrões do SDK, para que uma instabilidade não custe uma gravação, mas uma interrupção prolongada no seu bucket ainda custará.
Defina como true se preferir manter a gravação do que manter a garantia. O dashboard exibe um aviso explicando precisamente o que você está abrindo mão ao alternar, porque não deve ser uma caixa de seleção que você passa sem ler.
Se gravações estiverem sendo perdidas e seu bucket teve uma interrupção, esse é o primeiro ponto a verificar.
O que isso não faz
Quatro coisas, declaradas antecipadamente para que ninguém as descubra depois.
Nada que já foi gravado é movido. Não há migração. Cada bot continua resolvendo para onde foi escrito, o que torna a ativação segura, e também significa que seu histórico permanece em nossos buckets.
Sem configuração de prefixo por bucket. As chaves são <bot_uuid>/… e isso é fixo.
Isso não torna a residência de dados hermética por si só. Os pods de bot ainda mantêm a gravação no disco local enquanto a reunião está em andamento. O que ele fecha é o único caminho onde sua gravação fica em repouso na infraestrutura compartilhada do MeetingBaaS.
Os ativos do dashboard permanecem em nossos buckets. Logotipos, avatares de usuário e anexos de suporte não são dados de reunião e não são cobertos.
Revertendo
DELETE /v2/storage-config, seguro a qualquer momento. Novos bots voltam imediatamente para o nosso armazenamento. Os bots existentes continuam resolvendo para os seus buckets e continuam funcionando.
- Seis Provedores de Transcrição, Uma Única API — combine a região de um bucket com a região de um provedor para residência de ponta a ponta
- Self-hosting do Meeting BaaS — se ser dono do bucket ainda não for longe o bastante
- Referência da API do Meeting BaaS
Se você está trabalhando com um requisito de residência de dados e quer saber exatamente quais componentes acessam o quê, fale conosco. Daremos a mesma resposta que fornecemos aqui.