From 8f3b7b3b2bd5a6aa9559874470fb5fc6af030af2 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 2 Sep 2026 17:22:03 +0200 Subject: [PATCH] fix(apps): place n8n sandbox in app directory --- apps/docker-compose.yml | 159 ------------------ apps/{ => n8n-sandbox}/config.json | 0 apps/n8n-sandbox/docker-compose.json | 120 +++++++++++++ .../{ => n8n-sandbox/metadata}/description.md | 0 apps/{ => n8n-sandbox/metadata}/logo.jpg | Bin 5 files changed, 120 insertions(+), 159 deletions(-) delete mode 100644 apps/docker-compose.yml rename apps/{ => n8n-sandbox}/config.json (100%) create mode 100644 apps/n8n-sandbox/docker-compose.json rename apps/{ => n8n-sandbox/metadata}/description.md (100%) rename apps/{ => n8n-sandbox/metadata}/logo.jpg (100%) diff --git a/apps/docker-compose.yml b/apps/docker-compose.yml deleted file mode 100644 index 9992c1c..0000000 --- a/apps/docker-compose.yml +++ /dev/null @@ -1,159 +0,0 @@ -# n8n Sandbox Service — app custom Runtipi (format docker-compose.yml + x-runtipi) -# -# Images : n8nio/n8n-sandbox-service-{api,runner-dind,sandbox}:1.3.0 -# (verifier au besoin : docker manifest inspect n8nio/n8n-sandbox-service-sandbox:1.3.0) -# -# Les 3 secrets ne sont PAS dans ce fichier : ce sont des form_fields du -# config.json (SANDBOX_API_KEYS, SANDBOX_REGISTRATION_TOKEN, SANDBOX_RUNNER_KEY), -# saisis/generes dans la GUI Runtipi a l'installation, modifiables ensuite -# dans les parametres de l'app, et injectes ici par ${...}. -# -# Cote app n8n (autre app Runtipi, doc "Set up AI Assistant", setup 2) : -# N8N_ENABLED_MODULES=instance-ai -# N8N_INSTANCE_AI_SANDBOX_ENABLED=true -# N8N_INSTANCE_AI_SANDBOX_PROVIDER=n8n-sandbox -# N8N_INSTANCE_AI_SANDBOX_IMAGE=n8nio/n8n-sandbox-service-sandbox:1.3.0 -# N8N_SANDBOX_SERVICE_URL=http://sandbox-api:8080 -# N8N_SANDBOX_SERVICE_API_KEY= -# Test depuis le conteneur n8n : wget -qO- http://sandbox-api:8080/healthz -# -# Les noms de services sandbox-api et sandbox-runner-1 sont fixes : ce sont -# les SAN des certificats. Si un nom ou NUM_RUNNERS change, supprimer -# ${APP_DATA_DIR}/data/tls (bootstrap-mtls.sh ne regenere rien tant que -# les fichiers existent). -# -# Donnees persistantes (toutes sous ${APP_DATA_DIR}/data, donc couvertes par -# les sauvegardes Runtipi — attention, runner-docker peut peser plusieurs Go) : -# tls/ certificats mTLS (CA + feuilles) -# api/ SQLite de l'API (etat des sandboxes) -# runner-state/ SQLite du runner -# runner-docker/ /var/lib/docker du DinD (cache de l'image sandbox) - -services: - sandbox-certs: - image: n8nio/n8n-sandbox-service-api:1.3.0 - # Obligatoire sous Runtipi : sans cette ligne le builder injecte - # restart: unless-stopped et ce job one-shot boucle en redemarrage. - restart: "no" - user: "0:0" - entrypoint: ["sh", "-c"] - # Genere les certs (idempotent) puis fixe les droits pour l'utilisateur - # sandbox-api de l'image : /tls/api et le repertoire SQLite de l'API - # (en bind mount, Docker cree le dossier en root sinon). - command: - - > - bootstrap-mtls.sh --out-dir /tls --api-san sandbox-api - --control-san-prefix sandbox-runner && - chown -R sandbox-api:sandbox-api /tls/api /var/lib/n8n-sandbox-api - environment: - NUM_RUNNERS: "1" - volumes: - - ${APP_DATA_DIR}/data/tls:/tls - - ${APP_DATA_DIR}/data/api:/var/lib/n8n-sandbox-api - - sandbox-api: - image: n8nio/n8n-sandbox-service-api:1.3.0 - restart: unless-stopped - - depends_on: - sandbox-certs: - condition: service_completed_successfully - - environment: - SANDBOX_API_KEYS: "${SANDBOX_API_KEYS}" - SANDBOX_API_RUNNER_REGISTRATION_TOKEN: "${SANDBOX_REGISTRATION_TOKEN}" - SANDBOX_API_RUNNER_API_KEY: "${SANDBOX_RUNNER_KEY}" - - SANDBOX_API_GRPC_TLS_CERT_FILE: /tls/api/grpc-server.crt - SANDBOX_API_GRPC_TLS_KEY_FILE: /tls/api/grpc-server.key - SANDBOX_API_GRPC_TLS_CLIENT_CA_FILE: /tls/api/ca.crt - - SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_CA_FILE: /tls/api/ca.crt - SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_CERT_FILE: /tls/api/control-grpc-api-client.crt - SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_KEY_FILE: /tls/api/control-grpc-api-client.key - SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_SERVER_NAME: sandbox-runner-1 - - volumes: - - ${APP_DATA_DIR}/data/tls:/tls:ro - # SANDBOX_API_DATA_DIR par defaut (SQLite). - - ${APP_DATA_DIR}/data/api:/var/lib/n8n-sandbox-api - - healthcheck: - # Forme chaine (et non tableau) : c'est la forme documentee par Runtipi - # et la seule acceptee par le formulaire de creation. - test: "wget -qO- http://localhost:8080/healthz" - interval: 5s - timeout: 3s - retries: 5 - start_period: 10s - - # is_main est OBLIGATOIRE : l'UI Runtipi refuse un compose sans - # exactement un service principal ("There must be exactly one main service"). - # Sans internal_port et sans cocher "open port" a l'installation, - # is_main ne publie aucun port et ne pose aucun label Traefik. - # Le service principal rejoint automatiquement tipi_main_network, ce qui - # rend sandbox-api:8080 joignable par les autres apps Runtipi (dont n8n), - # protege uniquement par SANDBOX_API_KEYS. Ne jamais publier 8080/9090. - x-runtipi: - is_main: true - add_to_main_network: true - - sandbox-runner-1: - image: n8nio/n8n-sandbox-service-runner-dind:1.3.0 - restart: unless-stopped - # Mode "dev/macOS" de l'upstream (qui attend sysbox-runc en production ; - # n8n recommande Daytona pour la prod). Equivalent root sur l'hote : - # ce service reste hors de tipi_main_network et ne publie aucun port. - privileged: true - - depends_on: - sandbox-api: - condition: service_healthy - - environment: - SANDBOX_RUNNER_API_KEYS: "${SANDBOX_RUNNER_KEY}" - SANDBOX_RUNNER_REGISTRATION_TOKEN: "${SANDBOX_REGISTRATION_TOKEN}" - - SANDBOX_RUNNER_API_GRPC_ADDR: sandbox-api:9090 - # http:// et non https:// : le mTLS ne couvre que le gRPC - # (enregistrement + SandboxControl). Le trafic proxy exec/files de - # l'API vers le runner reste en HTTP clair authentifie par X-Api-Key - # (docs/configuration.md du depot et compose officiel n8n). - SANDBOX_RUNNER_HTTP_BASE_URL: http://sandbox-runner-1:8080 - - SANDBOX_RUNNER_CONTROL_GRPC_LISTEN_ADDR: ":9091" - SANDBOX_RUNNER_CONTROL_GRPC_ADVERTISE_ADDR: sandbox-runner-1:9091 - - SANDBOX_RUNNER_ID: runner-1 - - SANDBOX_RUNNER_DOCKER_SANDBOX_IMAGE: n8nio/n8n-sandbox-service-sandbox:1.3.0 - - SANDBOX_RUNNER_REGISTRATION_GRPC_CA_FILE: /tls/runner/ca.crt - SANDBOX_RUNNER_REGISTRATION_GRPC_CERT_FILE: /tls/runner/grpc-client.crt - SANDBOX_RUNNER_REGISTRATION_GRPC_KEY_FILE: /tls/runner/grpc-client.key - SANDBOX_RUNNER_REGISTRATION_GRPC_SERVER_NAME: sandbox-api - - SANDBOX_RUNNER_CONTROL_GRPC_TLS_CERT_FILE: /tls/runner/control-grpc-server.crt - SANDBOX_RUNNER_CONTROL_GRPC_TLS_KEY_FILE: /tls/runner/control-grpc-server.key - SANDBOX_RUNNER_CONTROL_GRPC_TLS_CLIENT_CA_FILE: /tls/runner/ca.crt - - volumes: - - ${APP_DATA_DIR}/data/tls:/tls:ro - # Etat SQLite du runner (SANDBOX_RUNNER_DATA_DIR par defaut). - - ${APP_DATA_DIR}/data/runner-state:/var/sandboxes - # Docker interne du DinD : sans ce montage, l'image sandbox est - # re-telechargee a chaque recreation de l'app. Necessite un app-data - # sur ext4/xfs/btrfs (overlay2 ne fonctionne pas sur NFS). - - ${APP_DATA_DIR}/data/runner-docker:/var/lib/docker - - healthcheck: - # /readyz passe au vert une fois le runner enregistre aupres de l'API - # (meme check que le compose du depot upstream). - test: "wget -qO- http://localhost:8080/readyz" - interval: 5s - timeout: 5s - retries: 24 - start_period: 20s - -x-runtipi: - schema_version: 2 diff --git a/apps/config.json b/apps/n8n-sandbox/config.json similarity index 100% rename from apps/config.json rename to apps/n8n-sandbox/config.json diff --git a/apps/n8n-sandbox/docker-compose.json b/apps/n8n-sandbox/docker-compose.json new file mode 100644 index 0000000..d9947c0 --- /dev/null +++ b/apps/n8n-sandbox/docker-compose.json @@ -0,0 +1,120 @@ +{ + "services": [ + { + "name": "sandbox-certs", + "image": "n8nio/n8n-sandbox-service-api:1.3.0", + "command": [ + "bootstrap-mtls.sh --out-dir /tls --api-san sandbox-api --control-san-prefix sandbox-runner && chown -R sandbox-api:sandbox-api /tls/api /var/lib/n8n-sandbox-api\n" + ], + "entrypoint": [ + "sh", + "-c" + ], + "environment": { + "NUM_RUNNERS": "1" + }, + "user": "0:0", + "volumes": [ + { + "hostPath": "${APP_DATA_DIR}/data/tls", + "containerPath": "/tls" + }, + { + "hostPath": "${APP_DATA_DIR}/data/api", + "containerPath": "/var/lib/n8n-sandbox-api" + } + ] + }, + { + "name": "sandbox-api", + "image": "n8nio/n8n-sandbox-service-api:1.3.0", + "isMain": true, + "internalPort": 8080, + "addToMainNetwork": true, + "environment": { + "SANDBOX_API_KEYS": "${SANDBOX_API_KEYS}", + "SANDBOX_API_RUNNER_REGISTRATION_TOKEN": "${SANDBOX_REGISTRATION_TOKEN}", + "SANDBOX_API_RUNNER_API_KEY": "${SANDBOX_RUNNER_KEY}", + "SANDBOX_API_GRPC_TLS_CERT_FILE": "/tls/api/grpc-server.crt", + "SANDBOX_API_GRPC_TLS_KEY_FILE": "/tls/api/grpc-server.key", + "SANDBOX_API_GRPC_TLS_CLIENT_CA_FILE": "/tls/api/ca.crt", + "SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_CA_FILE": "/tls/api/ca.crt", + "SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_CERT_FILE": "/tls/api/control-grpc-api-client.crt", + "SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_KEY_FILE": "/tls/api/control-grpc-api-client.key", + "SANDBOX_API_RUNNER_CONTROL_GRPC_TLS_SERVER_NAME": "sandbox-runner-1" + }, + "volumes": [ + { + "hostPath": "${APP_DATA_DIR}/data/tls", + "containerPath": "/tls", + "readOnly": true + }, + { + "hostPath": "${APP_DATA_DIR}/data/api", + "containerPath": "/var/lib/n8n-sandbox-api" + } + ], + "healthCheck": { + "test": "wget -qO- http://localhost:8080/healthz", + "interval": "5s", + "timeout": "3s", + "retries": 5, + "startPeriod": "10s" + }, + "dependsOn": { + "sandbox-certs": { + "condition": "service_completed_successfully" + } + } + }, + { + "name": "sandbox-runner-1", + "image": "n8nio/n8n-sandbox-service-runner-dind:1.3.0", + "environment": { + "SANDBOX_RUNNER_API_KEYS": "${SANDBOX_RUNNER_KEY}", + "SANDBOX_RUNNER_REGISTRATION_TOKEN": "${SANDBOX_REGISTRATION_TOKEN}", + "SANDBOX_RUNNER_API_GRPC_ADDR": "sandbox-api:9090", + "SANDBOX_RUNNER_HTTP_BASE_URL": "http://sandbox-runner-1:8080", + "SANDBOX_RUNNER_CONTROL_GRPC_LISTEN_ADDR": ":9091", + "SANDBOX_RUNNER_CONTROL_GRPC_ADVERTISE_ADDR": "sandbox-runner-1:9091", + "SANDBOX_RUNNER_ID": "runner-1", + "SANDBOX_RUNNER_DOCKER_SANDBOX_IMAGE": "n8nio/n8n-sandbox-service-sandbox:1.3.0", + "SANDBOX_RUNNER_REGISTRATION_GRPC_CA_FILE": "/tls/runner/ca.crt", + "SANDBOX_RUNNER_REGISTRATION_GRPC_CERT_FILE": "/tls/runner/grpc-client.crt", + "SANDBOX_RUNNER_REGISTRATION_GRPC_KEY_FILE": "/tls/runner/grpc-client.key", + "SANDBOX_RUNNER_REGISTRATION_GRPC_SERVER_NAME": "sandbox-api", + "SANDBOX_RUNNER_CONTROL_GRPC_TLS_CERT_FILE": "/tls/runner/control-grpc-server.crt", + "SANDBOX_RUNNER_CONTROL_GRPC_TLS_KEY_FILE": "/tls/runner/control-grpc-server.key", + "SANDBOX_RUNNER_CONTROL_GRPC_TLS_CLIENT_CA_FILE": "/tls/runner/ca.crt" + }, + "privileged": true, + "volumes": [ + { + "hostPath": "${APP_DATA_DIR}/data/tls", + "containerPath": "/tls", + "readOnly": true + }, + { + "hostPath": "${APP_DATA_DIR}/data/runner-state", + "containerPath": "/var/sandboxes" + }, + { + "hostPath": "${APP_DATA_DIR}/data/runner-docker", + "containerPath": "/var/lib/docker" + } + ], + "healthCheck": { + "test": "wget -qO- http://localhost:8080/readyz", + "interval": "5s", + "timeout": "5s", + "retries": 24, + "startPeriod": "20s" + }, + "dependsOn": { + "sandbox-api": { + "condition": "service_healthy" + } + } + } + ] +} diff --git a/apps/description.md b/apps/n8n-sandbox/metadata/description.md similarity index 100% rename from apps/description.md rename to apps/n8n-sandbox/metadata/description.md diff --git a/apps/logo.jpg b/apps/n8n-sandbox/metadata/logo.jpg similarity index 100% rename from apps/logo.jpg rename to apps/n8n-sandbox/metadata/logo.jpg