Transferência de sites

Nesta página

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

MecanismoFinalidadeImportávelArquivos de mídiaUsuários e segredos
Arquivo seedInicializar um modelo de conteúdo e conteúdo de exemploSim, com semântica de seedNãoNão
Snapshot de previewPreencher a renderização isolada de previewSomente previewNãoNão
Backup JSONInspecionar estado selecionado com forma de banco de dadosNãoNãoNão
Backup bruto de banco de dados e mídiaRecuperar um deploymentRestauração no mesmo tipo de bancoCópia separadaSim
Pacote de siteMover um site para outro site EmDashSim, para um site vazioSimNã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 como locale_recased.
  • Um limite de upload grande o suficiente. Cada arquivo de mídia deve caber no maxUploadSize do 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

  1. Abra Settings → Transfer. A página está disponível para administradores.

  2. Na seção Export, desative Include comments para deixar comentários e reações de fora.

  3. 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.

  4. 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.

  1. Inicie a exportação. Para deixar comentários e reações de fora, envie { "comments": false } como corpo. Um cabeçalho Idempotency-Key faz uma solicitação repetida retornar a mesma exportação em vez de iniciar outra. Reutilizar uma chave com opções diferentes falha com 409 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"
  2. Avance a exportação até que nextRequestInMs seja null. Espere o número de milissegundos retornado entre as chamadas. operation.progress informa os passos done e total, os records escritos até agora e bytesDone e bytesTotal quando o tamanho do pacote é conhecido.

    curl -X POST https://example.com/_emdash/api/admin/transfer/exports/$EXPORT_ID/advance \
      -H "Authorization: Bearer $EMDASH_TOKEN"
  3. Verifique se operation.state é complete. Uma exportação failed traz o motivo em operation.errorCode.

  4. 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

  1. 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.

  2. 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.

  3. Quando o upload termina, o site analisa o pacote. Você pode sair da página e voltar depois.

  4. 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.

  5. 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.

  6. Em Site identity, escolha se usa o título e o slogan do pacote ou mantém os deste site.

  7. 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.

  8. 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.

  1. Crie a importação. Envie os bytes inalterados de manifest.json como 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
  2. Envie cada arquivo que falta para imports/{id}/files/{path}. O cabeçalho Content-Length deve 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.ndjson

    Enviar um arquivo de índice declara os arquivos de registro e de mídia que ele lista. Solicite imports/{id}/missing de 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.

  3. Analise o pacote. Chame imports/{id}/analyze até que nextRequestInMs seja null. A resposta final contém o plan e seu planDigest.

    curl -X POST https://new.example.com/_emdash/api/admin/transfer/imports/$IMPORT_ID/analyze \
      -H "Authorization: Bearer $EMDASH_TOKEN"
  4. Revise o plano e leia cada bloqueio, aviso e transformação. Veja revisar o plano de importação.

  5. 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" } }'
  6. 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:…" }'
  7. Avance a importação até que nextRequestInMs seja null, 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"
  8. Verifique se operation.state é complete e, em seguida, leia o recibo em imports/{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_BLOCKED até que o plano não tenha nenhum. Altere os mapeamentos de principais para resolver um principal_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ódigoSignificado
package_invalidUm arquivo ou caminho do pacote falha na validação.
unsupported_formatO destino não oferece suporte ao formato ou à versão de formato do pacote.
unsupported_featureO pacote exige um recurso que o destino não oferece.
limit_exceededUm arquivo ou registro do pacote excede um limite.
file_missingUm arquivo declarado do pacote não foi enviado.
file_mismatchO tamanho ou o digest de um arquivo do pacote não coincide com a declaração.
record_invalidUm registro está malformado ou não é JSON canônico.
record_count_mismatchO número de registros de um tipo difere do manifesto.
record_order_invalidOs registros estão fora de ordem, ou um pai aparece depois do filho.
duplicate_idDois registros do mesmo tipo compartilham um ID.
dangling_referenceUm 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_cycleUm termo, comentário ou item de menu é pai de si mesmo.
media_ref_invalidO conteúdo referencia um registro de mídia que não está no pacote.
media_blob_missingO arquivo de um registro de mídia não está no pacote.
media_blob_too_largeUm arquivo de mídia é maior que o maxUploadSize do destino.
target_not_emptyO destino já tem conteúdo. O detail do bloqueio nomeia o que encontrou.
locale_not_configuredO pacote usa um locale que a configuração de i18n do destino não inclui.
field_type_unknownUm campo ou campo de byline usa um tipo que o destino não oferece.
principal_conflictOs mapeamentos de principais dariam a um usuário duas bylines no mesmo locale.
integer_out_of_rangeUm inteiro está fora do intervalo de inteiros do banco de destino. O PostgreSQL armazena inteiros em 32 bits.
value_constraint_violationUm valor que a API do admin recusaria. Veja a lista abaixo.
unique_violationUm 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 http ou https, 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 http ou https nem um caminho do site; e
  • uma URL de item de menu com um esquema que menus não permitem.

Avisos

CódigoSignificado
media_provider_externalO conteúdo usa mídia de um provedor externo. A referência é mantida; os arquivos não são copiados.
media_row_missingUma configuração referencia mídia que não está no pacote.
soft_reference_danglingUma referência opcional não resolve para um registro no pacote.
redirect_loops_uncheckedO pacote tem redirecionamentos demais para verificar loops antes de importar. Um redirecionamento que fecharia um loop é importado desativado.
issues_truncatedForam 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ódigoSignificado
orphan_droppedRegistros cujo pai não existia mais no site de origem foram omitidos, como uma revisão de uma entrada excluída.
soft_orphan_droppedLinks 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_nulledUma referência a um registro ausente foi removida, como a pasta excluída de um arquivo de mídia.
avatar_nulledUm 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_droppedMídia que não estava pronta, como um upload incompleto, foi omitida.
media_ref_unlinkedReferências a mídia que não está no pacote foram removidas do conteúdo.
media_url_relativizedURLs 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_droppedRedirecionamentos duplicados para o mesmo caminho de origem foram omitidos. Foi mantido um redirecionamento por caminho de origem.
unknown_storage_keyRegistros 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ódigoSignificado
principal_mappedReferências a principais são reescritas para os usuários de destino mapeados.
principal_unmappedReferências a principais sem mapeamento são removidas.
seeded_scaffold_removedO scaffold de configuração no destino é excluído antes que a importação escreva. O plano lista cada item.
redirect_loop_disabledRedirecionamentos que formam um loop são importados desativados.
search_unsupportedA busca é desativada para as collections listadas porque o destino usa PostgreSQL.
float4_roundedValores decimais são arredondados para a precisão das colunas real do PostgreSQL do destino.
locale_recasedLocales 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:

  1. Reservar o destino e verificar de novo que está vazio.
  2. Remover o scaffold de configuração listado no plano.
  3. Criar tipos de bloco, collections, campos, definições de taxonomia, definições de relação e campos de byline.
  4. Copiar arquivos de mídia para o armazenamento do destino e criar registros de mídia.
  5. Escrever termos e bylines.
  6. Escrever revisões e entradas.
  7. Escrever atribuições de termos, créditos de byline, referências de conteúdo e registros SEO.
  8. Escrever menus, widgets, seções, redirecionamentos, comentários, reações e configurações.
  9. Reconstruir índices de busca e caches, e enfileirar a reindexação de uso de mídia.
  10. 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 number e pontos focais de mídia como valores de ponto flutuante de 32 bits. Valores que mudam são declarados como float4_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 admin pode 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_approve e transfer_approval_deny. Cada entrada nomeia o usuário agente e a operação ou aprovação (tipo de recurso transfer_operation ou transfer_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:

EscopoPermite
transfer:exportIniciar, avançar e baixar exportações.
transfer:analyzeCriar importações, enviar arquivos do pacote, analisar e ler planos.
transfer:executeIniciar, 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

LimiteValor
manifest.json8 MiB
Um registro1.900.000 bytes
Um arquivo de registro ou índice4 MiB e 1.000 registros
Registros por pacote5.000.000
Arquivos por pacote1.000.000
Profundidade de aninhamento JSON64
Um arquivo de mídiaO 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:

  1. Provisione um novo site EmDash com seu armazenamento, locales e maxUploadSize, e conclua a configuração. Verifique se capabilities reporta portableDomain.empty como true.

  2. Emita um token para o plano de controle com transfer:analyze e transfer:execute. Mantenha-o fora de qualquer agente ou ferramenta de construção de site.

  3. Execute a importação e aplique sua própria política aos avisos do plano antes de executar. Recuse qualquer plano com bloqueios.

  4. 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; e
    • receiptDigest coincide com o JSON canônico do recibo.
  5. 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ódigoStatusO que fazer
TRANSFER_TARGET_NOT_EMPTY409O 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_PROGRESS503Uma 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_FAILED503O EmDash não pôde verificar se uma importação está em andamento. Tente a escrita de novo.
TRANSFER_EXPORT_CONCURRENT_WRITES409O site continuou mudando enquanto a exportação rodava. Exporte de novo quando a edição estiver quieta.
TRANSFER_EXPIRED410Os 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_MISSING422Alguns arquivos declarados não foram enviados. Envie tudo que imports/{id}/missing listar.
TRANSFER_FILE_NOT_DECLARED422O caminho de upload não está no pacote. Envie apenas caminhos listados.
TRANSFER_FILE_SIZE_MISMATCH422Content-Length ou os bytes enviados diferem do tamanho declarado. Envie o arquivo sem alteração.
TRANSFER_FILE_DIGEST_MISMATCH422Os 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_EXCEEDED413Um arquivo excede um limite. Para mídia, aumente o maxUploadSize do destino.
TRANSFER_MANIFEST_INVALID422O corpo da solicitação não é um manifesto válido. Envie manifest.json byte a byte.
TRANSFER_UNSUPPORTED_FORMAT422Atualize o EmDash no destino.
TRANSFER_UNSUPPORTED_FEATURE422Atualize o EmDash no destino.
TRANSFER_CONTAINER_INVALID422O arquivo .emdash não é um arquivo de pacote válido. Baixe-o de novo.
TRANSFER_PLAN_BLOCKED409O plano tem bloqueios. Veja revisar o plano de importação.
TRANSFER_PACKAGE_DIGEST_MISMATCH409O digest não coincide com o pacote preparado. Use o packageDigest da operação.
TRANSFER_PLAN_DIGEST_MISMATCH409O plano mudou desde que você o revisou. Leia o plano atual e revise-o de novo.
TRANSFER_DECISIONS_INVALID422Uma decisão nomeia um principal desconhecido ou um usuário de destino que não existe. Corrija o mapeamento.
TRANSFER_INVALID_STATE409A operação não está em um estado que permita a solicitação. Leia a operação e siga seu state.
TRANSFER_LEASE_ACTIVE409Outra solicitação está executando um passo. Espere e tente de novo.
TRANSFER_IDEMPOTENCY_CONFLICT409A 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_MISMATCH409Uma 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_FAILED422O 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_REQUIRED403Um administrador deve aprovar a solicitação. Veja aprovações para agentes.
TRANSFER_APPROVAL_INVALID403A aprovação é desconhecida, foi negada, expirou, foi usada ou está vinculada a outros parâmetros. Solicite uma nova.
TRANSFER_SCHEMA_UNCLASSIFIED500O 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_SCOPE403O token não tem admin nem o escopo de transferência que a solicitação precisa. Emita um token com o escopo.