Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Distribuir plugins

Has creado un plugin; ahora necesita salir de tu máquina sin pedir a las personas que lo instalan que confíen ciegamente en ti. La historia de distribución de ZeroClaw tiene dos capas independientes: firmas de manifiesto Ed25519 (quién lo publicó) e instalación desde el registro (cómo llega ahí). Esta página cubre ambas, verificada frente a crates/zeroclaw-plugins/src/signature.rs, src/plugin_registry.rs y la ruta de instalación en host.rs.

Firma

Qué está firmado

La firma cubre los bytes canónicos del manifiesto. El host analiza el TOML, elimina únicamente las entradas raíz exactas signature y publisher_key, conserva el resto del documento y elimina las líneas vacías finales (canonical_manifest_bytes en signature.rs). Esto permite incrustar la firma en el propio manifiesto: firma el manifiesto sin esos campos raíz y, después, añádelos; la verificación los elimina antes de comprobarla.

Dos consecuencias que conviene conocer:

  • El componente .wasm en sí no está cubierto por la firma. Lo que la firma acredita es el manifiesto: el nombre, la versión, las capacidades y los permisos que respalda un publicador. Combínalo con un digest sha256 del registro (más abajo) cuando la integridad del artefacto importe durante el tránsito.
  • Los campos anidados con nombres como config_schema.properties.signature y, de forma similar, los campos raíz con el prefijo signature_algorithm siguen estando firmados. Reformatear o reordenar el contenido conservado invalida la firma. Firme al final, después de finalizar el manifiesto.
  • Los marcadores de exposición de configuración como config_schema.properties.api_token.x-secret forman, por tanto, parte de la política cubierta por la firma. Cambiar una propiedad de herramienta o canal entre la exposición pública de configuración y el acceso con ámbito a secrets.get requiere volver a compilar y volver a firmar.
  • Los paquetes firmados por el antiguo canonicalizador basado en prefijos solo necesitan volver a firmarse si dependían de uno de esos casos extremos o de una decoración TOML asociada a un campo eliminado. Los manifiestos ordinarios conservan el mismo contenido firmado.

Claves y proceso

La firma usa Ed25519 mediante las mismas primitivas ring con las que verifica el host. La firma está codificada en base64url (sin relleno); la clave pública está codificada en hexadecimal. La crate expone toda la cadena de herramientas (signature.rs): generate_signing_key produce un par de claves PKCS#8 y su clave pública hex, sign_manifest produce la firma base64url sobre los bytes canónicos, y public_key_hex recupera la clave pública a partir de una clave privada almacenada. Hoy no hay un envoltorio de CLI para la firma; los publicadores invocan estas funciones desde un pequeño helper en Rust en su pipeline de publicación.

El manifiesto firmado incluye entonces dos campos adicionales de nivel raíz: signature (el valor en base64url) y publisher_key (tu clave pública hexadecimal). Coloca ambos antes del primer encabezado de tabla, incluido [config_schema]; añadirlos después de un encabezado de tabla hace que sean miembros de esa tabla según las reglas de TOML y el host verá un manifiesto sin firmar.

name = "my-plugin"
version = "0.1.0"
signature = "<base64url-signature>"
publisher_key = "<hex-public-key>"

[config_schema]
type = "object"
properties = {}
additionalProperties = false

Los operadores que quieran confiar en ti deben añadir esa clave hexadecimal a su lista plugins.security.trusted_publisher_keys:

zeroclaw config set plugins.security.signature_mode strict
zeroclaw config set plugins.security.trusted_publisher_keys '["<your-key-hex>"]'

Cómo se comporta la verificación

La verificación se ejecuta tanto en la detección como en la instalación (enforce_signature_policy llamado desde host.rs); la detección omite un plugin que falla y registra el error, la instalación devuelve el error. La matriz de modos, desde el lado del operador:

ModoSin firmarFirmado, clave no confiableFirmado, la firma no es válidaFirmado y confiable
disabledcargascargas, no comprobadascargas, no comprobadascargas, no comprobadas
permissivese carga con advertenciase carga con advertenciase carga con advertenciacargas, verificado
strictrechazadorechazadorechazadocargas

Toma nota de lo que significa strict para ti como publicador: un operador en modo estricto solo carga tu plugin si tu clave exacta está en su conjunto de confianza y los bytes del manifiesto se verifican. Cualquier edición del manifiesto posterior a la firma, hecha por ti o por cualquiera en la ruta de distribución, rompe la instalación. Ese es el objetivo.

Publicación en el registro

La ruta de instalación es el directorio local de plugins; un registry es únicamente un índice JSON consultado en tiempo de ejecución (zeroclaw plugin search / install). Ambos comandos existen solo en binarios con el host de plugins compilado (consulte build features); los binarios de versión precompilados no lo incluyen. El índice predeterminado es el registry.json del repositorio zeroclaw-labs/zeroclaw-plugins; los registries privados se configuran mediante una URL (--registry <url> por comando, o la variable de entorno ZEROCLAW_PLUGIN_REGISTRY_URL, resueltos en ese orden según registry_url en src/plugin_registry.rs).

Una entrada del registro (PluginRegistryEntry en crates/zeroclaw-plugins/src/registry.rs) contiene: name, version, description y author opcionales, capabilities, la url del archivo y un digest sha256 opcional del zip.

El contrato de archivo

zeroclaw plugin install <name> resuelve la entrada, descarga el zip, verifica el digest cuando está presente, extrae de forma segura y entrega el directorio extraído a la misma ruta PluginHost::install que usa una instalación local. La extracción es defensiva por diseño (src/plugin_registry.rs), y tu archivo comprimido debe sobrevivirla:

  • El ZIP debe contener manifest.toml en la raíz o exactamente un directorio de plugin anidado que contenga uno. Cero manifestos o más de uno hacen que el archivo sea rechazado.
  • Se rechazan los nombres de entrada con traversal de rutas, rutas absolutas o prefijos de unidad de Windows.
  • La descarga tiene un límite mientras se transmite (50 MiB), por lo que un servidor que omita Content-Length no puede forzar un almacenamiento en búfer ilimitado; la extracción también tiene el mismo límite, de modo que una bomba zip no puede expandirse sin límite.

Resolución de versiones: cuando el instalador recibe un nombre sin especificar, elige la última entrada coincidente en el índice; un name@version fijado selecciona exactamente esa versión. Ordena los nombres repetidos en tu registro intencionalmente, primero los más antiguos.

La búsqueda no es un límite de confianza

zeroclaw plugin search es un descubrimiento no autenticado sobre el índice; nunca instala, habilita ni ejecuta nada. La instalación es donde ocurre la seguridad: verificación de digest, extracción segura, validación del manifiesto y la política de firma del operador, idéntica a una instalación desde ruta local. Publique en consecuencia: suponga que todo antes de la instalación es transporte no confiable.

La lista de verificación del editor

[!IMPORTANTE] Los archivos .wasm y .cwasm compilados son artefactos binarios, a menudo de varios megabytes cada uno. No los incluyas en un árbol de código fuente de git sin Git LFS: cada recompilación confirmada como un blob plano infla el historial del repositorio de forma permanente, y las herramientas de git diff/revisión no pueden con ellos. Trátalos como cualquier otro resultado de compilación: añade target/ y *.wasm/*.cwasm a .gitignore, y distribúyelos mediante un artefacto de lanzamiento o un archivo del registro de plugins en su lugar. Si un artefacto realmente debe vivir en el árbol, registra el patrón con LFS (git lfs track "*.wasm") antes del primer commit.

  1. Finaliza el manifiesto: nombre, versión, capacidades y el conjunto de permisos más restringido que usa el código.
  2. Construye el componente; para los paquetes de habilidades, valida el frontmatter en cada SKILL.md (la detección impone name y description).
  3. Firmar: generar o cargar su clave Ed25519, firmar los bytes canónicos del manifiesto, incrustar signature y publisher_key.
  4. Comprima el directorio del plugin (un manifiesto, sin trucos de ruta, menos de 50 MiB).
  5. Calcule el SHA-256 del zip y publique la entrada del registro con el digest.
  6. Publica el hex de tu clave pública en algún lugar donde los operadores puedan verificarlo de forma independiente del registro (tu repositorio, tu sitio). La clave, no el registro, es lo que los operadores en modo strict confían.
  7. En cada versión: incrementa version, vuelve a firmar (la línea de versión está dentro de los bytes canónicos), vuelve a generar el digest, y añade la nueva entrada después de la anterior.