Um pacote de site é uma cópia portátil do modelo de conteúdo, do conteúdo, do histórico editorial, da apresentação do site, das configurações e dos arquivos de mídia de um site EmDash. Importe um pacote de site para mover um site para outro deployment EmDash, inclusive um que use um banco de dados diferente: SQLite, PostgreSQL ou Cloudflare D1.
Uma importação escreve em um site novo cuja área de conteúdo está vazia. O EmDash verifica o pacote inteiro antes de escrever qualquer coisa, executa a importação em pequenos passos retomáveis, lê de volta o site importado e emite um recibo quando o resultado corresponde ao pacote.
Um pacote de site não contém usuários, credenciais nem segredos. Ele contém, porém, todas as entradas e comentários do site, incluindo os endereços de e-mail de autores e comentaristas. Armazene e envie-o com o mesmo cuidado de um backup de banco de dados.
Escolher o tipo certo de cópia
| Mecanismo | Finalidade | Importável | Arquivos de mídia | Usuários e segredos |
|---|---|---|---|---|
| Arquivo seed | Inicializar um modelo de conteúdo e conteúdo de exemplo | Sim, com semântica de seed | Não | Não |
| Snapshot de preview | Preencher a renderização isolada de preview | Somente preview | Não | Não |
| Backup JSON | Inspecionar estado selecionado com forma de banco de dados | Não | Não | Não |
| Backup bruto de banco de dados e mídia | Recuperar um deployment | Restauração no mesmo tipo de banco | Cópia separada | Sim |
| Pacote de site | Mover um site para outro site EmDash | Sim, para um site vazio | Sim | Não. Apenas nomes e e-mails dos autores |
Use um backup bruto de banco de dados para recuperar um deployment após perda de dados. Use um pacote de site para criar uma nova cópia de um site em outro lugar.
O que um pacote de site contém
Um pacote de site contém:
- collections, campos, tipos de bloco com todas as versões, definições de taxonomia, definições de relação e definições de campo de byline;
- todas as entradas de conteúdo em todos os locales, incluindo rascunhos, entradas agendadas, entradas na lixeira, histórico de revisões e grupos de tradução;
- termos de taxonomia e atribuições de termos, bylines e créditos, referências de conteúdo e registros SEO;
- menus e itens de menu, áreas de widgets e widgets, seções e redirecionamentos;
- comentários e reações a comentários, a menos que a exportação desative os comentários;
- pastas de mídia, metadados de mídia e os bytes de cada arquivo de mídia pronto; e
- as configurações portáteis do site listadas abaixo.
O pacote armazena valores JSON, como campos JSON e Portable Text, com as chaves dos objetos ordenadas. Portanto, um valor importado pode listar as chaves em ordem diferente da origem. Os valores, de resto, permanecem inalterados.
Configurações portáteis
Somente estas configurações são exportadas: site:title, site:tagline, site:logo, site:favicon, site:postsPerPage, site:dateFormat, site:timezone, site:social, site:seo, emdash:site_title, emdash:site_tagline e emdash:locale.
O site de destino mantém sua própria URL (site:url e emdash:site_url), ID do site, estado de configuração e configurações de backup. Uma importação nunca as sobrescreve.
O plano de importação pergunta se deve manter o título e o slogan do destino, escritos pelo assistente de configuração, ou usar os valores do pacote. O padrão usa os valores do pacote.
Principais
Uma conta de usuário nunca se move com um pacote. Para cada usuário de origem ao qual conteúdo, revisões, mídia, bylines ou comentários fazem referência, o pacote carrega um principal: o ID do usuário, o nome de exibição e o endereço de e-mail. Um principal não tem papel, senha, passkey, sessão nem token.
Durante a importação, você mapeia cada principal para um usuário no site de destino ou o deixa sem mapeamento. Veja mapear autores para usuários de destino.
Comentários
Os comentários incluem o nome e o e-mail do autor, o corpo, o status, o encadeamento, os carimbos de data/hora e os metadados de moderação. O hash do endereço IP e o user agent não são exportados.
As reações mantêm suas contagens. O exportador substitui cada hash de votante por um novo valor aleatório, de modo que o destino não possa associar uma reação ao visitante que a fez.
O que um pacote de site deixa de fora
Um pacote de site nunca contém:
- usuários, sessões, passkeys, contas OAuth, domínios permitidos, tokens de API, clientes OAuth, códigos de autorização ou códigos de dispositivo;
- armazenamento, estado ou configurações de plugins, incluindo segredos de plugins;
- configurações além das portáteis, como o segredo de assinatura de preview;
- logs de auditoria, limites de taxa, bloqueios de edição, estado de tarefas agendadas, o log 404 ou o histórico de migração;
- registros de uso de mídia e índices de busca, que a importação reconstrói;
- chaves de armazenamento, nomes de bucket, nomes de banco de dados ou nomes de binding da origem; ou
- mídia que não está pronta, como um upload incompleto.
Mídia de um provedor externo permanece externa. O pacote mantém a referência, mas os arquivos do provedor não são copiados.
Preparar o site de destino
Importe para um site que atenda a todos os requisitos abaixo. Quando o conteúdo, os locales, o limite de upload ou o formato suportado do destino não cabem no pacote, a análise reporta um bloqueio.
- Uma conta de administrador. A importação é executada como administrador autenticado ou com um token de API. Crie o administrador do destino durante a configuração.
- Um backend de armazenamento. A origem e o destino precisam de armazenamento configurado. O EmDash prepara os arquivos do pacote nele.
- Nenhum conteúdo. O destino não deve conter entradas (incluindo as da lixeira), revisões, mídia ou pastas de mídia, bylines ou campos de byline, comentários, redirecionamentos, atribuições de termos, relações, registros SEO, seções criadas no admin, nem collections ou tipos de bloco criados após a configuração. Um site configurado a partir de qualquer template oficial atende. O que a configuração criou é o scaffold de configuração: as collections e tipos de bloco semeados, as definições de taxonomia e seus termos não atribuídos, os menus e seus itens, as áreas de widgets e seus widgets, e as seções do tema. O plano lista o scaffold, e a importação o remove depois que você confirma o plano.
- Todos os locales que o pacote usa. Adicione cada locale do pacote à configuração de i18n do destino. Um site sem configuração de i18n aceita apenas
en. Os locales são comparados sem distinção de maiúsculas/minúsculas, e a importação escreve cada locale com a capitalização configurada no destino, declarada comolocale_recased. - Um limite de upload grande o suficiente. Cada arquivo de mídia deve caber no
maxUploadSizedo destino, que por padrão é 50 MiB. - Versão de formato
1. O destino deve oferecer suporte à versão de formato do pacote e a todos os recursos exigidos.
A solicitação a seguir retorna as versões de formato, recursos e limites suportados. Seu objeto portableDomain indica se o site pode receber uma importação e, se não puder, por quê.
curl https://new.example.com/_emdash/api/admin/transfer/capabilities \
-H "Authorization: Bearer $EMDASH_TOKEN"
Exportar um site
Uma exportação lê o site em passos limitados e escreve o pacote no armazenamento do site. Antes de terminar, o exportador valida o pacote concluído da mesma forma que uma importação. Quando uma escrita no site tem sucesso durante uma exportação, o exportador recomeça. Obter ou renovar um bloqueio de edição de entrada não conta como escrita. Após três tentativas, falha com TRANSFER_EXPORT_CONCURRENT_WRITES.
Os arquivos da exportação permanecem disponíveis por sete dias após a criação da exportação. Depois disso, um download retorna TRANSFER_EXPIRED.
Exportar no admin
-
Abra Settings → Transfer. A página está disponível para administradores.
-
Na seção Export, desative Include comments para deixar comentários e reações de fora.
-
Selecione Export site. A página mostra o progresso da exportação. Mantenha a página aberta; se sair, a exportação continua quando você voltar.
-
Quando Export ready aparecer, selecione Download package e escolha onde salvar o arquivo
.emdash. A página mostra quantos arquivos e bytes foram baixados, e Stop cancela o download.
A seção também mostra o digest do pacote, o número de registros de cada tipo e as exportações recentes do site, cada uma com seu próprio botão de download até expirar.
Download package busca a exportação arquivo a arquivo, confere o tamanho e o digest SHA-256 de cada arquivo com o manifesto e monta o arquivo .emdash no navegador, funcionando em Cloudflare Workers para sites de qualquer tamanho. Se um arquivo não coincidir, o download para com erro. Chrome, Edge e outros navegadores baseados em Chromium gravam o arquivo direto no disco. Outros navegadores mantêm o pacote inteiro na memória até o download terminar; para uma exportação maior que cerca de 500 MB, a página recomenda um navegador baseado em Chromium ou a CLI.
Download as one file pede ao servidor o arquivo em uma única resposta. Serve para sites pequenos. No Cloudflare Workers, um site grande pode exceder os limites de uma única solicitação.
Exportar com a CLI
Faça login no site de origem e exporte-o para um arquivo de pacote:
npx emdash login --url https://example.com
npx emdash site export --url https://example.com --output site.emdash
O comando leva a exportação até o fim, baixa o pacote arquivo a arquivo, confere o tamanho e o digest de cada arquivo e escreve site.emdash. Adicione --no-comments para deixar comentários e reações de fora. Se o comando for interrompido, execute-o de novo com as mesmas opções para retomar a mesma exportação. Veja a referência de emdash site export.
Exportar com a API REST
Cada chamada a advance executa um passo e retorna nextRequestInMs, o atraso antes da próxima chamada. A exportação termina quando nextRequestInMs é null.
Estes exemplos usam um token de acesso pessoal com o escopo transfer:export. Veja escopos de token.
-
Inicie a exportação. Para deixar comentários e reações de fora, envie
{ "comments": false }como corpo. Um cabeçalhoIdempotency-Keyfaz uma solicitação repetida retornar a mesma exportação em vez de iniciar outra. Reutilizar uma chave com opções diferentes falha com409 TRANSFER_IDEMPOTENCY_CONFLICT.curl -X POST https://example.com/_emdash/api/admin/transfer/exports \ -H "Authorization: Bearer $EMDASH_TOKEN" \ -H "Idempotency-Key: move-to-new-host" -
Avance a exportação até que
nextRequestInMssejanull. Espere o número de milissegundos retornado entre as chamadas.operation.progressinforma os passosdoneetotal, osrecordsescritos até agora ebytesDoneebytesTotalquando o tamanho do pacote é conhecido.curl -X POST https://example.com/_emdash/api/admin/transfer/exports/$EXPORT_ID/advance \ -H "Authorization: Bearer $EMDASH_TOKEN" -
Verifique se
operation.stateécomplete. Uma exportaçãofailedtraz o motivo emoperation.errorCode. -
Baixe o pacote como um único arquivo
.emdash:curl -o site.emdash \ https://example.com/_emdash/api/admin/transfer/exports/$EXPORT_ID/archive \ -H "Authorization: Bearer $EMDASH_TOKEN"
Um arquivo .emdash é um arquivo tar sem compressão com manifest.json como primeira entrada. O arquivo transmite todos os ficheiros em uma única resposta. No Cloudflare Workers, um site grande pode exceder os limites de uma única solicitação. Em vez disso, baixe manifest.json de exports/{id}/manifest e cada arquivo de exports/{id}/files/{path}. Cada arquivo baixado é conferido com seu digest registrado enquanto é transmitido. Se os bytes armazenados mudaram após a exportação, o download termina com erro em vez de ser concluído.
Importar um site
Uma importação é criada a partir de um pacote, analisada em um plano e executada somente depois que você confirma esse plano pelo seu digest. Uma importação que ainda não começou a ser executada expira 24 horas após ser criada.
O admin, a CLI e a API REST podem executar todos os passos. Um agente de IA pode analisar e iniciar uma importação já enviada, por meio das ferramentas MCP.
Importar no admin
-
No site de destino, abra Settings → Transfer. A seção Import aparece quando o site pode receber uma importação. Caso contrário, lista o que o site já tem e que impede uma importação.
-
Selecione Choose package file e escolha o arquivo
.emdash. O navegador verifica o pacote e o envia em partes. Nada no site muda durante o upload. Se o upload parar, escolha o mesmo arquivo de novo para continuar de onde parou. -
Quando o upload termina, o site analisa o pacote. Você pode sair da página e voltar depois.
-
Revise a importação: o site de origem, a data de exportação e a versão do EmDash, o tamanho, o digest do pacote e o número de registros de cada tipo. Leia os Blockers e os Warnings, as Differences from the source site, que listam as transformações do plano, e o Starter content that will be removed, agrupado por tipo. Veja revisar o plano de importação.
-
Em Authors, escolha o usuário neste site que deve ser dono do conteúdo de cada autor, ou Don’t map. Autores que coincidem com o e-mail de um usuário são marcados como Matched by email. Veja mapear autores para usuários de destino.
-
Em Site identity, escolha se usa o título e o slogan do pacote ou mantém os deste site.
-
Selecione Start import e confirme. O botão fica desativado enquanto o plano tiver bloqueios. A edição no site fica pausada até a importação terminar.
-
Acompanhe o progresso. Quando a importação termina, a página mostra o recibo com um distintivo Verified e seus digests de recibo, pacote, plano e conteúdo. Selecione Copy receipt para guardar uma cópia do JSON do recibo.
A página também oferece Cancel import desde o upload até a importação terminar, e Abandon import depois que uma importação que começou a escrever falha ou é cancelada. Ambas pedem confirmação. Veja cancelar uma importação e abandonar uma importação incompleta.
Importar com a CLI
Faça login no site de destino e analise o pacote:
npx emdash login --url https://new.example.com
npx emdash site import site.emdash --url https://new.example.com --analyze
O comando verifica localmente o arquivo de pacote inteiro, faz o upload, analisa e imprime o plano com seu digest de plano. Termina com o código 2 quando o plano tem bloqueios. Revise o plano como descrito em revisar o plano de importação.
Para alterar as decisões do plano, execute --analyze de novo com flags de decisão. --map-principal mapeia um principal, por ID ou e-mail, para um usuário de destino por ID ou e-mail, ou para none. --use-target-title e --use-target-tagline mantêm o título e o slogan do destino:
npx emdash site import site.emdash --url https://new.example.com --analyze \
--map-principal editor@example.com=editor@example.com \
--map-principal 01J8ZQ4Y6T2N0D3VJ5R7K9M1PX=none \
--use-target-title
Execute o plano que você revisou passando seu digest:
npx emdash site import site.emdash --url https://new.example.com \
--plan sha256:3f1c… --confirm
O comando executa a importação até o fim e imprime o recibo. Se for interrompido, continue com emdash site import resume <operation-id>. emdash site import status <operation-id> imprime o estado da importação, e emdash site import receipt <operation-id> imprime o recibo de novo. Veja a referência de emdash site import.
Importar com a API REST
O servidor trabalha com os arquivos dentro de um pacote, não com o arquivo .emdash. Descompacte o arquivo primeiro. Ele contém manifest.json, arquivos de índice em index/, arquivos de registros em records/ e arquivos de mídia em media/. O manifesto fixa o tamanho e o digest SHA-256 de cada arquivo, de modo que o digest do pacote identifica o pacote inteiro.
Estes exemplos usam um token com os escopos transfer:analyze e transfer:execute.
-
Crie a importação. Envie os bytes inalterados de
manifest.jsoncomo corpo da solicitação. A resposta contém a operação e a primeira página de arquivos que o servidor ainda precisa.curl -X POST https://new.example.com/_emdash/api/admin/transfer/imports \ -H "Authorization: Bearer $EMDASH_TOKEN" \ -H "Idempotency-Key: move-to-new-host" \ --data-binary @site/manifest.json -
Envie cada arquivo que falta para
imports/{id}/files/{path}. O cabeçalhoContent-Lengthdeve ser igual ao tamanho declarado do arquivo, e os bytes devem coincidir com o digest declarado.curl -X PUT \ https://new.example.com/_emdash/api/admin/transfer/imports/$IMPORT_ID/files/index/000000.ndjson \ -H "Authorization: Bearer $EMDASH_TOKEN" \ --data-binary @site/index/000000.ndjsonEnviar um arquivo de índice declara os arquivos de registro e de mídia que ele lista. Solicite
imports/{id}/missingde novo após cada lote de uploads e continue até que não retorne itens.Enviar um arquivo já armazenado o verifica de novo. Se a cópia armazenada não coincidir mais, o upload a substitui e a resposta reporta
alreadyVerified: false. -
Analise o pacote. Chame
imports/{id}/analyzeaté quenextRequestInMssejanull. A resposta final contém oplane seuplanDigest.curl -X POST https://new.example.com/_emdash/api/admin/transfer/imports/$IMPORT_ID/analyze \ -H "Authorization: Bearer $EMDASH_TOKEN" -
Revise o plano e leia cada bloqueio, aviso e transformação. Veja revisar o plano de importação.
-
Envie decisões se os padrões não forem o que você quer. Cada envio retorna um novo plano e digest de plano. Depois que a execução é solicitada, o plano fica congelado e enviar decisões falha com
409 TRANSFER_INVALID_STATE.curl -X POST https://new.example.com/_emdash/api/admin/transfer/imports/$IMPORT_ID/analyze \ -H "Authorization: Bearer $EMDASH_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "decisions": { "principalMappings": { "01J8ZQ4Y6T2N0D3VJ5R7K9M1PX": null }, "siteTitle": "target" } }' -
Inicie a importação com os digests que você revisou:
curl -X POST https://new.example.com/_emdash/api/admin/transfer/imports/$IMPORT_ID/execute \ -H "Authorization: Bearer $EMDASH_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "packageDigest": "sha256:…", "planDigest": "sha256:…" }' -
Avance a importação até que
nextRequestInMssejanull, esperando o atraso retornado entre as chamadas.curl -X POST https://new.example.com/_emdash/api/admin/transfer/imports/$IMPORT_ID/advance \ -H "Authorization: Bearer $EMDASH_TOKEN" -
Verifique se
operation.stateécompletee, em seguida, leia o recibo emimports/{id}/receipt.
A execução falha com TRANSFER_PACKAGE_DIGEST_MISMATCH ou TRANSFER_PLAN_DIGEST_MISMATCH quando qualquer digest difere do pacote preparado ou do plano atual. Leia o plano atual em imports/{id}/plan, revise-o de novo e tente novamente com seus digests.
Mapear autores para usuários de destino
A análise lista cada principal com seu nome de exibição, e-mail e o número de registros do pacote que o referenciam. Quando exatamente um usuário de destino tem o mesmo e-mail, comparado sem distinção de maiúsculas/minúsculas, o plano sugere esse usuário e mapeia o principal para ele por padrão. Principais sem sugestão começam sem mapeamento.
Para alterar um mapeamento, passe --map-principal para emdash site import --analyze, ou envie principalMappings para o endpoint de análise. Cada mapeamento nomeia um usuário de destino ou deixa o principal sem mapeamento (none na CLI, null na API). O EmDash aplica cada mapeamento a autores de entradas, autores de revisões, quem enviou mídia, vínculos de usuário de byline e autores de comentários.
As referências de um principal sem mapeamento são removidas. Quando um autor sem mapeamento também tinha uma byline vinculada à conta, o importador credita essa byline explicitamente em cada entrada do autor que não tem crédito de byline explícito e cujo locale tem a byline do autor. Assim, o crédito do autor permanece na página.
Dois mapeamentos produzem um bloqueio principal_conflict:
- um principal com mais de uma byline no mesmo locale é mapeado para um usuário; ou
- dois principais que têm bylines no mesmo locale são mapeados para o mesmo usuário.
Um usuário de destino pode ter apenas uma byline por locale. Deixe um principal sem mapeamento ou mapeie os principais para usuários diferentes.
Revisar o plano de importação
Um plano lista o que a importação criará, as decisões que aplicará e três tipos de achados:
- Bloqueios impedem a execução. A execução retorna
TRANSFER_PLAN_BLOCKEDaté que o plano não tenha nenhum. Altere os mapeamentos de principais para resolver umprincipal_conflict. Qualquer outro bloqueio precisa de uma mudança no pacote ou no destino: cancele a importação, faça a mudança e crie uma nova importação. - Avisos descrevem problemas no pacote que não param a importação. São copiados para o recibo.
- Transformações são as diferenças exatas e declaradas entre o site de origem e o site importado. As mudanças do exportador vêm primeiro, depois as da importação. A verificação aplica as transformações da importação ao comparar o site importado com o pacote.
Um plano lista no máximo 500 bloqueios e avisos. Um aviso issues_truncated informa quantos mais foram encontrados.
Bloqueios
| Código | Significado |
|---|---|
package_invalid | Um arquivo ou caminho do pacote falha na validação. |
unsupported_format | O destino não oferece suporte ao formato ou à versão de formato do pacote. |
unsupported_feature | O pacote exige um recurso que o destino não oferece. |
limit_exceeded | Um arquivo ou registro do pacote excede um limite. |
file_missing | Um arquivo declarado do pacote não foi enviado. |
file_mismatch | O tamanho ou o digest de um arquivo do pacote não coincide com a declaração. |
record_invalid | Um registro está malformado ou não é JSON canônico. |
record_count_mismatch | O número de registros de um tipo difere do manifesto. |
record_order_invalid | Os registros estão fora de ordem, ou um pai aparece depois do filho. |
duplicate_id | Dois registros do mesmo tipo compartilham um ID. |
dangling_reference | Um registro referencia um registro que não está no pacote. Isso inclui um campo blocks que nomeia um tipo de bloco ausente no pacote, e um tipo de bloco cuja versão atual falta no pacote. |
reference_cycle | Um termo, comentário ou item de menu é pai de si mesmo. |
media_ref_invalid | O conteúdo referencia um registro de mídia que não está no pacote. |
media_blob_missing | O arquivo de um registro de mídia não está no pacote. |
media_blob_too_large | Um arquivo de mídia é maior que o maxUploadSize do destino. |
target_not_empty | O destino já tem conteúdo. O detail do bloqueio nomeia o que encontrou. |
locale_not_configured | O pacote usa um locale que a configuração de i18n do destino não inclui. |
field_type_unknown | Um campo ou campo de byline usa um tipo que o destino não oferece. |
principal_conflict | Os mapeamentos de principais dariam a um usuário duas bylines no mesmo locale. |
integer_out_of_range | Um inteiro está fora do intervalo de inteiros do banco de destino. O PostgreSQL armazena inteiros em 32 bits. |
value_constraint_violation | Um valor que a API do admin recusaria. Veja a lista abaixo. |
unique_violation | Um registro duplicaria a chave única de outro registro no destino. |
O importador escreve os registros diretamente, então a análise aplica as mesmas verificações que a API do admin aplica ao salvar esses registros. Cada um destes valores é um value_constraint_violation:
- um valor de entrada que não cabe na coluna do campo, um campo obrigatório sem valor, ou um valor para um campo que a collection não tem;
- um redirecionamento cuja origem ou destino não é um caminho do site, cujo tipo não é suportado, cujo padrão de origem é inválido, ou cujo destino usa um parâmetro que a origem não captura;
- um site de byline que não é uma URL
httpouhttps, um valor de campo de byline que não cabe no tipo ou nas opções do campo, ou um campo de byline com mais opções do que um site admite; - um padrão de URL de collection inválido;
- um tipo de bloco com slug reservado, um rótulo vazio ou com mais de 200 caracteres, ou definições de campo que o editor de tipos de bloco recusaria;
- uma URL canônica SEO que não é nem uma URL
httpouhttpsnem um caminho do site; e - uma URL de item de menu com um esquema que menus não permitem.
Avisos
| Código | Significado |
|---|---|
media_provider_external | O conteúdo usa mídia de um provedor externo. A referência é mantida; os arquivos não são copiados. |
media_row_missing | Uma configuração referencia mídia que não está no pacote. |
soft_reference_dangling | Uma referência opcional não resolve para um registro no pacote. |
redirect_loops_unchecked | O pacote tem redirecionamentos demais para verificar loops antes de importar. Um redirecionamento que fecharia um loop é importado desativado. |
issues_truncated | Foram encontrados mais bloqueios ou avisos do que o plano lista. |
Transformações
O exportador declara as mudanças que fez nos dados do site de origem. Cada uma destas transformações carrega um tipo de registro e uma contagem:
| Código | Significado |
|---|---|
orphan_dropped | Registros cujo pai não existia mais no site de origem foram omitidos, como uma revisão de uma entrada excluída. |
soft_orphan_dropped | Links para registros ausentes foram omitidos, como uma atribuição de termo a um termo excluído ou um item de menu apontando para uma entrada excluída. |
orphan_reference_nulled | Uma referência a um registro ausente foi removida, como a pasta excluída de um arquivo de mídia. |
avatar_nulled | Um avatar de byline ou imagem de preview de seção referenciava mídia que não está no pacote e foi removido. |
media_not_ready_dropped | Mídia que não estava pronta, como um upload incompleto, foi omitida. |
media_ref_unlinked | Referências a mídia que não está no pacote foram removidas do conteúdo. |
media_url_relativized | URLs absolutas para os próprios arquivos de mídia do site de origem foram convertidas em URLs relativas ao site que resolvem no destino. |
redirect_duplicate_dropped | Redirecionamentos duplicados para o mesmo caminho de origem foram omitidos. Foi mantido um redirecionamento por caminho de origem. |
unknown_storage_key | Registros ainda referenciam arquivos de mídia que o site de origem não tem. Foram exportados sem alteração. |
A importação declara suas próprias mudanças:
| Código | Significado |
|---|---|
principal_mapped | Referências a principais são reescritas para os usuários de destino mapeados. |
principal_unmapped | Referências a principais sem mapeamento são removidas. |
seeded_scaffold_removed | O scaffold de configuração no destino é excluído antes que a importação escreva. O plano lista cada item. |
redirect_loop_disabled | Redirecionamentos que formam um loop são importados desativados. |
search_unsupported | A busca é desativada para as collections listadas porque o destino usa PostgreSQL. |
float4_rounded | Valores decimais são arredondados para a precisão das colunas real do PostgreSQL do destino. |
locale_recased | Locales são escritos com a capitalização configurada no destino, como pt-br como pt-BR. |
Executar a importação
A execução percorre estas etapas nesta ordem:
- Reservar o destino e verificar de novo que está vazio.
- Remover o scaffold de configuração listado no plano.
- Criar tipos de bloco, collections, campos, definições de taxonomia, definições de relação e campos de byline.
- Copiar arquivos de mídia para o armazenamento do destino e criar registros de mídia.
- Escrever termos e bylines.
- Escrever revisões e entradas.
- Escrever atribuições de termos, créditos de byline, referências de conteúdo e registros SEO.
- Escrever menus, widgets, seções, redirecionamentos, comentários, reações e configurações.
- Reconstruir índices de busca e caches, e enfileirar a reindexação de uso de mídia.
- Verificar o resultado.
Cada chamada a advance executa um passo limitado, que cabe nos limites de solicitação do Cloudflare Workers no D1. O progresso é armazenado no servidor. Uma solicitação interrompida perde no máximo o passo em andamento, e cada escrita é idempotente, de modo que executar um passo de novo não duplica registros.
Enquanto outra solicitação executa um passo, ou quando outra solicitação assume a operação durante um passo, advance retorna a operação com um nextRequestInMs curto. Um erro de armazenamento ou de banco é retentado: a operação registra o erro e nextRequestInMs cresce a cada falha consecutiva. Após falhas repetidas sem progresso, a importação falha.
Escritas são bloqueadas durante uma importação
Do primeiro passo de execução até a importação terminar, o EmDash rejeita solicitações de escrita à sua API com 503 TRANSFER_IMPORT_IN_PROGRESS. Isso cobre o admin, a API REST, rotas de plugins, envios públicos de comentários, publicação agendada e escritas de conteúdo de plugins. Login, gestão de usuários e tokens de API, bloqueios de edição de entradas e a própria API de transferência continuam disponíveis. Solicitações de leitura não são bloqueadas.
Ferramentas MCP de escrita, incluindo ferramentas MCP de plugins, falham com TRANSFER_IMPORT_IN_PROGRESS no erro de ferramenta usual. Ferramentas MCP somente leitura e as ferramentas de transferência site_* continuam funcionando, de modo que uma importação iniciada via MCP pode ser retomada, inspecionada e concluída via MCP.
Retomar após uma interrupção
A página do admin só avança uma importação enquanto estiver aberta. Para retomar, reabra Settings → Transfer, execute emdash site import resume <operation-id> ou chame advance de novo para a mesma operação. O servidor continua a partir do último passo concluído. Se a solicitação interrompida ainda detinha a operação, a próxima chamada espera essa retenção expirar, no máximo cinco minutos.
Uma importação com falha ou cancelada não pode ser retomada.
Cancelar uma importação
Selecione Cancel import em Settings → Transfer, execute emdash site import cancel <operation-id> ou envie POST imports/{id}/cancel. Um passo em andamento para após o lote atual. Cancelar não remove registros já escritos.
Abandonar uma importação incompleta
Uma importação com falha ou cancelada que começou a escrever continua bloqueando escritas, para que o site incompleto não seja editado por engano. Para levantar o bloqueio, selecione Abandon import em Settings → Transfer, execute emdash site import abandon <operation-id> ou envie POST imports/{id}/abandon. Abandonar mantém os dados importados.
Após um abandono, o site não está mais vazio, então não pode receber outra importação. Importe para um site recém-configurado.
Uma importação com falha ou cancelada que nunca começou a escrever não bloqueia escritas e não precisa ser abandonada.
Verificar o resultado
A verificação lê de volta cada registro importado com o mesmo código que o exportador usa, aplica as transformações declaradas do plano aos registros do pacote e compara os dois. Também confere a contagem de registros de cada tipo e baixa de novo cada arquivo de mídia importado para conferir o digest. Qualquer diferença faz a importação falhar com TRANSFER_VERIFICATION_FAILED. O errorDetail da operação lista até 50 das diferenças.
Uma importação bem-sucedida produz um recibo:
{
"operationId": "01J8ZR2C4S6D8F0G2H4J6K8M0N",
"packageDigest": "sha256:…",
"planDigest": "sha256:…",
"targetSiteId": "01J8ZR0A2B4C6D8E0F2G4H6J8K",
"originSiteId": "01J1A3C5E7G9J1L3N5Q7S9U1W3",
"formatVersion": "1",
"importerEmDashVersion": "0.38.0",
"completedAt": "2026-09-23T10:15:00.000Z",
"logicalDigest": "sha256:…",
"counts": { "entry": 412, "media": 96 },
"warnings": [],
"verification": "verified",
"receiptDigest": "sha256:…"
}
Um recibo registra que o site de destino identificado por targetSiteId continha exatamente o conteúdo do pacote identificado por packageDigest, após o plano identificado por planDigest, quando a verificação terminou. O logicalDigest resume os registros verificados.
receiptDigest é o digest SHA-256 do JSON canônico do recibo com a propriedade receiptDigest removida. Detecta um recibo alterado depois de emitido. Um recibo não é assinado, então não prova qual servidor o emitiu. Busque o recibo no destino por uma conexão autenticada quando isso importar.
Um recibo descreve o site no momento em que a verificação terminou. Não diz nada sobre edições posteriores.
Mover entre bancos de dados
Um pacote não depende do banco de dados da origem. Exporte de SQLite, PostgreSQL ou D1 e importe em qualquer um deles. Planeje as seguintes diferenças quando o destino usar PostgreSQL:
- O PostgreSQL armazena inteiros em 32 bits. Um inteiro fora desse intervalo é um bloqueio
integer_out_of_range. - O PostgreSQL armazena campos
numbere pontos focais de mídia como valores de ponto flutuante de 32 bits. Valores que mudam são declarados comofloat4_rounded, e a verificação compara os valores arredondados. - A busca de texto completo está disponível apenas em SQLite e D1. Collections com busca ativada são importadas com a busca desativada e declaradas como
search_unsupported.
O importador escreve mídia no backend de armazenamento do destino sob novas chaves de armazenamento e reescreve referências de mídia no conteúdo, nas configurações e nos registros SEO para corresponder. Uma referência a um arquivo de mídia que a origem não tem é exportada sem alteração e declarada como unknown_storage_key.
Segurança
- Trate um pacote como sensível. Ele contém todo o conteúdo, incluindo rascunhos e lixeira, e os e-mails de autores e comentaristas. Mantenha-o fora de buckets públicos e pastas compartilhadas, e exclua cópias de que não precisa mais.
- Trate um pacote como entrada não confiável. A importação verifica caminhos, tamanhos, digests, esquemas de registros, referências e limites antes de escrever. Nunca executa código ou SQL de um pacote e nunca busca URLs de um.
- Conceda acesso à transferência de propósito. A transferência exige o papel de administrador. Um token com o escopo
adminpode executar toda ação de transferência, então dê ao token de um agente apenas o escopo de transferência de que precisa. - Revise o log de auditoria. O EmDash registra ações de transferência no log de auditoria do site:
transfer_export_create,transfer_import_create,transfer_import_execute,transfer_import_cancel,transfer_import_abandon,transfer_import_complete,transfer_import_fail,transfer_approval_approveetransfer_approval_deny. Cada entrada nomeia o usuário agente e a operação ou aprovação (tipo de recursotransfer_operationoutransfer_approval). Seus detalhes só têm IDs, digests, contagens de registros e códigos de erro, nunca conteúdo do pacote. Detalhes de erro de transferência também nunca incluem conteúdo do pacote. - Mantenha o staging privado. O EmDash prepara arquivos de pacote sob o prefixo
transfers/do seu bucket de armazenamento e se recusa a servir esse prefixo pela rota de mídia. Se o bucket tiver um domínio público, limite-o à mídia, como nos backups. Arquivos preparados são excluídos depois que uma operação termina ou expira.
Escopos de token
A transferência usa três escopos de token de API:
| Escopo | Permite |
|---|---|
transfer:export | Iniciar, avançar e baixar exportações. |
transfer:analyze | Criar importações, enviar arquivos do pacote, analisar e ler planos. |
transfer:execute | Iniciar, avançar, cancelar e abandonar importações. |
O escopo admin inclui os três, então o token que emdash login salva pode executar toda transferência. Cada escopo de transferência concede apenas suas próprias ações, e só um administrador pode emitir um. Use-os para dar a um token acesso mais restrito que admin, por exemplo a um agente que pode analisar pacotes mas não exportar nem importar. Veja a referência de escopos.
Aprovações para agentes
Agentes de IA conduzem transferências pelas ferramentas MCP site_*. As ferramentas iniciam, avançam e relatam operações. Nunca carregam bytes do pacote, então o usuário de um agente baixa exportações e envia pacotes com a CLI ou a API REST. Toda ferramenta exige o papel Admin.
Um cliente MCP cujo token não tem admin nem o escopo de transferência correspondente, como um agente ao qual só foi concedido transfer:analyze, não pode iniciar uma exportação ou importação sozinho. Sua chamada a site_export_start ou site_import_start cria uma solicitação de aprovação pendente e falha com TRANSFER_APPROVAL_REQUIRED e o ID da aprovação. Um administrador aprova ou nega a solicitação em Approval requests em Settings → Transfer, que lista cada solicitação pendente com solicitante, ação e horário de expiração. Os endpoints exclusivos de sessão POST /_emdash/api/admin/transfer/approvals/{id}/approve e …/deny fazem o mesmo. Tokens de API não podem aprovar solicitações. O cliente então repete a chamada com o ID da aprovação. Aprovações se aplicam apenas a estas ferramentas MCP; a API REST não tem parâmetro de aprovação.
Uma aprovação concede uma chamada ao usuário que a solicitou, a partir do mesmo token, com os mesmos argumentos. Uma aprovação de exportação fica vinculada às opções de exportação. Uma aprovação de importação fica vinculada à operação e a ambos os digests, então um plano alterado precisa de uma nova aprovação. Uma solicitação pendente expira após 15 minutos, e uma aprovada 15 minutos após a aprovação. A nova tentativa que inicia a operação a consome; se a operação não iniciar, a mesma aprovação pode ser tentada de novo até expirar. O mesmo usuário e token podem então verificar e avançar aquela operação sem o escopo.
Conceda transfer:export, transfer:execute ou admin ao token de um agente apenas quando o agente precisar executar transferências sem uma pessoa aprovar cada uma.
Limites
| Limite | Valor |
|---|---|
manifest.json | 8 MiB |
| Um registro | 1.900.000 bytes |
| Um arquivo de registro ou índice | 4 MiB e 1.000 registros |
| Registros por pacote | 5.000.000 |
| Arquivos por pacote | 1.000.000 |
| Profundidade de aninhamento JSON | 64 |
| Um arquivo de mídia | O maxUploadSize do destino, 50 MiB por padrão |
O endpoint capabilities reporta os valores que o site aplica.
Para provedores de hospedagem
Um plano de controle de hospedagem pode mover o site de um cliente para produção só com a API REST:
-
Provisione um novo site EmDash com seu armazenamento, locales e
maxUploadSize, e conclua a configuração. Verifique secapabilitiesreportaportableDomain.emptycomotrue. -
Emita um token para o plano de controle com
transfer:analyzeetransfer:execute. Mantenha-o fora de qualquer agente ou ferramenta de construção de site. -
Execute a importação e aplique sua própria política aos avisos do plano antes de executar. Recuse qualquer plano com bloqueios.
-
Busque o recibo e confira-o antes de promover o site:
verificationéverified;packageDigesté o digest do pacote que você pretendia publicar;planDigesté o plano que você aceitou;targetSiteIdé o site que você está prestes a promover; ereceiptDigestcoincide com o JSON canônico do recibo.
-
Promova o site, por exemplo roteando seu domínio para ele.
Mantenha o destino inacessível até o passo 4 ter sucesso. O EmDash não oculta de visitantes um site parcialmente importado.
Solução de problemas
Erros de transferência usam códigos estáveis. O status HTTP aparece com cada código.
| Código | Status | O que fazer |
|---|---|---|
TRANSFER_TARGET_NOT_EMPTY | 409 | O destino já tem conteúdo. Importe para um site recém-configurado. Settings → Transfer e capabilities listam o que torna o site inelegível. |
TRANSFER_IMPORT_IN_PROGRESS | 503 | Uma importação está em andamento neste site, ou uma importação incompleta ainda bloqueia escritas. Espere terminar ou abandone uma importação com falha ou cancelada. |
TRANSFER_FENCE_CHECK_FAILED | 503 | O EmDash não pôde verificar se uma importação está em andamento. Tente a escrita de novo. |
TRANSFER_EXPORT_CONCURRENT_WRITES | 409 | O site continuou mudando enquanto a exportação rodava. Exporte de novo quando a edição estiver quieta. |
TRANSFER_EXPIRED | 410 | Os arquivos da exportação foram excluídos após sete dias, ou uma importação não foi executada em 24 horas. Comece de novo. |
TRANSFER_FILE_MISSING | 422 | Alguns arquivos declarados não foram enviados. Envie tudo que imports/{id}/missing listar. |
TRANSFER_FILE_NOT_DECLARED | 422 | O caminho de upload não está no pacote. Envie apenas caminhos listados. |
TRANSFER_FILE_SIZE_MISMATCH | 422 | Content-Length ou os bytes enviados diferem do tamanho declarado. Envie o arquivo sem alteração. |
TRANSFER_FILE_DIGEST_MISMATCH | 422 | Os bytes enviados diferem do digest declarado, ou um arquivo da exportação mudou após a exportação. Envie o arquivo original ou exporte de novo. |
TRANSFER_LIMIT_EXCEEDED | 413 | Um arquivo excede um limite. Para mídia, aumente o maxUploadSize do destino. |
TRANSFER_MANIFEST_INVALID | 422 | O corpo da solicitação não é um manifesto válido. Envie manifest.json byte a byte. |
TRANSFER_UNSUPPORTED_FORMAT | 422 | Atualize o EmDash no destino. |
TRANSFER_UNSUPPORTED_FEATURE | 422 | Atualize o EmDash no destino. |
TRANSFER_CONTAINER_INVALID | 422 | O arquivo .emdash não é um arquivo de pacote válido. Baixe-o de novo. |
TRANSFER_PLAN_BLOCKED | 409 | O plano tem bloqueios. Veja revisar o plano de importação. |
TRANSFER_PACKAGE_DIGEST_MISMATCH | 409 | O digest não coincide com o pacote preparado. Use o packageDigest da operação. |
TRANSFER_PLAN_DIGEST_MISMATCH | 409 | O plano mudou desde que você o revisou. Leia o plano atual e revise-o de novo. |
TRANSFER_DECISIONS_INVALID | 422 | Uma decisão nomeia um principal desconhecido ou um usuário de destino que não existe. Corrija o mapeamento. |
TRANSFER_INVALID_STATE | 409 | A operação não está em um estado que permita a solicitação. Leia a operação e siga seu state. |
TRANSFER_LEASE_ACTIVE | 409 | Outra solicitação está executando um passo. Espere e tente de novo. |
TRANSFER_IDEMPOTENCY_CONFLICT | 409 | A Idempotency-Key já foi usada para uma exportação com outras opções ou uma importação de outro pacote. Use uma chave nova. |
TRANSFER_RUNTIME_MISMATCH | 409 | Uma versão incompatível do EmDash iniciou a operação. Conclua-a com a versão que a iniciou ou inicie uma nova. |
TRANSFER_VERIFICATION_FAILED | 422 | O site importado não coincide com o pacote. Leia as diferenças em errorDetail, abandone a importação e importe para um site novo. |
TRANSFER_APPROVAL_REQUIRED | 403 | Um administrador deve aprovar a solicitação. Veja aprovações para agentes. |
TRANSFER_APPROVAL_INVALID | 403 | A aprovação é desconhecida, foi negada, expirou, foi usada ou está vinculada a outros parâmetros. Solicite uma nova. |
TRANSFER_SCHEMA_UNCLASSIFIED | 500 | O banco tem uma tabela ou coluna que o exportador não reconhece. Execute a versão do EmDash que corresponde às migrações do banco. |
INSUFFICIENT_SCOPE | 403 | O token não tem admin nem o escopo de transferência que a solicitação precisa. Emita um token com o escopo. |