Transferencia de sitios

En esta página

Un paquete de sitio es una copia portátil del modelo de contenido, el contenido, el historial editorial, la presentación, los ajustes y los archivos multimedia de un sitio EmDash. Importa un paquete de sitio para mover un sitio a otro despliegue de EmDash, incluido uno que use una base de datos diferente: SQLite, PostgreSQL o Cloudflare D1.

Una importación escribe en un sitio nuevo cuya área de contenido está vacía. EmDash comprueba el paquete completo antes de escribir nada, ejecuta la importación en pequeños pasos reanudables, vuelve a leer el sitio importado y emite un recibo cuando el resultado coincide con el paquete.

Un paquete de sitio no contiene usuarios, credenciales ni secretos. Sí contiene todas las entradas y comentarios del sitio, incluidas las direcciones de correo electrónico de autores y comentaristas. Guárdalo y envíalo con el mismo cuidado que una copia de seguridad de la base de datos.

Elegir el tipo de copia adecuado

MecanismoFinalidadImportableArchivos multimediaUsuarios y secretos
Archivo seedInicializar un modelo de contenido y contenido de ejemploSí, con semántica de seedNoNo
Instantánea de vista previaPoblar el renderizado aislado de vistas previasSolo vista previaNoNo
Copia de seguridad JSONInspeccionar el estado seleccionado con forma de base de datosNoNoNo
Copia de seguridad en bruto de base de datos y mediosRecuperar un despliegueRestauración en el mismo tipo de base de datosCopia aparteSí
Paquete de sitioMover un sitio a otro sitio EmDashSí, en un sitio vacíoSíNo. Solo nombres y direcciones de correo de los autores

Usa una copia de seguridad en bruto de la base de datos para recuperar un despliegue tras una pérdida de datos. Usa un paquete de sitio para crear una copia nueva de un sitio en otro lugar.

Qué contiene un paquete de sitio

Un paquete de sitio contiene:

  • colecciones, campos, tipos de bloque con todas sus versiones, definiciones de taxonomías, definiciones de relaciones y definiciones de campos de byline;
  • todas las entradas de contenido en todos los idiomas, incluidos borradores, entradas programadas, entradas en la papelera, historial de revisiones y grupos de traducción;
  • términos de taxonomía y asignaciones de términos, bylines y créditos, referencias de contenido y registros SEO;
  • menús y elementos de menú, áreas de widgets y widgets, secciones y redirecciones;
  • comentarios y reacciones a comentarios, salvo que la exportación desactive los comentarios;
  • carpetas de medios, metadatos de medios y los bytes de cada archivo multimedia listo; y
  • los ajustes portátiles del sitio que se enumeran a continuación.

El paquete almacena los valores JSON, como los campos JSON y Portable Text, con las claves de los objetos ordenadas. Por tanto, un valor importado puede enumerar sus claves en un orden distinto al del origen. Por lo demás, los valores no cambian.

Ajustes portátiles

Solo se exportan estos ajustes: site:title, site:tagline, site:logo, site:favicon, site:postsPerPage, site:dateFormat, site:timezone, site:social, site:seo, emdash:site_title, emdash:site_tagline y emdash:locale.

El sitio de destino conserva su propia URL (site:url y emdash:site_url), su ID de sitio, su estado de configuración y sus ajustes de copia de seguridad. Una importación nunca los sobrescribe.

El plan de importación pregunta si se deben mantener el título y el eslogan del destino, que escribió el asistente de configuración, o usar los valores del paquete. De forma predeterminada se usan los valores del paquete.

Principales

Una cuenta de usuario nunca se traslada con un paquete. Por cada usuario del origen al que hacen referencia el contenido, las revisiones, los medios, las bylines o los comentarios, el paquete incluye un principal: el ID del usuario, su nombre visible y su dirección de correo electrónico. Un principal no tiene rol, contraseña, passkey, sesión ni token.

Durante la importación, asignas cada principal a un usuario del sitio de destino o lo dejas sin asignar. Consulta asignar autores a usuarios de destino.

Comentarios

Los comentarios incluyen el nombre y la dirección de correo del autor, el cuerpo, el estado, el hilo, las marcas de tiempo y los metadatos de moderación. El hash de la dirección IP y el user agent no se exportan.

Las reacciones conservan sus recuentos. El exportador sustituye cada hash de votante por un valor aleatorio nuevo, de modo que el destino no puede asociar una reacción con el visitante que la hizo.

Qué deja fuera un paquete de sitio

Un paquete de sitio nunca contiene:

  • usuarios, sesiones, passkeys, cuentas OAuth, dominios permitidos, tokens de API, clientes OAuth, códigos de autorización ni códigos de dispositivo;
  • almacenamiento, estado o ajustes de plugins, incluidos los secretos de plugins;
  • ajustes distintos de los ajustes portátiles, como el secreto de firma de las vistas previas;
  • registros de auditoría, límites de frecuencia, bloqueos de edición, estado de tareas programadas, el registro de errores 404 ni el historial de migraciones;
  • registros de uso de medios e índices de búsqueda, que la importación reconstruye;
  • claves de almacenamiento, nombres de buckets, nombres de bases de datos ni nombres de bindings del origen; ni
  • medios que no están listos, como una subida incompleta.

Los medios de un proveedor externo siguen siendo externos. El paquete conserva la referencia, pero los archivos del proveedor no se copian.

Preparar el sitio de destino

Importa en un sitio que cumpla todos los requisitos siguientes. Cuando el contenido, los idiomas, el límite de subida o el formato admitido del destino no encajan con el paquete, el análisis informa de un bloqueo.

  • Una cuenta de administrador. La importación se ejecuta como un administrador con sesión iniciada o con un token de API. Crea el administrador del destino durante la configuración.
  • Un backend de almacenamiento. Tanto el origen como el destino necesitan almacenamiento configurado. EmDash prepara allí los archivos del paquete.
  • Ningún contenido. El destino no debe contener entradas (incluidas las entradas en la papelera), revisiones, medios o carpetas de medios, bylines o campos de byline, comentarios, redirecciones, asignaciones de términos, relaciones, registros SEO, secciones creadas en el panel de administración, ni colecciones o tipos de bloque creados después de la configuración. Un sitio configurado a partir de cualquier plantilla oficial cumple los requisitos. Lo que creó la configuración es la estructura inicial: las colecciones y los tipos de bloque sembrados, las definiciones de taxonomías y sus términos sin asignar, los menús y sus elementos, las áreas de widgets y sus widgets, y las secciones del tema. El plan enumera esa estructura inicial, y la importación la elimina después de que confirmes el plan.
  • Todos los idiomas que usa el paquete. Añade cada uno de los idiomas del paquete a la configuración de i18n del destino. Un sitio sin configuración de i18n solo acepta en. Los idiomas se comparan sin distinguir mayúsculas de minúsculas, y la importación escribe cada idioma con las mayúsculas configuradas en el destino, lo que se declara como locale_recased.
  • Un límite de subida suficientemente grande. Cada archivo multimedia debe caber en el maxUploadSize del destino, que por defecto es de 50 MiB.
  • Versión de formato 1. El destino debe admitir la versión de formato del paquete y todas las funciones requeridas.

La siguiente solicitud devuelve las versiones de formato, funciones y límites admitidos. Su objeto portableDomain indica si el sitio puede recibir una importación y, si no puede, por qué.

curl https://new.example.com/_emdash/api/admin/transfer/capabilities \
  -H "Authorization: Bearer $EMDASH_TOKEN"

Exportar un sitio

Una exportación lee el sitio en pasos acotados y escribe el paquete en el almacenamiento del sitio. Antes de que termine una exportación, el exportador valida el paquete terminado del mismo modo que lo hace una importación. Si una escritura en el sitio tiene éxito durante una exportación, el exportador vuelve a empezar. Obtener o renovar un bloqueo de edición de una entrada no cuenta como escritura. Tras tres intentos, falla con TRANSFER_EXPORT_CONCURRENT_WRITES.

Los archivos de la exportación siguen disponibles durante siete días desde que se crea la exportación. Después, una descarga devuelve TRANSFER_EXPIRED.

Exportar desde el panel de administración

  1. Abre Settings → Transfer. La página está disponible para los administradores.

  2. En la sección Export, desactiva Include comments para dejar fuera los comentarios y las reacciones.

  3. Selecciona Export site. La página muestra el progreso de la exportación. Mantén la página abierta; si sales, la exportación continúa cuando vuelvas.

  4. Cuando aparezca Export ready, selecciona Download package y elige dónde guardar el archivo .emdash. La página muestra cuántos archivos y bytes se han descargado, y Stop cancela la descarga.

La sección también muestra el digest del paquete, el número de registros de cada tipo y las exportaciones recientes del sitio, cada una con su propio botón de descarga hasta que caduca.

Download package obtiene la exportación archivo por archivo, comprueba el tamaño y el digest SHA-256 de cada archivo con el manifiesto y construye el archivo .emdash en el navegador, por lo que funciona en Cloudflare Workers para sitios de cualquier tamaño. Si un archivo no coincide, la descarga se detiene con un error. Chrome, Edge y otros navegadores basados en Chromium escriben el archivo directamente en el disco. Otros navegadores mantienen el paquete completo en memoria hasta que termina la descarga; para una exportación de más de unos 500 MB, la página recomienda un navegador basado en Chromium o la CLI.

Download as one file pide al servidor el archivo en una sola respuesta. Es adecuado para sitios pequeños. En Cloudflare Workers, un sitio grande puede superar los límites de una sola solicitud.

Exportar con la CLI

Inicia sesión en el sitio de origen y expórtalo a un archivo de paquete:

npx emdash login --url https://example.com
npx emdash site export --url https://example.com --output site.emdash

El comando lleva la exportación hasta el final, descarga el paquete archivo por archivo, comprueba el tamaño y el digest de cada archivo y escribe site.emdash. Añade --no-comments para dejar fuera los comentarios y las reacciones. Si el comando se interrumpe, vuelve a ejecutarlo con las mismas opciones para reanudar la misma exportación. Consulta la referencia de emdash site export.

Exportar con la API REST

Cada llamada a advance ejecuta un paso y devuelve nextRequestInMs, el tiempo de espera antes de la siguiente llamada. La exportación termina cuando nextRequestInMs es null.

Estos ejemplos usan un token de acceso personal con el ámbito transfer:export. Consulta ámbitos de token.

  1. Inicia la exportación. Para dejar fuera los comentarios y las reacciones, envía { "comments": false } como cuerpo. Una cabecera Idempotency-Key hace que una solicitud reintentada devuelva la misma exportación en lugar de iniciar otra. Reutilizar una clave con opciones diferentes falla con 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. Avanza la exportación hasta que nextRequestInMs sea null. Espera entre llamadas el número de milisegundos devuelto. operation.progress indica los pasos done y total, los records escritos hasta el momento y bytesDone y bytesTotal una vez que se conoce el tamaño del paquete.

    curl -X POST https://example.com/_emdash/api/admin/transfer/exports/$EXPORT_ID/advance \
      -H "Authorization: Bearer $EMDASH_TOKEN"
  3. Comprueba que operation.state sea complete. Una exportación failed incluye el motivo en operation.errorCode.

  4. Descarga el paquete como un único archivo .emdash:

    curl -o site.emdash \
      https://example.com/_emdash/api/admin/transfer/exports/$EXPORT_ID/archive \
      -H "Authorization: Bearer $EMDASH_TOKEN"

Un archivo .emdash es un archivo tar sin comprimir con manifest.json como primera entrada. El archivo transmite todos los ficheros en una sola respuesta. En Cloudflare Workers, un sitio grande puede superar los límites de una sola solicitud. En ese caso, descarga manifest.json desde exports/{id}/manifest y cada archivo desde exports/{id}/files/{path}. Cada archivo descargado se comprueba con su digest registrado mientras se transmite. Si los bytes almacenados cambiaron después de la exportación, la descarga termina con un error en lugar de completarse.

Importar un sitio

Una importación se crea a partir de un paquete, se analiza para generar un plan y solo se ejecuta después de que confirmes ese plan mediante su digest. Una importación que no ha empezado a ejecutarse caduca 24 horas después de crearse.

El panel de administración, la CLI y la API REST pueden ejecutar todos los pasos. Un agente de IA puede analizar e iniciar una importación que ya se ha subido, mediante las herramientas MCP.

Importar desde el panel de administración

  1. En el sitio de destino, abre Settings → Transfer. La sección Import aparece cuando el sitio puede recibir una importación. De lo contrario, enumera lo que el sitio ya tiene y que impide una importación.

  2. Selecciona Choose package file y elige el archivo .emdash. El navegador comprueba el paquete y lo sube por partes. Nada cambia en el sitio durante la subida. Si la subida se detiene, vuelve a elegir el mismo archivo para continuar donde se quedó.

  3. Cuando termina la subida, el sitio analiza el paquete. Puedes salir de la página y volver más tarde.

  4. Revisa la importación: el sitio de origen, la fecha de exportación y la versión de EmDash, el tamaño, el digest del paquete y el número de registros de cada tipo. Lee los Blockers y los Warnings, las Differences from the source site, que enumeran las transformaciones del plan, y el Starter content that will be removed, agrupado por tipo. Consulta revisar el plan de importación.

  5. En Authors, elige el usuario de este sitio que debe ser propietario del contenido de cada autor, o Don’t map. Los autores que coinciden con la dirección de correo de un usuario se marcan como Matched by email. Consulta asignar autores a usuarios de destino.

  6. En Site identity, elige si usar el título y el eslogan del sitio del paquete o mantener los de este sitio.

  7. Selecciona Start import y confirma. El botón está desactivado mientras el plan tenga bloqueos. La edición en el sitio queda en pausa hasta que termina la importación.

  8. Sigue el progreso. Cuando la importación termina, la página muestra el recibo con una insignia Verified y sus digests de recibo, paquete, plan y contenido. Selecciona Copy receipt para guardar una copia del JSON del recibo.

La página también ofrece Cancel import desde la subida hasta que termina la importación, y Abandon import después de que falle o se cancele una importación que empezó a escribir. Ambas piden confirmación. Consulta cancelar una importación y abandonar una importación incompleta.

Importar con la CLI

Inicia sesión en el sitio de destino y analiza el paquete:

npx emdash login --url https://new.example.com
npx emdash site import site.emdash --url https://new.example.com --analyze

El comando comprueba localmente el archivo de paquete completo, lo sube, lo analiza e imprime el plan con su digest de plan. Termina con el código 2 cuando el plan tiene bloqueos. Revisa el plan como se describe en revisar el plan de importación.

Para cambiar las decisiones del plan, vuelve a ejecutar --analyze con opciones de decisión. --map-principal asigna un principal, por ID o dirección de correo, a un usuario de destino por ID o dirección de correo, o a none. --use-target-title y --use-target-tagline mantienen el título y el eslogan del destino:

npx emdash site import site.emdash --url https://new.example.com --analyze \
  --map-principal [email protected][email protected] \
  --map-principal 01J8ZQ4Y6T2N0D3VJ5R7K9M1PX=none \
  --use-target-title

Ejecuta el plan que revisaste pasando su digest:

npx emdash site import site.emdash --url https://new.example.com \
  --plan sha256:3f1c… --confirm

El comando ejecuta la importación hasta el final e imprime el recibo. Si se interrumpe, continúala con emdash site import resume <operation-id>. emdash site import status <operation-id> imprime el estado de la importación, y emdash site import receipt <operation-id> vuelve a imprimir el recibo. Consulta la referencia de emdash site import.

Importar con la API REST

El servidor trabaja con los archivos que hay dentro de un paquete, no con el archivo .emdash. Descomprime primero el archivo. Contiene manifest.json, archivos de índice en index/, archivos de registros en records/ y archivos multimedia en media/. El manifiesto fija el tamaño y el digest SHA-256 de cada archivo, de modo que el digest del paquete identifica el paquete completo.

Estos ejemplos usan un token con los ámbitos transfer:analyze y transfer:execute.

  1. Crea la importación. Envía los bytes sin modificar de manifest.json como cuerpo de la solicitud. La respuesta contiene la operación y la primera página de archivos que el servidor todavía necesita.

    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. Sube cada archivo que falte a imports/{id}/files/{path}. La cabecera Content-Length debe ser igual al tamaño declarado del archivo, y los bytes deben coincidir con su 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

    Subir un archivo de índice declara los archivos de registros y de medios que enumera. Vuelve a solicitar imports/{id}/missing después de cada lote de subidas y continúa hasta que no devuelva ningún elemento.

    Subir un archivo que ya está almacenado lo vuelve a comprobar. Si la copia almacenada ya no coincide, la subida la sustituye y la respuesta indica alreadyVerified: false.

  3. Analiza el paquete. Llama a imports/{id}/analyze hasta que nextRequestInMs sea null. La respuesta final contiene el plan y su planDigest.

    curl -X POST https://new.example.com/_emdash/api/admin/transfer/imports/$IMPORT_ID/analyze \
      -H "Authorization: Bearer $EMDASH_TOKEN"
  4. Revisa el plan y lee cada bloqueo, advertencia y transformación. Consulta revisar el plan de importación.

  5. Envía decisiones si los valores predeterminados no son los que quieres. Cada envío devuelve un plan nuevo y un digest de plan nuevo. Una vez solicitada la ejecución, el plan queda congelado y el envío de decisiones falla con 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. Inicia la importación con los digests que revisaste:

    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. Avanza la importación hasta que nextRequestInMs sea null, esperando entre llamadas el retardo devuelto.

    curl -X POST https://new.example.com/_emdash/api/admin/transfer/imports/$IMPORT_ID/advance \
      -H "Authorization: Bearer $EMDASH_TOKEN"
  8. Comprueba que operation.state sea complete y, a continuación, lee el recibo desde imports/{id}/receipt.

La ejecución falla con TRANSFER_PACKAGE_DIGEST_MISMATCH o TRANSFER_PLAN_DIGEST_MISMATCH cuando alguno de los digests difiere del paquete preparado o del plan actual. Lee el plan actual desde imports/{id}/plan, revísalo de nuevo y reintenta con sus digests.

Asignar autores a usuarios de destino

El análisis enumera cada principal con su nombre visible, su dirección de correo y el número de registros del paquete que hacen referencia a él. Cuando exactamente un usuario de destino tiene la misma dirección de correo, comparada sin distinguir mayúsculas de minúsculas, el plan sugiere ese usuario y le asigna el principal de forma predeterminada. Los principales sin sugerencia empiezan sin asignar.

Para cambiar una asignación, pasa --map-principal a emdash site import --analyze, o envía principalMappings al endpoint de análisis. Cada asignación indica un usuario de destino o deja el principal sin asignar (none en la CLI, null en la API). EmDash aplica cada asignación a los autores de entradas, los autores de revisiones, quienes subieron los medios, los vínculos de usuario de las bylines y los autores de comentarios.

Las referencias de un principal sin asignar se eliminan. Cuando un autor sin asignar también tenía una byline vinculada a su cuenta, el importador acredita esa byline de forma explícita en cada una de las entradas del autor que no tenga un crédito de byline explícito y cuyo idioma tenga la byline del autor. Así, el crédito del autor permanece en la página.

Dos asignaciones producen un bloqueo principal_conflict:

  • un principal con más de una byline en el mismo idioma se asigna a un usuario; o
  • dos principales que tienen bylines en el mismo idioma se asignan al mismo usuario.

Un usuario de destino solo puede tener una byline por idioma. Deja un principal sin asignar o asigna los principales a usuarios diferentes.

Revisar el plan de importación

Un plan enumera lo que creará la importación, las decisiones que aplicará y tres tipos de hallazgos:

  • Los bloqueos impiden la ejecución. La ejecución devuelve TRANSFER_PLAN_BLOCKED hasta que el plan no tenga ninguno. Cambia las asignaciones de principales para resolver un principal_conflict. Cualquier otro bloqueo requiere un cambio en el paquete o en el destino: cancela la importación, haz el cambio y crea una importación nueva.
  • Las advertencias describen problemas del paquete que no detienen la importación. Se copian en el recibo.
  • Las transformaciones son las diferencias exactas y declaradas entre el sitio de origen y el sitio importado. Primero se enumeran los cambios del exportador y después los de la importación. La verificación aplica las transformaciones de la importación cuando compara el sitio importado con el paquete.

Un plan enumera como máximo 500 bloqueos y advertencias. Una advertencia issues_truncated indica cuántos más se encontraron.

Bloqueos

CódigoSignificado
package_invalidUn archivo o una ruta del paquete no supera la validación.
unsupported_formatEl destino no admite el formato o la versión de formato del paquete.
unsupported_featureEl paquete requiere una función que el destino no admite.
limit_exceededUn archivo o registro del paquete supera un límite.
file_missingNo se ha subido un archivo declarado del paquete.
file_mismatchEl tamaño o el digest de un archivo del paquete no coincide con su declaración.
record_invalidUn registro está mal formado o no es JSON canónico.
record_count_mismatchEl número de registros de un tipo difiere del manifiesto.
record_order_invalidLos registros están desordenados, o un padre aparece después de su hijo.
duplicate_idDos registros del mismo tipo comparten un ID.
dangling_referenceUn registro hace referencia a un registro que no está en el paquete. Esto incluye un campo de bloques que nombra un tipo de bloque que falta en el paquete, y un tipo de bloque cuya versión actual falta en el paquete.
reference_cycleUn término, comentario o elemento de menú es su propio padre.
media_ref_invalidEl contenido hace referencia a un registro de medio que no está en el paquete.
media_blob_missingEl archivo de un registro de medio no está en el paquete.
media_blob_too_largeUn archivo multimedia es mayor que el maxUploadSize del destino.
target_not_emptyEl destino ya tiene contenido. El detail del bloqueo indica lo que encontró.
locale_not_configuredEl paquete usa un idioma que la configuración de i18n del destino no incluye.
field_type_unknownUn campo o campo de byline usa un tipo que el destino no admite.
principal_conflictLas asignaciones de principales darían a un usuario dos bylines en el mismo idioma.
integer_out_of_rangeUn entero está fuera del rango de enteros de la base de datos de destino. PostgreSQL almacena los enteros en 32 bits.
value_constraint_violationUn valor que la API de administración rechazaría. Consulta la lista siguiente.
unique_violationUn registro duplicaría la clave única de otro registro en el destino.

El importador escribe los registros directamente, por lo que el análisis aplica las mismas comprobaciones que aplica la API de administración cuando se guardan esos registros. Cada uno de estos valores es un value_constraint_violation:

  • un valor de entrada que no cabe en la columna de su campo, un campo obligatorio sin valor o un valor para un campo que la colección no tiene;
  • una redirección cuyo origen o destino no es una ruta del sitio, cuyo tipo no se admite, cuyo patrón de origen no es válido o cuyo destino usa un parámetro que el origen no captura;
  • un sitio web de byline que no es una URL http o https, un valor de campo de byline que no se ajusta al tipo o a las opciones de su campo, o un campo de byline con más opciones de las que admite un sitio;
  • un patrón de URL de colección que no es válido;
  • un tipo de bloque con un slug reservado, una etiqueta vacía o de más de 200 caracteres, o definiciones de campos que el editor de tipos de bloque rechazaría;
  • una URL canónica SEO que no es ni una URL http o https ni una ruta del sitio; y
  • una URL de elemento de menú con un esquema que los menús no permiten.

Advertencias

CódigoSignificado
media_provider_externalEl contenido usa medios de un proveedor externo. Se conserva la referencia; los archivos no se copian.
media_row_missingUn ajuste hace referencia a un medio que no está en el paquete.
soft_reference_danglingUna referencia opcional no se resuelve en un registro del paquete.
redirect_loops_uncheckedEl paquete tiene demasiadas redirecciones para comprobar los bucles antes de importar. Una redirección que cerraría un bucle se importa desactivada.
issues_truncatedSe encontraron más bloqueos o advertencias de los que enumera el plan.

Transformaciones

El exportador declara los cambios que hizo en los datos del sitio de origen. Cada una de estas transformaciones incluye un tipo de registro y un recuento:

CódigoSignificado
orphan_droppedSe omitieron registros cuyo padre ya no existía en el sitio de origen, como una revisión de una entrada eliminada.
soft_orphan_droppedSe omitieron vínculos a registros que faltan, como una asignación de término a un término eliminado o un elemento de menú que apunta a una entrada eliminada.
orphan_reference_nulledSe eliminó una referencia a un registro que falta, como la carpeta eliminada de un archivo multimedia.
avatar_nulledUn avatar de byline o una imagen de vista previa de sección hacía referencia a un medio que no está en el paquete, y se eliminó.
media_not_ready_droppedSe omitieron los medios que no estaban listos, como una subida incompleta.
media_ref_unlinkedSe eliminaron del contenido las referencias a medios que no están en el paquete.
media_url_relativizedLas URL absolutas a los propios archivos multimedia del sitio de origen se convirtieron en URL relativas al sitio que se resuelven en el destino.
redirect_duplicate_droppedSe omitieron redirecciones duplicadas para la misma ruta de origen. Se conservó una redirección por ruta de origen.
unknown_storage_keyHay registros que siguen haciendo referencia a archivos multimedia que el sitio de origen no tiene. Se exportaron sin cambios.

La importación declara sus propios cambios:

CódigoSignificado
principal_mappedLas referencias a principales se reescriben con los usuarios de destino asignados.
principal_unmappedSe eliminan las referencias a principales sin asignar.
seeded_scaffold_removedLa estructura inicial del destino se elimina antes de que la importación escriba. El plan enumera cada elemento.
redirect_loop_disabledLas redirecciones que forman un bucle se importan desactivadas.
search_unsupportedLa búsqueda se desactiva para las colecciones enumeradas porque el destino usa PostgreSQL.
float4_roundedLos valores decimales se redondean a la precisión de las columnas real de PostgreSQL del destino.
locale_recasedLos idiomas se escriben con las mayúsculas configuradas en el destino, por ejemplo pt-br como pt-BR.

Ejecutar la importación

La ejecución recorre estas fases en orden:

  1. Reservar el destino y volver a comprobar que está vacío.
  2. Eliminar la estructura inicial que enumera el plan.
  3. Crear tipos de bloque, colecciones, campos, definiciones de taxonomías, definiciones de relaciones y campos de byline.
  4. Copiar los archivos multimedia en el almacenamiento del destino y crear los registros de medios.
  5. Escribir términos y bylines.
  6. Escribir revisiones y entradas.
  7. Escribir asignaciones de términos, créditos de byline, referencias de contenido y registros SEO.
  8. Escribir menús, widgets, secciones, redirecciones, comentarios, reacciones y ajustes.
  9. Reconstruir los índices de búsqueda y las cachés, y poner en cola la reindexación del uso de medios.
  10. Verificar el resultado.

Cada llamada a advance ejecuta un paso acotado, que cabe en los límites de solicitud de Cloudflare Workers con D1. El progreso se almacena en el servidor. Una solicitud interrumpida pierde como máximo el paso en curso, y cada escritura es idempotente, por lo que volver a ejecutar un paso no duplica registros.

Mientras otra solicitud está ejecutando un paso, o cuando otra solicitud toma el control de la operación durante un paso, advance devuelve la operación con un nextRequestInMs corto. Un error de almacenamiento o de base de datos se reintenta: la operación registra el error y nextRequestInMs crece con cada fallo consecutivo. Tras fallos repetidos sin progreso, la importación falla.

Las escrituras se bloquean durante una importación

Desde el primer paso de ejecución hasta que termina la importación, EmDash rechaza las solicitudes de escritura a su API con 503 TRANSFER_IMPORT_IN_PROGRESS. Esto abarca el panel de administración, la API REST, las rutas de plugins, el envío público de comentarios, la publicación programada y las escrituras de contenido de los plugins. El inicio de sesión, la gestión de usuarios y tokens de API, los bloqueos de edición de entradas y la propia API de transferencia siguen disponibles. Las solicitudes de lectura no se bloquean.

Las herramientas MCP de escritura, incluidas las herramientas MCP de plugins, fallan con TRANSFER_IMPORT_IN_PROGRESS en el error de herramienta habitual. Las herramientas MCP de solo lectura y las herramientas de transferencia site_* siguen funcionando, de modo que una importación iniciada por MCP se puede reanudar, inspeccionar y completar por MCP.

Reanudar tras una interrupción

La página de administración solo hace avanzar una importación mientras está abierta. Para reanudarla, vuelve a abrir Settings → Transfer, ejecuta emdash site import resume <operation-id> o vuelve a llamar a advance para la misma operación. El servidor continúa desde el último paso completado. Si la solicitud interrumpida seguía reteniendo la operación, la siguiente llamada espera a que caduque esa retención, como máximo cinco minutos.

Una importación fallida o cancelada no se puede reanudar.

Cancelar una importación

Selecciona Cancel import en Settings → Transfer, ejecuta emdash site import cancel <operation-id> o envía POST imports/{id}/cancel. Un paso en curso se detiene después de su lote actual. Cancelar no elimina los registros que ya se escribieron.

Abandonar una importación incompleta

Una importación fallida o cancelada que empezó a escribir sigue bloqueando las escrituras, para que el sitio incompleto no se pueda editar por error. Para levantar el bloqueo, selecciona Abandon import en Settings → Transfer, ejecuta emdash site import abandon <operation-id> o envía POST imports/{id}/abandon. Abandonar conserva los datos importados.

Después de abandonarla, el sitio ya no está vacío, así que no puede recibir otra importación. En su lugar, importa en un sitio recién configurado.

Una importación fallida o cancelada que nunca empezó a escribir no bloquea las escrituras y no es necesario abandonarla.

Verificar el resultado

La verificación vuelve a leer cada registro importado con el mismo código que usa el exportador, aplica las transformaciones declaradas del plan a los registros del paquete y compara ambos. También comprueba el recuento de registros de cada tipo y vuelve a descargar cada archivo multimedia importado para comprobar su digest. Cualquier diferencia hace que la importación falle con TRANSFER_VERIFICATION_FAILED. El errorDetail de la operación enumera hasta 50 de las diferencias.

Una importación correcta produce un 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:…"
}

Un recibo registra que el sitio de destino identificado por targetSiteId contenía exactamente el contenido del paquete identificado por packageDigest, tras aplicar el plan identificado por planDigest, en el momento en que terminó la verificación. El logicalDigest resume los registros verificados.

receiptDigest es el digest SHA-256 del JSON canónico del recibo sin la propiedad receiptDigest. Detecta un recibo que se modificó después de emitirse. Un recibo no está firmado, por lo que no demuestra qué servidor lo emitió. Obtén el recibo del destino mediante una conexión autenticada cuando eso importe.

Un recibo describe el sitio en el momento en que terminó la verificación. No dice nada sobre ediciones posteriores.

Moverse entre bases de datos

Un paquete no depende de la base de datos del origen. Exporta desde SQLite, PostgreSQL o D1 e importa en cualquiera de ellas. Ten en cuenta las siguientes diferencias cuando el destino use PostgreSQL:

  • PostgreSQL almacena los enteros en 32 bits. Un entero fuera de ese rango es un bloqueo integer_out_of_range.
  • PostgreSQL almacena los campos number y los puntos focales de los medios como valores de coma flotante de 32 bits. Los valores que cambian se declaran como float4_rounded, y la verificación compara los valores redondeados.
  • La búsqueda de texto completo solo está disponible en SQLite y D1. Las colecciones con la búsqueda activada se importan con la búsqueda desactivada y se declaran como search_unsupported.

El importador escribe los medios en el backend de almacenamiento del destino con claves de almacenamiento nuevas y reescribe en consecuencia las referencias a medios del contenido, los ajustes y los registros SEO. Una referencia a un archivo multimedia que el origen no tiene se exporta sin cambios y se declara como unknown_storage_key.

Seguridad

  • Trata un paquete como información sensible. Contiene todo el contenido, incluidos borradores y papelera, y las direcciones de correo de autores y comentaristas. Mantenlo fuera de buckets públicos y carpetas compartidas, y elimina las copias que ya no necesites.
  • Trata un paquete como una entrada no fiable. La importación comprueba rutas, tamaños, digests, esquemas de registros, referencias y límites antes de escribir. Nunca ejecuta código ni SQL de un paquete y nunca obtiene URL de uno.
  • Concede el acceso a la transferencia de forma deliberada. La transferencia requiere el rol de administrador. Un token con el ámbito admin puede ejecutar todas las acciones de transferencia, así que da al token de un agente solo el ámbito de transferencia que necesita.
  • Revisa el registro de auditoría. EmDash registra las acciones de transferencia en el registro de auditoría del sitio: transfer_export_create, transfer_import_create, transfer_import_execute, transfer_import_cancel, transfer_import_abandon, transfer_import_complete, transfer_import_fail, transfer_approval_approve y transfer_approval_deny. Cada entrada indica el usuario que actuó y la operación o aprobación (tipo de recurso transfer_operation o transfer_approval). Sus detalles solo contienen ID, digests, recuentos de registros y códigos de error, nunca contenido del paquete. Del mismo modo, los detalles de los errores de transferencia nunca incluyen contenido del paquete.
  • Mantén privada la preparación. EmDash prepara los archivos del paquete bajo el prefijo transfers/ de tu bucket de almacenamiento y se niega a servir ese prefijo a través de su ruta de medios. Si el bucket tiene un dominio público, limítalo a los medios, como con las copias de seguridad. Los archivos preparados se eliminan cuando una operación termina o caduca.

Ámbitos de token

La transferencia usa tres ámbitos de token de API:

ÁmbitoPermite
transfer:exportIniciar, avanzar y descargar exportaciones.
transfer:analyzeCrear importaciones, subir archivos del paquete, analizar y leer planes.
transfer:executeIniciar, avanzar, cancelar y abandonar importaciones.

El ámbito admin incluye los tres, por lo que el token que guarda emdash login puede ejecutar cualquier transferencia. Cada ámbito de transferencia concede solo sus propias acciones, y solo un administrador puede emitir uno. Úsalos para dar a un token un acceso más limitado que admin, por ejemplo, a un agente que puede analizar paquetes pero no exportar ni importar. Consulta la referencia de ámbitos.

Aprobaciones para agentes

Los agentes de IA gestionan las transferencias mediante las herramientas MCP site_*. Las herramientas inician y avanzan las operaciones e informan sobre ellas. Nunca transportan los bytes del paquete, por lo que el usuario de un agente descarga las exportaciones y sube los paquetes con la CLI o la API REST. Todas las herramientas requieren el rol Admin.

Un cliente MCP cuyo token no tiene ni admin ni el ámbito de transferencia correspondiente, como un agente al que solo se le concedió transfer:analyze, no puede iniciar una exportación ni una importación por sí solo. Su llamada a site_export_start o site_import_start crea una solicitud de aprobación pendiente y falla con TRANSFER_APPROVAL_REQUIRED y el ID de la aprobación. Un administrador aprueba o deniega la solicitud en Approval requests dentro de Settings → Transfer, que enumera cada solicitud pendiente con su solicitante, su acción y su hora de caducidad. Los endpoints exclusivos de sesión POST /_emdash/api/admin/transfer/approvals/{id}/approve y …/deny hacen lo mismo. Los tokens de API no pueden aprobar solicitudes. A continuación, el cliente repite la llamada con el ID de la aprobación. Las aprobaciones solo se aplican a estas herramientas MCP; la API REST no tiene parámetro de aprobación.

Una aprobación concede una llamada al usuario que la solicitó, desde el mismo token y con los mismos argumentos. Una aprobación de exportación está vinculada a las opciones de exportación. Una aprobación de importación está vinculada a la operación y a ambos digests, por lo que un plan modificado necesita una aprobación nueva. Una solicitud pendiente caduca a los 15 minutos, y una aprobada, 15 minutos después de la aprobación. El reintento que inicia la operación la consume; si la operación no llega a iniciarse, se puede reintentar con la misma aprobación hasta que caduque. Después, el mismo usuario y el mismo token pueden consultar y hacer avanzar esa operación sin el ámbito.

Concede transfer:export, transfer:execute o admin al token de un agente solo cuando el agente deba ejecutar transferencias sin que una persona apruebe cada una.

Límites

LímiteValor
manifest.json8 MiB
Un registro1.900.000 bytes
Un archivo de registros o de índice4 MiB y 1.000 registros
Registros por paquete5.000.000
Archivos por paquete1.000.000
Profundidad de anidamiento JSON64
Un archivo multimediaEl maxUploadSize del destino, 50 MiB por defecto

El endpoint capabilities indica los valores que aplica el sitio.

Para proveedores de alojamiento

Un plano de control de alojamiento puede pasar el sitio de un cliente a producción usando solo la API REST:

  1. Aprovisiona un sitio EmDash nuevo con su almacenamiento, sus idiomas y su maxUploadSize, y completa la configuración. Comprueba que capabilities indica portableDomain.empty como true.

  2. Emite un token para el plano de control con transfer:analyze y transfer:execute. Mantenlo fuera de cualquier agente o herramienta de creación de sitios.

  3. Ejecuta la importación y aplica tu propia política a las advertencias del plan antes de ejecutarla. Rechaza cualquier plan con bloqueos.

  4. Obtén el recibo y compruébalo antes de promocionar el sitio:

    • verification es verified;
    • packageDigest es el digest del paquete que querías publicar;
    • planDigest es el plan que aceptaste;
    • targetSiteId es el sitio que estás a punto de promocionar; y
    • receiptDigest coincide con el JSON canónico del recibo.
  5. Promociona el sitio, por ejemplo, dirigiendo su dominio hacia él.

Mantén el destino inaccesible hasta que el paso 4 tenga éxito. EmDash no oculta a los visitantes un sitio importado parcialmente.

Solución de problemas

Los errores de transferencia usan códigos estables. El estado HTTP aparece junto a cada código.

CódigoEstadoQué hacer
TRANSFER_TARGET_NOT_EMPTY409El destino ya tiene contenido. Importa en un sitio recién configurado. Settings → Transfer y capabilities enumeran lo que hace que el sitio no sea apto.
TRANSFER_IMPORT_IN_PROGRESS503Hay una importación en curso en este sitio, o una importación incompleta sigue bloqueando las escrituras. Espera a que termine o abandona una importación fallida o cancelada.
TRANSFER_FENCE_CHECK_FAILED503EmDash no pudo comprobar si hay una importación en curso. Reintenta la escritura.
TRANSFER_EXPORT_CONCURRENT_WRITES409El sitio no dejó de cambiar mientras se ejecutaba la exportación. Vuelve a exportar cuando haya poca actividad de edición.
TRANSFER_EXPIRED410Los archivos de la exportación se eliminaron tras siete días, o una importación no se ejecutó en 24 horas. Empieza de nuevo.
TRANSFER_FILE_MISSING422Algunos archivos declarados no se han subido. Sube todo lo que enumere imports/{id}/missing.
TRANSFER_FILE_NOT_DECLARED422La ruta de subida no está en el paquete. Sube solo las rutas enumeradas.
TRANSFER_FILE_SIZE_MISMATCH422Content-Length o los bytes subidos difieren del tamaño declarado. Sube el archivo sin modificarlo.
TRANSFER_FILE_DIGEST_MISMATCH422Los bytes subidos difieren del digest declarado, o un archivo de la exportación cambió después de la exportación. Sube el archivo original o vuelve a exportar.
TRANSFER_LIMIT_EXCEEDED413Un archivo supera un límite. Para los medios, aumenta el maxUploadSize del destino.
TRANSFER_MANIFEST_INVALID422El cuerpo de la solicitud no es un manifiesto válido. Envía manifest.json byte a byte.
TRANSFER_UNSUPPORTED_FORMAT422Actualiza EmDash en el destino.
TRANSFER_UNSUPPORTED_FEATURE422Actualiza EmDash en el destino.
TRANSFER_CONTAINER_INVALID422El archivo .emdash no es un archivo de paquete válido. Vuelve a descargarlo.
TRANSFER_PLAN_BLOCKED409El plan tiene bloqueos. Consulta revisar el plan de importación.
TRANSFER_PACKAGE_DIGEST_MISMATCH409El digest no coincide con el paquete preparado. Usa el packageDigest de la operación.
TRANSFER_PLAN_DIGEST_MISMATCH409El plan cambió desde que lo revisaste. Lee el plan actual y revísalo de nuevo.
TRANSFER_DECISIONS_INVALID422Una decisión nombra un principal desconocido o un usuario de destino que no existe. Corrige la asignación.
TRANSFER_INVALID_STATE409La operación no está en un estado que permita la solicitud. Lee la operación y actúa según su state.
TRANSFER_LEASE_ACTIVE409Otra solicitud está ejecutando un paso. Espera y reintenta.
TRANSFER_IDEMPOTENCY_CONFLICT409La Idempotency-Key ya se usó para una exportación con otras opciones o para una importación de otro paquete. Usa una clave nueva.
TRANSFER_RUNTIME_MISMATCH409Una versión incompatible de EmDash inició la operación. Termínala con la versión que la inició o inicia una nueva.
TRANSFER_VERIFICATION_FAILED422El sitio importado no coincide con el paquete. Lee las diferencias en errorDetail, abandona la importación e importa en un sitio nuevo.
TRANSFER_APPROVAL_REQUIRED403Un administrador debe aprobar la solicitud. Consulta aprobaciones para agentes.
TRANSFER_APPROVAL_INVALID403La aprobación es desconocida, se denegó, caducó, ya se usó o está vinculada a otros parámetros. Solicita una nueva.
TRANSFER_SCHEMA_UNCLASSIFIED500La base de datos tiene una tabla o columna que el exportador no reconoce. Ejecuta la versión de EmDash que corresponde a las migraciones de la base de datos.
INSUFFICIENT_SCOPE403El token no tiene ni admin ni el ámbito de transferencia que necesita la solicitud. Emite un token con ese ámbito.