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
| Mecanismo | Finalidad | Importable | Archivos multimedia | Usuarios y secretos |
|---|---|---|---|---|
| Archivo seed | Inicializar un modelo de contenido y contenido de ejemplo | Sí, con semántica de seed | No | No |
| Instantánea de vista previa | Poblar el renderizado aislado de vistas previas | Solo vista previa | No | No |
| Copia de seguridad JSON | Inspeccionar el estado seleccionado con forma de base de datos | No | No | No |
| Copia de seguridad en bruto de base de datos y medios | Recuperar un despliegue | Restauración en el mismo tipo de base de datos | Copia aparte | Sí |
| Paquete de sitio | Mover un sitio a otro sitio EmDash | Sí, en un sitio vacío | Sí | 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 comolocale_recased. - Un límite de subida suficientemente grande. Cada archivo multimedia debe caber en el
maxUploadSizedel 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
-
Abre Settings → Transfer. La página está disponible para los administradores.
-
En la sección Export, desactiva Include comments para dejar fuera los comentarios y las reacciones.
-
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.
-
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.
-
Inicia la exportación. Para dejar fuera los comentarios y las reacciones, envía
{ "comments": false }como cuerpo. Una cabeceraIdempotency-Keyhace que una solicitud reintentada devuelva la misma exportación en lugar de iniciar otra. Reutilizar una clave con opciones diferentes falla con409 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" -
Avanza la exportación hasta que
nextRequestInMsseanull. Espera entre llamadas el número de milisegundos devuelto.operation.progressindica los pasosdoneytotal, losrecordsescritos hasta el momento ybytesDoneybytesTotaluna 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" -
Comprueba que
operation.stateseacomplete. Una exportaciónfailedincluye el motivo enoperation.errorCode. -
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
-
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.
-
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ó. -
Cuando termina la subida, el sitio analiza el paquete. Puedes salir de la página y volver más tarde.
-
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.
-
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.
-
En Site identity, elige si usar el título y el eslogan del sitio del paquete o mantener los de este sitio.
-
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.
-
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 editor@example.com=editor@example.com \
--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.
-
Crea la importación. Envía los bytes sin modificar de
manifest.jsoncomo 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 -
Sube cada archivo que falte a
imports/{id}/files/{path}. La cabeceraContent-Lengthdebe 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.ndjsonSubir un archivo de índice declara los archivos de registros y de medios que enumera. Vuelve a solicitar
imports/{id}/missingdespué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. -
Analiza el paquete. Llama a
imports/{id}/analyzehasta quenextRequestInMsseanull. La respuesta final contiene elplany suplanDigest.curl -X POST https://new.example.com/_emdash/api/admin/transfer/imports/$IMPORT_ID/analyze \ -H "Authorization: Bearer $EMDASH_TOKEN" -
Revisa el plan y lee cada bloqueo, advertencia y transformación. Consulta revisar el plan de importación.
-
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" } }' -
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:…" }' -
Avanza la importación hasta que
nextRequestInMsseanull, 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" -
Comprueba que
operation.stateseacompletey, a continuación, lee el recibo desdeimports/{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_BLOCKEDhasta que el plan no tenga ninguno. Cambia las asignaciones de principales para resolver unprincipal_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ódigo | Significado |
|---|---|
package_invalid | Un archivo o una ruta del paquete no supera la validación. |
unsupported_format | El destino no admite el formato o la versión de formato del paquete. |
unsupported_feature | El paquete requiere una función que el destino no admite. |
limit_exceeded | Un archivo o registro del paquete supera un límite. |
file_missing | No se ha subido un archivo declarado del paquete. |
file_mismatch | El tamaño o el digest de un archivo del paquete no coincide con su declaración. |
record_invalid | Un registro está mal formado o no es JSON canónico. |
record_count_mismatch | El número de registros de un tipo difiere del manifiesto. |
record_order_invalid | Los registros están desordenados, o un padre aparece después de su hijo. |
duplicate_id | Dos registros del mismo tipo comparten un ID. |
dangling_reference | Un 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_cycle | Un término, comentario o elemento de menú es su propio padre. |
media_ref_invalid | El contenido hace referencia a un registro de medio que no está en el paquete. |
media_blob_missing | El archivo de un registro de medio no está en el paquete. |
media_blob_too_large | Un archivo multimedia es mayor que el maxUploadSize del destino. |
target_not_empty | El destino ya tiene contenido. El detail del bloqueo indica lo que encontró. |
locale_not_configured | El paquete usa un idioma que la configuración de i18n del destino no incluye. |
field_type_unknown | Un campo o campo de byline usa un tipo que el destino no admite. |
principal_conflict | Las asignaciones de principales darían a un usuario dos bylines en el mismo idioma. |
integer_out_of_range | Un entero está fuera del rango de enteros de la base de datos de destino. PostgreSQL almacena los enteros en 32 bits. |
value_constraint_violation | Un valor que la API de administración rechazaría. Consulta la lista siguiente. |
unique_violation | Un 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
httpohttps, 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
httpohttpsni una ruta del sitio; y - una URL de elemento de menú con un esquema que los menús no permiten.
Advertencias
| Código | Significado |
|---|---|
media_provider_external | El contenido usa medios de un proveedor externo. Se conserva la referencia; los archivos no se copian. |
media_row_missing | Un ajuste hace referencia a un medio que no está en el paquete. |
soft_reference_dangling | Una referencia opcional no se resuelve en un registro del paquete. |
redirect_loops_unchecked | El paquete tiene demasiadas redirecciones para comprobar los bucles antes de importar. Una redirección que cerraría un bucle se importa desactivada. |
issues_truncated | Se 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ódigo | Significado |
|---|---|
orphan_dropped | Se omitieron registros cuyo padre ya no existía en el sitio de origen, como una revisión de una entrada eliminada. |
soft_orphan_dropped | Se 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_nulled | Se eliminó una referencia a un registro que falta, como la carpeta eliminada de un archivo multimedia. |
avatar_nulled | Un 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_dropped | Se omitieron los medios que no estaban listos, como una subida incompleta. |
media_ref_unlinked | Se eliminaron del contenido las referencias a medios que no están en el paquete. |
media_url_relativized | Las 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_dropped | Se omitieron redirecciones duplicadas para la misma ruta de origen. Se conservó una redirección por ruta de origen. |
unknown_storage_key | Hay 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ódigo | Significado |
|---|---|
principal_mapped | Las referencias a principales se reescriben con los usuarios de destino asignados. |
principal_unmapped | Se eliminan las referencias a principales sin asignar. |
seeded_scaffold_removed | La estructura inicial del destino se elimina antes de que la importación escriba. El plan enumera cada elemento. |
redirect_loop_disabled | Las redirecciones que forman un bucle se importan desactivadas. |
search_unsupported | La búsqueda se desactiva para las colecciones enumeradas porque el destino usa PostgreSQL. |
float4_rounded | Los valores decimales se redondean a la precisión de las columnas real de PostgreSQL del destino. |
locale_recased | Los 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:
- Reservar el destino y volver a comprobar que está vacío.
- Eliminar la estructura inicial que enumera el plan.
- Crear tipos de bloque, colecciones, campos, definiciones de taxonomías, definiciones de relaciones y campos de byline.
- Copiar los archivos multimedia en el almacenamiento del destino y crear los registros de medios.
- Escribir términos y bylines.
- Escribir revisiones y entradas.
- Escribir asignaciones de términos, créditos de byline, referencias de contenido y registros SEO.
- Escribir menús, widgets, secciones, redirecciones, comentarios, reacciones y ajustes.
- Reconstruir los índices de búsqueda y las cachés, y poner en cola la reindexación del uso de medios.
- 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
numbery los puntos focales de los medios como valores de coma flotante de 32 bits. Los valores que cambian se declaran comofloat4_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
adminpuede 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_approveytransfer_approval_deny. Cada entrada indica el usuario que actuó y la operación o aprobación (tipo de recursotransfer_operationotransfer_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:
| Ámbito | Permite |
|---|---|
transfer:export | Iniciar, avanzar y descargar exportaciones. |
transfer:analyze | Crear importaciones, subir archivos del paquete, analizar y leer planes. |
transfer:execute | Iniciar, 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ímite | Valor |
|---|---|
manifest.json | 8 MiB |
| Un registro | 1.900.000 bytes |
| Un archivo de registros o de índice | 4 MiB y 1.000 registros |
| Registros por paquete | 5.000.000 |
| Archivos por paquete | 1.000.000 |
| Profundidad de anidamiento JSON | 64 |
| Un archivo multimedia | El 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:
-
Aprovisiona un sitio EmDash nuevo con su almacenamiento, sus idiomas y su
maxUploadSize, y completa la configuración. Comprueba quecapabilitiesindicaportableDomain.emptycomotrue. -
Emite un token para el plano de control con
transfer:analyzeytransfer:execute. Mantenlo fuera de cualquier agente o herramienta de creación de sitios. -
Ejecuta la importación y aplica tu propia política a las advertencias del plan antes de ejecutarla. Rechaza cualquier plan con bloqueos.
-
Obtén el recibo y compruébalo antes de promocionar el sitio:
verificationesverified;packageDigestes el digest del paquete que querías publicar;planDigestes el plan que aceptaste;targetSiteIdes el sitio que estás a punto de promocionar; yreceiptDigestcoincide con el JSON canónico del recibo.
-
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ódigo | Estado | Qué hacer |
|---|---|---|
TRANSFER_TARGET_NOT_EMPTY | 409 | El 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_PROGRESS | 503 | Hay 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_FAILED | 503 | EmDash no pudo comprobar si hay una importación en curso. Reintenta la escritura. |
TRANSFER_EXPORT_CONCURRENT_WRITES | 409 | El sitio no dejó de cambiar mientras se ejecutaba la exportación. Vuelve a exportar cuando haya poca actividad de edición. |
TRANSFER_EXPIRED | 410 | Los 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_MISSING | 422 | Algunos archivos declarados no se han subido. Sube todo lo que enumere imports/{id}/missing. |
TRANSFER_FILE_NOT_DECLARED | 422 | La ruta de subida no está en el paquete. Sube solo las rutas enumeradas. |
TRANSFER_FILE_SIZE_MISMATCH | 422 | Content-Length o los bytes subidos difieren del tamaño declarado. Sube el archivo sin modificarlo. |
TRANSFER_FILE_DIGEST_MISMATCH | 422 | Los 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_EXCEEDED | 413 | Un archivo supera un límite. Para los medios, aumenta el maxUploadSize del destino. |
TRANSFER_MANIFEST_INVALID | 422 | El cuerpo de la solicitud no es un manifiesto válido. Envía manifest.json byte a byte. |
TRANSFER_UNSUPPORTED_FORMAT | 422 | Actualiza EmDash en el destino. |
TRANSFER_UNSUPPORTED_FEATURE | 422 | Actualiza EmDash en el destino. |
TRANSFER_CONTAINER_INVALID | 422 | El archivo .emdash no es un archivo de paquete válido. Vuelve a descargarlo. |
TRANSFER_PLAN_BLOCKED | 409 | El plan tiene bloqueos. Consulta revisar el plan de importación. |
TRANSFER_PACKAGE_DIGEST_MISMATCH | 409 | El digest no coincide con el paquete preparado. Usa el packageDigest de la operación. |
TRANSFER_PLAN_DIGEST_MISMATCH | 409 | El plan cambió desde que lo revisaste. Lee el plan actual y revísalo de nuevo. |
TRANSFER_DECISIONS_INVALID | 422 | Una decisión nombra un principal desconocido o un usuario de destino que no existe. Corrige la asignación. |
TRANSFER_INVALID_STATE | 409 | La operación no está en un estado que permita la solicitud. Lee la operación y actúa según su state. |
TRANSFER_LEASE_ACTIVE | 409 | Otra solicitud está ejecutando un paso. Espera y reintenta. |
TRANSFER_IDEMPOTENCY_CONFLICT | 409 | La 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_MISMATCH | 409 | Una versión incompatible de EmDash inició la operación. Termínala con la versión que la inició o inicia una nueva. |
TRANSFER_VERIFICATION_FAILED | 422 | El sitio importado no coincide con el paquete. Lee las diferencias en errorDetail, abandona la importación e importa en un sitio nuevo. |
TRANSFER_APPROVAL_REQUIRED | 403 | Un administrador debe aprobar la solicitud. Consulta aprobaciones para agentes. |
TRANSFER_APPROVAL_INVALID | 403 | La aprobación es desconocida, se denegó, caducó, ya se usó o está vinculada a otros parámetros. Solicita una nueva. |
TRANSFER_SCHEMA_UNCLASSIFIED | 500 | La 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_SCOPE | 403 | El token no tiene ni admin ni el ámbito de transferencia que necesita la solicitud. Emite un token con ese ámbito. |