Ir al contenido

Seguridad

arcane-mcp puede hacer todo lo que la credencial que tiene puede hacer en Arcane, y eso incluye borrar volúmenes, limpiar imágenes, restaurar copias de seguridad y gestionar usuarios. Estos controles deciden cuánto de todo eso recibe el asistente y cuándo tiene que preguntar antes. Viven en el catálogo y en el único despachador al que llaman todas las superficies, así que se aplican igual en las superficies dynamic, meta e individual. La parte del despliegue (quién tiene cada credencial, cómo se endurece el transporte HTTP, la cadena de suministro) está en Seguridad del despliegue.

Cada llamada pasa por los mismos pasos, en este orden:

  1. rechazar una acción que se sabe que la versión de Arcane de la instancia todavía no tiene (consulta Una acción necesita un Arcane más reciente);
  2. validar los argumentos contra el esquema de entrada de la acción;
  3. ante una acción destructiva sin confirm: true, preguntar al usuario mediante elicitación MCP si el cliente lo admite y, si no, rechazarla;
  4. en modo seguro, responder a una acción que modifica con una vista previa;
  5. ejecutar la acción dentro del plazo por acción y presentar el resultado;
  6. escribir un registro de auditoría.

Qué cuenta como modificación y como destrucción

Sección titulada «Qué cuenta como modificación y como destrucción»

Cada acción se clasifica al generar el catálogo:

  • Lectura — las rutas GET de Arcane, más las acciones de flujos en directo que solo escuchan (logs, estadísticas, el canal de eventos).
  • Escritura (modifica) — cualquier otro método: crear, actualizar, arrancar, parar, descargar, desplegar…
  • Destructiva — las rutas DELETE y las acciones cuyo verbo elimina o cambia algo de forma irreversible: delete, remove, prune, kill, destroy, restore, purge, reset, wipe.

api/actions.yaml corrige la regla en cualquier sentido cuando el verbo engaña; los redespliegues, por ejemplo, se marcan ahí como destructivos porque recrean los contenedores, y las comprobaciones de actualización de imágenes (image_update.check_ref, image_update.check_by_id) son rutas GET marcadas como de escritura porque guardan sus resultados, así que el modo solo lectura las oculta y el modo seguro las previsualiza. La referencia de herramientas muestra el tipo de cada acción.

Una acción destructiva no hace nada hasta que se aprueba. Hay dos formas de aprobarla: que la llamada lleve confirm: true, o que el usuario acepte la pregunta de confirmación que el servidor le envía mediante elicitación MCP.

confirm es un argumento de nivel superior en todas las superficies —junto a action y params en dynamic y meta, junto a los parámetros en individual—, así que no puede colarse como parámetro de Arcane. Las anotaciones MCP de las herramientas activan además destructiveHint, lo que permite a los clientes que lo admiten preguntar al usuario antes incluso de hacer la llamada.

Algunas rutas de Arcane reciben la operación, o una opción que destruye, como argumento, así que una misma acción mezcla llamadas inofensivas y destructivas. En esas, solo las llamadas destructivas necesitan aprobación; el resto se ejecutan como cualquier escritura:

AcciónNecesita aprobación cuandoSe ejecuta sin ella
podman_pod.lifecycleaction es killstart, stop, restart, pause, unpause (como container.stop y similares)
podman_quadlet.lifecycleaction es stop o restart (la unidad de un quadlet .container borra su contenedor y sus volúmenes anónimos al pararse)start
podman.kube_playreplace es true (primero se eliminan los pods existentes con los mismos nombres)sin replace o con false
podman.auto_update_rundryRun falta o es falsedryRun: true (solo informa)
updater.rundryRun falta o es falsedryRun: true (solo informa)
project.uprecreateVolumes, forceRecreate o removeOrphans es true (volúmenes recreados con pérdida de sus datos, contenedores recreados o eliminados, como project.redeploy)un up normal (los tres ausentes o false); en un proyecto gestionado por GitOps Arcane elimina los huérfanos en cada despliegue, así que un up normal los elimina igualmente sin aprobación
environment.updateregenerateApiKey es true (el agente que usa la clave antigua queda fuera)cualquier otro cambio (nombre, URL, activación…)
swarm_node.agent_deployment_createrotate es true (se borra el token antiguo)reutilizar o crear el entorno del nodo
environment.pair_agentrotate es true (un manager que use el token antiguo pierde el acceso)leer el token de agente local (se crea si no existe)

La decisión se toma sobre los argumentos ya validados, igual en todas las superficies, antes de que la elicitación, el modo seguro y el registro de auditoría vean la llamada. Un argumento ausente cuenta como el valor que Arcane usa por defecto, y un valor que la regla no puede comparar se trata con prudencia: necesita aprobación. Lo que no cambia: esa acción conserva destructiveHint (una indicación no debe prometer menos de lo que una llamada puede hacer), su herramienta individual mantiene el argumento confirm, el modo de solo lectura la sigue ocultando (modifica el estado en cualquier caso), y su nota de uso y la referencia de herramientas dicen qué valores necesitan confirm. Una llamada rechazada lo indica: “podman_pod.lifecycle is destructive when action is kill, as in this call”.

Cuando el cliente declaró la capacidad de elicitación (modo formulario) al inicializar, una llamada destructiva sin confirm: true no falla: el servidor pregunta directamente al usuario, en la propia interfaz del cliente, antes de que nada llegue a Arcane. La pregunta nombra la acción, sobre qué actúa y qué supone aceptar:

Run volume.delete (Delete a volume) on volumeName="data", environmentId="0" (default), force=true?
This is destructive: it deletes or replaces state in Arcane and may not be reversible.
Accept to run it now; decline to do nothing.
  • Aceptar ejecuta la acción y devuelve su resultado como siempre.

  • Rechazar o descartar devuelve un resultado que no es un error y dice que no se hizo nada, para que el modelo no lo reintente por su cuenta:

    The user declined to run volume.delete (Delete a volume) when asked to confirm it.
    Nothing was done. Do not retry it unless the user asks again.
  • La pregunta muestra lo que se va a enviar: todos los argumentos, primero los obligatorios y después el entorno (marcado (default) cuando la llamada depende de él), seis como máximo con +N more para el resto. Los objetos enumeran sus campos (volumes={enabled=true}) y las listas, su longitud (resourceIds=[3 items]).

  • Los valores se muestran de forma que no puedan reescribir la pregunta: cada cadena se pliega en una sola línea, se le quitan las secuencias de terminal y los caracteres de control y bidireccionales, se recorta a 80 caracteres y se pone entre comillas, con cualquier carácter invisible que quede escapado. El contenido de ficheros (composeContent, envContent…) y las cadenas de más de 200 bytes se muestran como su tamaño (envContent=(124 bytes)).

  • El valor de un argumento cuyo nombre sugiere una credencial (que contiene key, token, secret, password, recovery, credential o passphrase, como recoveryKey o stepUpToken) se muestra como (hidden). Los booleanos se muestran siempre (rotateManagerToken=true dice qué ocurre, no es un secreto), y los nombres que terminan en Id o Name (secretId) identifican algo y se muestran.

  • confirm: true sigue saltándose la pregunta: al modelo se le indicó que preguntara al usuario antes de ponerlo.

  • Si el cliente no responde en 5 minutos, o la petición de elicitación falla, el servidor vuelve al rechazo de abajo con una nota que explica el motivo, y no se ejecuta nada.

  • Con el protocolo 2026-07-28 y posteriores, en los que un servidor no puede enviar peticiones propias mientras atiende una llamada, la pregunta viaja en el resultado de la llamada como petición de entrada (peticiones de varios intercambios) y el cliente repite la llamada con la respuesta. La respuesta queda ligada a la acción y a sus argumentos: no aprueba una llamada distinta.

  • ARCANE_MCP_ELICITATION=off desactiva la pregunta; todos los clientes reciben entonces el rechazo.

Sin esa capacidad (o con la elicitación desactivada), el servidor responde:

container.kill is destructive (Kill container). Nothing was done.
Repeat the call with confirm: true once the user has approved it.

El modelo pregunta entonces al usuario en la conversación y repite la llamada con confirm: true.

Cuando una llamada hace más de lo que muestran sus argumentos, su acción lleva una nota que se añade, palabra por palabra e igual en todas las superficies, a la pregunta de confirmación, a la negativa anterior y a la vista previa del modo seguro (como confirmNote). Solo aparece en las llamadas que necesitan aprobación, y solo cuando se cumple su condición. La nota está en inglés, como el resto de mensajes del servidor:

AcciónSe muestra cuandoNota
updater.rundryRun y automatic faltan o son falseLos contenedores que renunciaron a las actualizaciones (etiqueta com.getarcaneapp.arcane.updater=false o el ajuste autoUpdateExcludedContainers) y tienen una actualización pendiente también se recrean, con o sin resourceIds; pasa automatic=true para dejarlos en paz.
updater.container_updatesiempreEl contenedor se actualiza aunque haya renunciado a las actualizaciones (misma etiqueta o ajuste).

Arcane v2.17.1 hizo que las ejecuciones manuales del actualizador ignoren esas renuncias; la nota evita que una aprobación abarque sin aviso contenedores que el usuario había excluido.

ARCANE_MCP_READ_ONLY=true (o --read-only) elimina del catálogo todas las acciones que modifican antes de construir ninguna superficie. El asistente no las ve: no aparecen en los resultados de arcane_find_action, en los enums de las meta-herramientas, en la lista de herramientas individuales ni en arcane://tools. Si el modelo nombra una de todos modos, en la superficie dynamic (arcane_execute_action) o en la meta (arcane_image con la acción prune), se le explica por qué no está disponible (en la superficie individual la herramienta sencillamente no existe, así que el cliente recibe su error de herramienta desconocida y no se escribe ningún registro de auditoría):

action "image.prune" is not available: the server runs in read-only mode (ARCANE_MCP_READ_ONLY)

Úsalo para asistentes que deban observar y diagnosticar, nunca cambiar. Combínalo con una clave de API de un usuario cuyo rol tampoco pueda escribir, para que el límite se mantenga incluso fuera de este servidor.

ARCANE_MCP_SAFE_MODE=true (o --safe-mode) mantiene visibles las acciones que modifican, pero no las envía. Una llamada que modifica —tras la validación y la confirmación— recibe como respuesta una vista previa de lo que se habría enviado:

{
"safeMode": true,
"executed": false,
"destructive": false,
"action": "container.restart",
"params": { "containerId": "web-1" },
"request": "POST /api/environments/0/containers/web-1/restart",
"note": "Safe mode is on (ARCANE_MCP_SAFE_MODE or --safe-mode): this mutating action was not sent to Arcane."
}

Ese es el contenido estructurado. El texto que lee el modelo presenta la misma vista previa para personas: un encabezado «Safe mode preview», una línea en negrita NOT EXECUTED, la línea de la petición, la consulta, si la llamada es destructiva y su nota de confirmación si la tiene, y después los parámetros y el cuerpo como bloques JSON:

## Safe mode preview: Restart a container (container.restart)
**NOT EXECUTED.** Safe mode is on (ARCANE_MCP_SAFE_MODE or --safe-mode): this mutating action was not sent to Arcane. Nothing changed.
- **Request**: `POST /api/environments/0/containers/web-1/restart`
- **Destructive**: no

Se incluyen los cuerpos JSON (hasta 2.000 caracteres) y las cadenas de consulta, con los valores que parecen credenciales (contraseñas, claves, tokens, el data de un secreto) mostrados como (hidden), igual que en la pregunta de confirmación. Las acciones de lectura se ejecutan con normalidad. El modo seguro nunca pregunta al usuario mediante elicitación: una llamada destructiva sin confirm: true se rechaza exactamente igual que en un cliente sin elicitación, y con confirm: true se previsualiza. El modo seguro sirve para probar peticiones contra un Arcane de producción y para revisar qué haría un asistente antes de darle permiso de escritura.

ARCANE_MCP_EXCLUDE_TOOLS (o --exclude-tools) retira las acciones que elijas, como una lista separada por comas de cualquiera de estas entradas:

EntradaRetira
image.pruneuna acción, por su ID canónico
arcane_image_pruneuna acción, por su nombre de herramienta individual
swarm_nodeun dominio entero
arcane_swarm_nodeun dominio entero, por su nombre de meta-herramienta
Ventana de terminal
ARCANE_MCP_EXCLUDE_TOOLS=user,role,api_key,settings,volume.delete

Las acciones excluidas desaparecen de todas las superficies y de arcane://tools, igual que en el modo de solo lectura, y una llamada que nombre alguna en la superficie dynamic o meta recibe excluded by the server configuration (ARCANE_MCP_EXCLUDE_TOOLS).

Todo lo anterior reduce lo que ofrece el servidor. El límite exterior es siempre la credencial de Arcane: el servidor actúa como el usuario propietario de la clave de API, y Arcane aplica los roles de ese usuario en cada petición. Un 403 vuelve con una pista que apunta a las asignaciones de roles. Da al asistente una clave de un usuario con el rol mínimo que necesite.

La credencial solo viaja donde Arcane la necesita. Las rutas públicas (inicio de sesión, renovación del token, inicios de sesión con OIDC y passkey, versión, estado, imágenes de la aplicación y avatares) se llaman sin ella, porque Arcane no autentica a nadie en ellas; settings.public la conserva, porque Arcane comprueba quién llama antes de reenviar a un entorno remoto. environment.pair y notification.dispatch envían solo la clave del entorno indicada en su parámetro apiKey, nunca la del servidor.

En modo Streamable HTTP un único servidor puede atender a varias personas, cada una actuando como sí misma:

  • Un cliente puede enviar su propia clave de Arcane en la cabecera X-Arcane-API-Key. El servidor crea entonces un cliente de Arcane aparte para esa clave (uno por clave distinta, reutilizado entre peticiones), y todas las acciones se ejecutan con los permisos de esa clave.
  • Una clave por cliente nunca se mezcla con la sesión del operador: cuando un cliente aporta su propia clave, el ARCANE_USERNAME/ARCANE_PASSWORD del servidor no se usa para ese cliente.
  • Sin la cabecera, el servidor recurre a ARCANE_API_KEY (o al usuario y la contraseña configurados). Si no hay ninguno, la petición se rechaza; así, un despliegue sin credencial global solo atiende a clientes que traen la suya.
  • ARCANE_MCP_HTTP_TOKEN protege el propio endpoint: cada petición a /mcp debe llevar Authorization: Bearer <token>, que se compara en tiempo constante. /healthz queda abierto para las sondas.
  • ARCANE_MCP_HTTP_AUTH=apikey sustituye a ambos: el token Bearer es la propia clave de Arcane de quien llama, comprobada contra Arcane antes de atenderla, sin token compartido ni credencial global del servidor a la que recurrir. En los dos modos, una dirección de cliente que falla la autenticación una y otra vez queda rechazada durante un tiempo, y cada clave puede tener un límite de ritmo. Consulta Autenticación HTTP.

El servidor habla HTTP sin cifrar; termina TLS delante de él antes de que el token bearer y las claves de Arcane crucen una red en la que no confíes.

Cada acción se ejecuta dentro de un plazo global, ARCANE_MCP_ACTION_TIMEOUT (30m por defecto, o ARCANE_MCP_TIMEOUT si es mayor; un valor explícito menor que ARCANE_MCP_TIMEOUT impide arrancar). Las peticiones normales ya están acotadas por ARCANE_MCP_TIMEOUT (60s), que sigue vigente dentro del plazo; el plazo cubre lo que aquel no cubre: los flujos de progreso (descarga y construcción de imágenes, up, pull y redespliegue de proyectos), las descargas y las acciones que hacen varias peticiones. Una llamada que se queda sin tiempo se cancela y recibe un error que nombra el plazo; Arcane puede terminar aun así el trabajo que ya había empezado, así que comprueba su estado antes de reintentar.

Los logs van solo a la salida de error, con el nivel ARCANE_MCP_LOG_LEVEL (info por defecto). La salida estándar transporta el protocolo stdio y nada más.

Cada llamada a una herramienta deja un registro de auditoría de nivel info, en todas las superficies:

level=INFO msg="tool call" surface=meta tool=arcane_volume action=volume.delete outcome=ok confirmed=user duration=412ms
level=INFO msg="tool call" surface=dynamic tool=arcane_execute_action action=container.get outcome=error duration=35ms status=404 key=3f9a1c0b7e21
CampoSignificado
surface, tool, actionPor dónde llegó la llamada y la acción del catálogo a la que corresponde (en una llamada rechazada como read_only o excluded, la acción retenida; ausente si el nombre no corresponde a ninguna acción)
outcomeok, error (la acción falló), refused (no se ejecutó), declined (el usuario dijo que no), previewed (modo seguro), pending (se envió una pregunta de confirmación en el resultado; la llamada repetida deja su propio registro), panic (la acción provocó un pánico; la recuperación del servidor responde a la llamada y registra el pánico)
reasonPor qué se rechazó o declinó una llamada: invalid_args, needs_confirmation, elicitation_failed, unknown_action, read_only y excluded (una acción que retiene el modo de solo lectura o ARCANE_MCP_EXCLUDE_TOOLS), bad_arguments, older_arcane (la instancia ejecuta una versión de Arcane más antigua que la acción), decline, cancel; o por qué falló: action_deadline (ARCANE_MCP_ACTION_TIMEOUT), request_timeout (el ARCANE_MCP_TIMEOUT de una petición), cancelled (el cliente la canceló)
detailCon elicitation_failed, por qué falló la pregunta (el error de elicitación, que puede venir del cliente)
destructiveSolo en una acción cuya confirmación depende de sus argumentos (consulta Cuando depende de los argumentos): si esta llamada contó como destructiva
confirmedCómo se aprobó una acción destructiva que se ejecutó (también si falló o provocó un pánico): flag (confirm: true) o user (elicitación); ausente en las vistas previas
duration, statusTiempo empleado, incluida la espera al usuario; el estado HTTP de Arcane cuando respondió con un error
keyEn modo HTTP, una huella de la clave de API de Arcane con la que se ejecutó la llamada (los 12 primeros dígitos hexadecimales de su SHA-256), nunca la clave

Los valores de los argumentos no se registran nunca. ARCANE_MCP_AUDIT_LOG_ARG_NAMES=on añade los nombres de los argumentos pasados (args=containerId,force); ARCANE_MCP_AUDIT_LOG=off desactiva los registros, igual que un ARCANE_MCP_LOG_LEVEL por encima de info.