mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-01 10:22:08 +00:00
Merge branch 'develop' of https://github.com/MacRimi/ProxMenux into develop
This commit is contained in:
@@ -91,7 +91,7 @@ class AnthropicProvider(AIProvider):
|
||||
if result.get('stop_reason') == 'refusal':
|
||||
category = (result.get('stop_details') or {}).get('category')
|
||||
raise AIProviderError(f"Request declined by Anthropic (category: {category})")
|
||||
|
||||
|
||||
# Content is a list of blocks; thinking blocks may come before the
|
||||
# text, so take the first text block rather than content[0].
|
||||
content = result.get('content')
|
||||
|
||||
@@ -216,6 +216,7 @@
|
||||
"API returned an invalid timezone": "Die API hat eine ungültige Zeitzone zurückgegeben",
|
||||
"API tokens (Home Assistant, scripts) will keep working.": "API-Tokens (Home Assistant, Skripte) funktionieren weiterhin.",
|
||||
"APP_KEY: 32-byte Base64 key (generate with openssl rand -base64 32)": "APP_KEY: 32-Byte-Base64-Schlüssel (mit openssl rand -base64 32 erzeugen)",
|
||||
"APP_KEY: use the upstream command openssl rand -base64 32 and prefix its output with base64:": "APP_KEY: Führe den vom Projekt angegebenen Befehl openssl rand -base64 32 aus und stelle dem Ergebnis base64: voran.",
|
||||
"APT IPv4 configuration completed": "APT IPv4-Konfiguration abgeschlossen",
|
||||
"APT IPv4 configuration removed": "APT IPv4-Konfiguration entfernt",
|
||||
"APT and repositories": "APT und Repositorys",
|
||||
@@ -1465,6 +1466,7 @@
|
||||
"Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {ui} before retrying; no automatic rollback was attempted.": "Der Repository-Wechsel konnte nicht abgeschlossen oder überprüft werden; die Änderungen sind möglicherweise unvollständig und der Zustand der Repositorys ist unbekannt. Prüfen Sie {ui}, bevor Sie es erneut versuchen; es wurde kein automatisches Zurücksetzen versucht.",
|
||||
"Could not complete private network release:": "Die Veröffentlichung des privaten Netzwerks konnte nicht abgeschlossen werden:",
|
||||
"Could not configure IOMMU kernel parameters automatically. Configure manually and reboot.": "IOMMU-Kernelparameter konnten nicht automatisch konfiguriert werden. Manuell konfigurieren und neu starten.",
|
||||
"Could not configure the tmpfs mounts": "Die tmpfs-Mounts konnten nicht konfiguriert werden",
|
||||
"Could not convert the OCI rootfs to privileged": "Konnte die OCI-Rootfs nicht in privilegierte umwandeln",
|
||||
"Could not copy the PVE keyfile into place. Check permissions on:": "Die PVE-Schlüsseldatei konnte nicht kopiert werden. Überprüfen Sie die Berechtigungen für:",
|
||||
"Could not copy the keyfile into place.": "Die Schlüsseldatei konnte nicht kopiert werden.",
|
||||
@@ -3310,6 +3312,7 @@
|
||||
"Initiator IQN is authorised on the target": "Der Initiator-IQN ist auf dem Ziel autorisiert",
|
||||
"Initiator IQN:": "Initiator-IQN:",
|
||||
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape ist professionelle Qualitätsvektor-Grafiksoftware, die auf Linux, Mac OS X und Windows Desktop-Computern läuft.",
|
||||
"Input": "Input",
|
||||
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Erstellen Sie im LXC den Administrator: Konsole kimai:user:creat YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
|
||||
"Inspect disks before any action": "Überprüfen Sie die Datenträger vor jeder Aktion",
|
||||
"Inspect host device nodes": "Untersuchen Sie die Host-Geräteknoten",
|
||||
@@ -6184,6 +6187,7 @@
|
||||
"Self-hosted file sharing with a modern web interface": "Self-Hosted File Sharing mit einem modernen Web-Interface",
|
||||
"Self-hosted file sync and sharing.": "Selbst gehostete Dateisynchronisierung und -freigabe.",
|
||||
"Self-hosted file toolkit for images, video, audio, PDFs, and files": "Selbst gehostetes Datei-Toolkit für Bilder, Video, Audio, PDFs und Dateien",
|
||||
"Self-hosted forms and workflows.": "Selbst gehostete Formulare und Arbeitsabläufe.",
|
||||
"Self-hosted household plant-care tracker with reminders, photos, and multiple user accounts.": "Selbst gehostete Pflegeverwaltung für Zimmerpflanzen mit Erinnerungen, Fotos und mehreren Benutzerkonten.",
|
||||
"Self-hosted image-hosting service with authenticated uploads, image processing, public links, and local storage.": "Selbst gehosteter Bildhosting-Dienst mit authentifizierten Uploads, Bildverarbeitung, öffentlichen Links und lokalem Speicher.",
|
||||
"Self-hosted internet archiving solution": "Selbst gehostete Internet-Archivierungslösung",
|
||||
@@ -6789,6 +6793,9 @@
|
||||
"The OCI contract changed while reviewing; nothing was adopted": "Der OCI-Vertrag wurde während der Überprüfung geändert.nichts wurde übernommen",
|
||||
"The OCI engine is not installed. Update ProxMenux and try again.": "Der OCI-Motor ist nicht installiert. Aktualisieren Sie ProxMenux und versuchen Sie es erneut.",
|
||||
"The OCI image storage was not kept": "Der OCI-Bildspeicher wurde nicht beibehalten",
|
||||
"The OCI image user or group is ambiguous": "Der OCI-Image-Benutzer oder die Gruppe ist nicht eindeutig",
|
||||
"The OCI image user or group is not numeric": "Der OCI-Image-Benutzer oder die Gruppe ist nicht numerisch",
|
||||
"The OCI image user or group is outside the supported range": "Der OCI-Image-Benutzer oder die Gruppe liegt außerhalb des unterstützten Bereichs",
|
||||
"The OCR language must use Tesseract codes, for example eng or eng+spa": "Die OCR-Sprache muss Tesseract-Codes verwenden, z. B. eng oder eng+spa",
|
||||
"The PATH of the new image is outside the reproducible profile": "Der PATH des neuen Bildes liegt außerhalb des reproduzierbaren Profils",
|
||||
"The Paperless persistent volumes need at least 8 GB": "Die papierlosen persistenten Volumes benötigen mindestens 8 GB",
|
||||
@@ -6850,6 +6857,8 @@
|
||||
"The application has been recreated with the new options.": "Die Anwendung wurde mit den neuen Optionen neu erstellt.",
|
||||
"The application needs it for": "Die Anwendung benötigt es für",
|
||||
"The application stopped during its first start:": "Die Anwendung wurde während ihres ersten Starts gestoppt:",
|
||||
"The application user or group is not numeric": "Der Anwendungsbenutzer oder die Gruppe ist nicht numerisch",
|
||||
"The application user or group is outside the supported range": "Der Anwendungsbenutzer oder die Gruppe liegt außerhalb des unterstützten Bereichs",
|
||||
"The application was not modified.": "Die Anwendung wurde nicht geändert.",
|
||||
"The application was removed": "Die Anwendung wurde entfernt",
|
||||
"The archive could not be extracted.": "Das Archiv konnte nicht extrahiert werden.",
|
||||
@@ -7301,6 +7310,7 @@
|
||||
"The sysctl include is unknown or differs from the saved record": "Das sysctl include ist unbekannt oder unterscheidet sich vom gespeicherten Datensatz",
|
||||
"The temporary password could not be retrieved.": "Das temporäre Passwort konnte nicht abgerufen werden.",
|
||||
"The test will continue even if you close this terminal.": "Der Test wird auch dann fortgesetzt, wenn Sie dieses Terminal schließen.",
|
||||
"The tmpfs mode is specified more than once": "Der tmpfs-Modus wurde mehrfach angegeben",
|
||||
"The tmpfs mounts of the container differ from the saved record": "Die tmpfs Halterungen des Containers unterscheiden sich vom gespeicherten Datensatz",
|
||||
"The tmpfs path or size is outside the supported profile": "Der Pfad oder die Größe von tmpfs befindet sich außerhalb des unterstützten Profils",
|
||||
"The translated recipe changed during the preparation": "Das übersetzte Rezept änderte sich während der Vorbereitung",
|
||||
@@ -7713,7 +7723,9 @@
|
||||
"Unsupported prlimit resource": "Nicht unterstützte Prilimit Ressource",
|
||||
"Unsupported secret generator:": "Nicht unterstützter geheimer Generator:",
|
||||
"Unsupported storage mode:": "Nicht unterstützter Speichermodus:",
|
||||
"Unsupported tmpfs mode": "Nicht unterstützter tmpfs-Modus",
|
||||
"Unsupported tmpfs options": "Nicht unterstützte tmpfs Optionen",
|
||||
"Unsupported tmpfs owner strategy": "Nicht unterstützte tmpfs-Eigentümerstrategie",
|
||||
"Unsupported volume options:": "Nicht unterstützte Volumenoptionen:",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host and changes nothing otherwise.": "Bis zur erneuten Registrierung können sie hier nicht aktualisiert oder verwaltet werden. Die Wiederherstellung prüft zunächst, ob sich alles, was sie benötigen, auf diesem Host befindet und ändert ansonsten nichts.",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host; an application that lacks something is not registered.": "Bis zur erneuten Registrierung können sie hier nicht aktualisiert oder verwaltet werden. Die Wiederherstellung prüft zunächst, ob sich alles, was sie benötigen, auf diesem Host befindet. Eine Anwendung, der etwas fehlt, wird nicht registriert.",
|
||||
|
||||
@@ -215,6 +215,7 @@
|
||||
"API returned an invalid timezone": "API devolvió una zona horaria no válida",
|
||||
"API tokens (Home Assistant, scripts) will keep working.": "Los tokens de API (Home Assistant, scripts) seguirán funcionando.",
|
||||
"APP_KEY: 32-byte Base64 key (generate with openssl rand -base64 32)": "APP_KEY: clave Base64 de 32 bytes (generar con openssl rand -base64 32)",
|
||||
"APP_KEY: use the upstream command openssl rand -base64 32 and prefix its output with base64:": "APP_KEY: ejecuta el comando indicado por el proyecto, openssl rand -base64 32, y antepone base64: al resultado.",
|
||||
"APT IPv4 configuration completed": "Configuración APT IPv4 completada",
|
||||
"APT IPv4 configuration removed": "Se eliminó la configuración de APT IPv4",
|
||||
"APT and repositories": "APT y repositorios",
|
||||
@@ -1463,6 +1464,7 @@
|
||||
"Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {ui} before retrying; no automatic rollback was attempted.": "No se pudo completar ni verificar el cambio de repositorio; los cambios pueden ser parciales y el estado del repositorio es desconocido. Revisa {ui} antes de volver a intentarlo; no se ha intentado ninguna reversión automática.",
|
||||
"Could not complete private network release:": "No se pudo completar la liberación de la red privada:",
|
||||
"Could not configure IOMMU kernel parameters automatically. Configure manually and reboot.": "No se pudieron configurar los parámetros del kernel IOMMU automáticamente. Configura manualmente y reinicia.",
|
||||
"Could not configure the tmpfs mounts": "No se pudieron configurar los montajes tmpfs",
|
||||
"Could not convert the OCI rootfs to privileged": "No podía convertir los rootfs OCI a privilegiados",
|
||||
"Could not copy the PVE keyfile into place. Check permissions on:": "No se pudo copiar el archivo de claves PVE en su lugar. Comprueba los permisos en:",
|
||||
"Could not copy the keyfile into place.": "no se pudo copiar el archivo de claves en su lugar.",
|
||||
@@ -3307,6 +3309,7 @@
|
||||
"Initiator IQN is authorised on the target": "El IQN del iniciador está autorizado en el destino",
|
||||
"Initiator IQN:": "IQN del iniciador:",
|
||||
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape es un software de gráficos vectoriales de calidad profesional para ordenadores de escritorio con Linux, Mac OS X y Windows.",
|
||||
"Input": "Input",
|
||||
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Dentro del LXC, crea el administrador: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
|
||||
"Inspect disks before any action": "Inspeccionar los discos antes de cualquier acción",
|
||||
"Inspect host device nodes": "Inspeccionar los nodos del dispositivo host",
|
||||
@@ -6168,6 +6171,7 @@
|
||||
"Self-hosted file sharing with a modern web interface": "Compartición de archivos autoalojada con una interfaz web moderna",
|
||||
"Self-hosted file sync and sharing.": "Sincronización y uso compartido de archivos autoalojados.",
|
||||
"Self-hosted file toolkit for images, video, audio, PDFs, and files": "Kit de herramientas autoalojado para imágenes, vídeo, audio, PDF y archivos",
|
||||
"Self-hosted forms and workflows.": "Formularios y flujos de trabajo autoalojados.",
|
||||
"Self-hosted household plant-care tracker with reminders, photos, and multiple user accounts.": "Gestor autoalojado del cuidado de plantas domésticas, con recordatorios, fotos y varias cuentas de usuario.",
|
||||
"Self-hosted image-hosting service with authenticated uploads, image processing, public links, and local storage.": "Servicio autoalojado para alojar imágenes, con subidas autenticadas, procesamiento de imágenes, enlaces públicos y almacenamiento local.",
|
||||
"Self-hosted internet archiving solution": "Solución autoalojada de archivado de Internet",
|
||||
@@ -6774,6 +6778,9 @@
|
||||
"The OCI contract changed while reviewing; nothing was adopted": "El contrato OCI cambió durante la revisión; no se ha incorporado nada",
|
||||
"The OCI engine is not installed. Update ProxMenux and try again.": "El motor OCI no está instalado. Actualiza ProxMenux e inténtalo de nuevo.",
|
||||
"The OCI image storage was not kept": "El almacenamiento de imágenes OCI no se mantuvo",
|
||||
"The OCI image user or group is ambiguous": "El usuario o grupo de la imagen OCI no es inequívoco",
|
||||
"The OCI image user or group is not numeric": "El usuario o grupo de la imagen OCI no es numérico",
|
||||
"The OCI image user or group is outside the supported range": "El usuario o grupo de la imagen OCI está fuera del rango admitido",
|
||||
"The OCR language must use Tesseract codes, for example eng or eng+spa": "El lenguaje OCR debe utilizar códigos de Tesseract, por ejemplo eng o eng+spa",
|
||||
"The PATH of the new image is outside the reproducible profile": "El PATH de la nueva imagen está fuera del perfil reproducible",
|
||||
"The Paperless persistent volumes need at least 8 GB": "Los volúmenes persistentes de Paperless necesitan al menos 8 GB",
|
||||
@@ -6833,6 +6840,8 @@
|
||||
"The application has been modified with the new options.": "La aplicación se ha modificado con las nuevas opciones.",
|
||||
"The application needs it for": "La aplicación lo necesita para",
|
||||
"The application stopped during its first start:": "La aplicación se detuvo durante su primer arranque:",
|
||||
"The application user or group is not numeric": "El usuario o grupo de la aplicación no es numérico",
|
||||
"The application user or group is outside the supported range": "El usuario o grupo de la aplicación está fuera del rango admitido",
|
||||
"The application was not modified.": "La aplicación no se ha modificado.",
|
||||
"The application was removed": "La aplicación se ha eliminado",
|
||||
"The archive could not be extracted.": "No se pudo extraer el archivo.",
|
||||
@@ -7277,6 +7286,7 @@
|
||||
"The sysctl include is unknown or differs from the saved record": "El include de sysctl es desconocido o difiere del registro guardado",
|
||||
"The temporary password could not be retrieved.": "La contraseña temporal no se puede recuperar.",
|
||||
"The test will continue even if you close this terminal.": "La prueba continuará incluso si cierra esta terminal.",
|
||||
"The tmpfs mode is specified more than once": "El modo de tmpfs está especificado más de una vez",
|
||||
"The tmpfs mounts of the container differ from the saved record": "Los montajes tmpfs del contenedor difieren del registro guardado",
|
||||
"The tmpfs path or size is outside the supported profile": "El camino o tamaño de tmpfs está fuera del perfil soportado",
|
||||
"The translated recipe changed during the preparation": "La receta traducida cambió durante la preparación",
|
||||
@@ -7688,7 +7698,9 @@
|
||||
"Unsupported prlimit resource": "Recursos imprevistos sin apoyo",
|
||||
"Unsupported secret generator:": "Generador de secretos no admitido:",
|
||||
"Unsupported storage mode:": "Modo de almacenamiento sin soporte:",
|
||||
"Unsupported tmpfs mode": "Modo de tmpfs no admitido",
|
||||
"Unsupported tmpfs options": "Opciones de tmpfs sin soporte",
|
||||
"Unsupported tmpfs owner strategy": "Estrategia de propietario de tmpfs no admitida",
|
||||
"Unsupported volume options:": "Opciones de volumen no admitidas:",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first what prevents their registration; an application with such a problem is not registered. A Rclone mount that has to be enabled again does not prevent it, but then the applications are not started automatically.": "Hasta que se registren de nuevo no se pueden actualizar ni gestionar desde aquí. La recuperación comprueba antes qué impide registrarlas; una aplicación con un problema así no se registra. Un montaje de Rclone que haya que activar de nuevo no lo impide, pero entonces las aplicaciones no se inician automáticamente.",
|
||||
"Untrusted or modified NVIDIA hook": "Hook NVIDIA sin confianza o modificado",
|
||||
|
||||
@@ -216,6 +216,7 @@
|
||||
"API returned an invalid timezone": "L'API a renvoyé un fuseau horaire non valide",
|
||||
"API tokens (Home Assistant, scripts) will keep working.": "Les jetons API (Home Assistant, scripts) continueront de fonctionner.",
|
||||
"APP_KEY: 32-byte Base64 key (generate with openssl rand -base64 32)": "APP_KEY : clé Base64 de 32 octets (générer avec openssl rand -base64 32)",
|
||||
"APP_KEY: use the upstream command openssl rand -base64 32 and prefix its output with base64:": "APP_KEY: exécutez la commande indiquée par le projet, openssl rand -base64 32, puis ajoutez base64: au début du résultat.",
|
||||
"APT IPv4 configuration completed": "Configuration APT IPv4 terminée",
|
||||
"APT IPv4 configuration removed": "Configuration APT IPv4 supprimée",
|
||||
"APT and repositories": "APT et référentiels",
|
||||
@@ -1465,6 +1466,7 @@
|
||||
"Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {ui} before retrying; no automatic rollback was attempted.": "Impossible de terminer ou de vérifier le changement de référentiel ; les modifications peuvent être partielles et l'état des référentiels est inconnu. Inspectez {ui} avant de réessayer ; aucun retour arrière automatique n'a été tenté.",
|
||||
"Could not complete private network release:": "Impossible de terminer la version du réseau privé :",
|
||||
"Could not configure IOMMU kernel parameters automatically. Configure manually and reboot.": "Impossible de configurer automatiquement les paramètres du noyau IOMMU. Configurez manuellement et redémarrez.",
|
||||
"Could not configure the tmpfs mounts": "Impossible de configurer les montages tmpfs",
|
||||
"Could not convert the OCI rootfs to privileged": "Impossible de convertir les roofs de l'OCI en privilégiés",
|
||||
"Could not copy the PVE keyfile into place. Check permissions on:": "Impossible de copier le fichier de clés PVE.Vérifiez les autorisations sur :",
|
||||
"Could not copy the keyfile into place.": "Impossible de copier le fichier de clés.",
|
||||
@@ -3310,6 +3312,7 @@
|
||||
"Initiator IQN is authorised on the target": "L'IQN initiateur est autorisé sur la cible",
|
||||
"Initiator IQN:": "IQN initiateur :",
|
||||
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape est un logiciel graphique vectoriel de qualité professionnelle qui fonctionne sur les ordinateurs de bureau Linux, Mac OS X et Windows.",
|
||||
"Input": "Input",
|
||||
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Dans le LXC, créez l’administrateur : console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
|
||||
"Inspect disks before any action": "Inspecter les disques avant toute action",
|
||||
"Inspect host device nodes": "Inspecter les nœuds du périphérique hôte",
|
||||
@@ -6184,6 +6187,7 @@
|
||||
"Self-hosted file sharing with a modern web interface": "Partage de fichiers auto-organisé avec une interface web moderne",
|
||||
"Self-hosted file sync and sharing.": "Service auto-hébergé de synchronisation et de partage de fichiers.",
|
||||
"Self-hosted file toolkit for images, video, audio, PDFs, and files": "Boîte à outils de fichiers auto-organisé pour les images, vidéo, audio, PDF et fichiers",
|
||||
"Self-hosted forms and workflows.": "Formulaires et workflows auto-hébergés.",
|
||||
"Self-hosted household plant-care tracker with reminders, photos, and multiple user accounts.": "Suivi auto-hébergé de l'entretien des plantes à la maison, avec rappels, photos et plusieurs comptes utilisateur.",
|
||||
"Self-hosted image-hosting service with authenticated uploads, image processing, public links, and local storage.": "Service auto-hébergé d'hébergement d'images avec téléversements authentifiés, traitement des images, liens publics et stockage local.",
|
||||
"Self-hosted internet archiving solution": "Solution d'archivage internet auto-organisé",
|
||||
@@ -6788,6 +6792,9 @@
|
||||
"The OCI contract changed while reviewing; nothing was adopted": "Le contrat OCI a été modifié lors de la révision ;rien n'a été adopté",
|
||||
"The OCI engine is not installed. Update ProxMenux and try again.": "Le moteur OCI n'est pas installé. Mettre à jour ProxMenux et réessayer.",
|
||||
"The OCI image storage was not kept": "Le stockage d'image OCI n'a pas été conservé",
|
||||
"The OCI image user or group is ambiguous": "La valeur de l’utilisateur ou du groupe de l’image OCI est ambiguë",
|
||||
"The OCI image user or group is not numeric": "La valeur de l’utilisateur ou du groupe de l’image OCI n’est pas numérique",
|
||||
"The OCI image user or group is outside the supported range": "La valeur de l’utilisateur ou du groupe de l’image OCI est en dehors de la plage prise en charge",
|
||||
"The OCR language must use Tesseract codes, for example eng or eng+spa": "Le langage OCR doit utiliser des codes Tesseract, par exemple eng ou eng+spa",
|
||||
"The PATH of the new image is outside the reproducible profile": "Le PATH de la nouvelle image est en dehors du profil reproductible",
|
||||
"The Paperless persistent volumes need at least 8 GB": "Les volumes persistants sans papier nécessitent au moins 8 Go",
|
||||
@@ -6849,6 +6856,8 @@
|
||||
"The application has been recreated with the new options.": "L'application a été recréée avec les nouvelles options.",
|
||||
"The application needs it for": "L'application en a besoin pour",
|
||||
"The application stopped during its first start:": "L'application s'est arrêtée lors de son premier démarrage :",
|
||||
"The application user or group is not numeric": "La valeur de l’utilisateur ou du groupe de l’application n’est pas numérique",
|
||||
"The application user or group is outside the supported range": "La valeur de l’utilisateur ou du groupe de l’application est en dehors de la plage prise en charge",
|
||||
"The application was not modified.": "L'application n'a pas été modifiée.",
|
||||
"The application was removed": "La demande a été retirée",
|
||||
"The archive could not be extracted.": "L'archive n'a pas pu être extraite.",
|
||||
@@ -7300,6 +7309,7 @@
|
||||
"The sysctl include is unknown or differs from the saved record": "Le système inclut est inconnu ou diffère de l'enregistrement enregistré",
|
||||
"The temporary password could not be retrieved.": "Le mot de passe temporaire n'a pas pu être récupéré.",
|
||||
"The test will continue even if you close this terminal.": "Le test continuera même si vous fermez ce terminal.",
|
||||
"The tmpfs mode is specified more than once": "Le mode tmpfs est indiqué plusieurs fois",
|
||||
"The tmpfs mounts of the container differ from the saved record": "Les supports tmpfs du conteneur diffèrent de l'enregistrement enregistré",
|
||||
"The tmpfs path or size is outside the supported profile": "Le chemin ou la taille de tmpfs est en dehors du profil pris en charge",
|
||||
"The translated recipe changed during the preparation": "La recette traduite a changé pendant la préparation",
|
||||
@@ -7712,7 +7722,9 @@
|
||||
"Unsupported prlimit resource": "Ressources prlimit non prises en charge",
|
||||
"Unsupported secret generator:": "Générateur secret non supporté:",
|
||||
"Unsupported storage mode:": "Mode de stockage non supporté:",
|
||||
"Unsupported tmpfs mode": "Mode tmpfs non pris en charge",
|
||||
"Unsupported tmpfs options": "Options tmpfs non prises en charge",
|
||||
"Unsupported tmpfs owner strategy": "Stratégie de propriétaire tmpfs non prise en charge",
|
||||
"Unsupported volume options:": "Options de volume non pris en charge:",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host and changes nothing otherwise.": "Jusqu'à ce qu'ils soient à nouveau enregistrés, ils ne peuvent pas être mis à jour ou gérés à partir d'ici. La récupération vérifie d'abord que tout ce dont ils ont besoin se trouve sur cet hôte et ne change rien autrement.",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host; an application that lacks something is not registered.": "Jusqu'à ce qu'ils soient à nouveau enregistrés, ils ne peuvent pas être mis à jour ou gérés à partir d'ici. La récupération vérifie d'abord que tout ce dont ils ont besoin se trouve sur cet hôte ; une application qui manque quelque chose n'est pas enregistrée.",
|
||||
|
||||
@@ -216,6 +216,7 @@
|
||||
"API returned an invalid timezone": "L'API ha restituito un fuso orario non valido",
|
||||
"API tokens (Home Assistant, scripts) will keep working.": "I token API (Home Assistant, script) continueranno a funzionare.",
|
||||
"APP_KEY: 32-byte Base64 key (generate with openssl rand -base64 32)": "APP_KEY: chiave Base64 da 32 byte (genera con openssl rand -base64 32)",
|
||||
"APP_KEY: use the upstream command openssl rand -base64 32 and prefix its output with base64:": "APP_KEY: esegui il comando indicato dal progetto, openssl rand -base64 32, e anteponi base64: al risultato.",
|
||||
"APT IPv4 configuration completed": "Configurazione APT IPv4 completata",
|
||||
"APT IPv4 configuration removed": "Configurazione APT IPv4 rimossa",
|
||||
"APT and repositories": "APT e repository",
|
||||
@@ -1465,6 +1466,7 @@
|
||||
"Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {ui} before retrying; no automatic rollback was attempted.": "Impossibile completare o verificare il cambio di repository; le modifiche potrebbero essere parziali e lo stato dei repository è sconosciuto. Controlla {ui} prima di riprovare; non è stato tentato alcun rollback automatico.",
|
||||
"Could not complete private network release:": "Impossibile completare la rimozione della rete privata:",
|
||||
"Could not configure IOMMU kernel parameters automatically. Configure manually and reboot.": "Impossibile configurare automaticamente i parametri IOMMU del kernel. Configura manualmente e riavvia.",
|
||||
"Could not configure the tmpfs mounts": "Impossibile configurare i mount tmpfs",
|
||||
"Could not convert the OCI rootfs to privileged": "Impossibile convertire il rootfs OCI alla modalità privilegiata",
|
||||
"Could not copy the PVE keyfile into place. Check permissions on:": "Impossibile copiare il file di chiavi PVE nella sua posizione. Controlla i permessi su:",
|
||||
"Could not copy the keyfile into place.": "Impossibile copiare il file di chiavi nella posizione prevista.",
|
||||
@@ -3310,6 +3312,7 @@
|
||||
"Initiator IQN is authorised on the target": "L'IQN dell'iniziatore è autorizzato sul target",
|
||||
"Initiator IQN:": "IQN dell'iniziatore:",
|
||||
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape è un software di grafica vettoriale di qualità professionale per computer desktop Linux, Mac OS X e Windows.",
|
||||
"Input": "Input",
|
||||
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Nel LXC, crea l'amministratore: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
|
||||
"Inspect disks before any action": "Ispeziona i dischi prima di qualsiasi azione",
|
||||
"Inspect host device nodes": "Ispeziona i nodi dei dispositivi host",
|
||||
@@ -6184,6 +6187,7 @@
|
||||
"Self-hosted file sharing with a modern web interface": "Condivisione file self-hosted con interfaccia web moderna",
|
||||
"Self-hosted file sync and sharing.": "Servizio self-hosted per sincronizzare e condividere file.",
|
||||
"Self-hosted file toolkit for images, video, audio, PDFs, and files": "Set di strumenti self-hosted per immagini, video, audio, PDF e altri file",
|
||||
"Self-hosted forms and workflows.": "Moduli e flussi di lavoro auto-ospitati.",
|
||||
"Self-hosted household plant-care tracker with reminders, photos, and multiple user accounts.": "Strumento per la cura delle piante domestiche da ospitare sul proprio server, con promemoria, foto e più account utente.",
|
||||
"Self-hosted image-hosting service with authenticated uploads, image processing, public links, and local storage.": "Servizio per ospitare immagini sul proprio server, con caricamenti autenticati, elaborazione delle immagini, link pubblici e archiviazione locale.",
|
||||
"Self-hosted internet archiving solution": "Soluzione self-hosted per archiviare contenuti Internet",
|
||||
@@ -6789,6 +6793,9 @@
|
||||
"The OCI contract changed while reviewing; nothing was adopted": "Il contratto OCI è cambiato durante la revisione; non è stata acquisita alcuna modifica",
|
||||
"The OCI engine is not installed. Update ProxMenux and try again.": "Il motore OCI non è installato. Aggiorna ProxMenux e riprova.",
|
||||
"The OCI image storage was not kept": "Lo storage delle immagini OCI non è stato conservato",
|
||||
"The OCI image user or group is ambiguous": "L’utente o il gruppo dell’immagine OCI non è univoco",
|
||||
"The OCI image user or group is not numeric": "L’utente o il gruppo dell’immagine OCI non è numerico",
|
||||
"The OCI image user or group is outside the supported range": "L’utente o il gruppo dell’immagine OCI è fuori dall’intervallo supportato",
|
||||
"The OCR language must use Tesseract codes, for example eng or eng+spa": "La lingua OCR deve usare i codici Tesseract, ad esempio eng o eng+spa",
|
||||
"The PATH of the new image is outside the reproducible profile": "Il PATH della nuova immagine non rientra nel profilo riproducibile",
|
||||
"The Paperless persistent volumes need at least 8 GB": "I volumi persistenti di Paperless richiedono almeno 8 GB",
|
||||
@@ -6850,6 +6857,8 @@
|
||||
"The application has been recreated with the new options.": "L’applicazione è stata ricreata con le nuove opzioni.",
|
||||
"The application needs it for": "L’applicazione ne ha bisogno per",
|
||||
"The application stopped during its first start:": "L’applicazione si è arrestata durante il primo avvio:",
|
||||
"The application user or group is not numeric": "L’utente o il gruppo dell’applicazione non è numerico",
|
||||
"The application user or group is outside the supported range": "L’utente o il gruppo dell’applicazione è fuori dall’intervallo supportato",
|
||||
"The application was not modified.": "L'applicazione non è stata modificata.",
|
||||
"The application was removed": "L'applicazione è stata rimossa",
|
||||
"The archive could not be extracted.": "Impossibile estrarre l'archivio.",
|
||||
@@ -7301,6 +7310,7 @@
|
||||
"The sysctl include is unknown or differs from the saved record": "La direttiva sysctl include è sconosciuta o diversa dal record salvato",
|
||||
"The temporary password could not be retrieved.": "Impossibile recuperare la password temporanea.",
|
||||
"The test will continue even if you close this terminal.": "Il test continuerà anche chiudendo questo terminale.",
|
||||
"The tmpfs mode is specified more than once": "La modalità tmpfs è specificata più di una volta",
|
||||
"The tmpfs mounts of the container differ from the saved record": "I mount tmpfs del container differiscono dal record salvato",
|
||||
"The tmpfs path or size is outside the supported profile": "Il percorso o la dimensione di tmpfs non rientra nel profilo supportato",
|
||||
"The translated recipe changed during the preparation": "La ricetta tradotta è cambiata durante la preparazione",
|
||||
@@ -7713,7 +7723,9 @@
|
||||
"Unsupported prlimit resource": "Risorsa prlimit non supportata",
|
||||
"Unsupported secret generator:": "Generatore segreti non supportato:",
|
||||
"Unsupported storage mode:": "Modalità storage non supportata:",
|
||||
"Unsupported tmpfs mode": "Modalità tmpfs non supportata",
|
||||
"Unsupported tmpfs options": "Opzioni tmpfs non supportate",
|
||||
"Unsupported tmpfs owner strategy": "Strategia di assegnazione del proprietario tmpfs non supportata",
|
||||
"Unsupported volume options:": "Opzioni volume non supportate:",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host and changes nothing otherwise.": "Finché non vengono nuovamente registrati non possono essere aggiornati o gestiti da qui. Il ripristino controlla innanzitutto che tutto ciò di cui hanno bisogno sia su questo host e non modifica nulla per il resto.",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host; an application that lacks something is not registered.": "Finché non vengono nuovamente registrati non possono essere aggiornati o gestiti da qui. Il ripristino controlla innanzitutto che tutto ciò di cui ha bisogno sia su questo host; un'applicazione a cui manca qualcosa non viene registrata.",
|
||||
|
||||
@@ -216,6 +216,7 @@
|
||||
"API returned an invalid timezone": "API retornou um fuso horário inválido",
|
||||
"API tokens (Home Assistant, scripts) will keep working.": "Os tokens de API (Home Assistant, scripts) continuarão funcionando.",
|
||||
"APP_KEY: 32-byte Base64 key (generate with openssl rand -base64 32)": "APP_KEY: chave Base64 de 32 bytes (gerar com openssl rand -base64 32)",
|
||||
"APP_KEY: use the upstream command openssl rand -base64 32 and prefix its output with base64:": "APP_KEY: execute o comando indicado pelo projeto, openssl rand -base64 32, e acrescente base64: no início do resultado.",
|
||||
"APT IPv4 configuration completed": "Configuração do APT IPv4 concluída",
|
||||
"APT IPv4 configuration removed": "Configuração APT IPv4 removida",
|
||||
"APT and repositories": "APT e repositórios",
|
||||
@@ -1465,6 +1466,7 @@
|
||||
"Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {ui} before retrying; no automatic rollback was attempted.": "Não foi possível concluir ou verificar a mudança de repositório; as alterações podem ser parciais e o estado do repositório é desconhecido. Inspecione {ui} antes de tentar novamente; não foi tentada nenhuma reversão automática.",
|
||||
"Could not complete private network release:": "Não foi possível concluir a liberação da rede privada:",
|
||||
"Could not configure IOMMU kernel parameters automatically. Configure manually and reboot.": "Não foi possível configurar os parâmetros do kernel IOMMU automaticamente. Configure manualmente e reinicie.",
|
||||
"Could not configure the tmpfs mounts": "Não foi possível configurar as montagens tmpfs",
|
||||
"Could not convert the OCI rootfs to privileged": "Não foi possível converter os rootfs do OCI para privilegiado",
|
||||
"Could not copy the PVE keyfile into place. Check permissions on:": "Não foi possível copiar o arquivo-chave PVE no lugar. Verifique as permissões em:",
|
||||
"Could not copy the keyfile into place.": "Não foi possível copiar o arquivo-chave no lugar.",
|
||||
@@ -3310,6 +3312,7 @@
|
||||
"Initiator IQN is authorised on the target": "O iniciador IQN está autorizado no alvo",
|
||||
"Initiator IQN:": "IQN do iniciador:",
|
||||
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape é um software gráfico vetorial de qualidade profissional que funciona em computadores desktop Linux, Mac OS X e Windows.",
|
||||
"Input": "Input",
|
||||
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Dentro do LXC, crie o administrador: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
|
||||
"Inspect disks before any action": "Inspecione os discos antes de qualquer ação",
|
||||
"Inspect host device nodes": "Inspecione os nós do dispositivo host",
|
||||
@@ -6184,6 +6187,7 @@
|
||||
"Self-hosted file sharing with a modern web interface": "Partilha de ficheiros auto- hospedados com uma interface Web moderna",
|
||||
"Self-hosted file sync and sharing.": "Sincronização e compartilhamento de arquivos self-hosted.",
|
||||
"Self-hosted file toolkit for images, video, audio, PDFs, and files": "Kit de ferramentas de arquivo auto hospedado para imagens, vídeo, áudio, PDFs e arquivos",
|
||||
"Self-hosted forms and workflows.": "Formulários e fluxos de trabalho autoalojados.",
|
||||
"Self-hosted household plant-care tracker with reminders, photos, and multiple user accounts.": "Gerenciador auto-hospedado de cuidados com plantas domésticas, com lembretes, fotos e várias contas de usuário.",
|
||||
"Self-hosted image-hosting service with authenticated uploads, image processing, public links, and local storage.": "Serviço auto-hospedado de hospedagem de imagens, com uploads autenticados, processamento de imagens, links públicos e armazenamento local.",
|
||||
"Self-hosted internet archiving solution": "Solução de arquivamento de internet self-hosted",
|
||||
@@ -6789,6 +6793,9 @@
|
||||
"The OCI contract changed while reviewing; nothing was adopted": "O contrato OCI foi alterado durante a revisão;nada foi adotado",
|
||||
"The OCI engine is not installed. Update ProxMenux and try again.": "O motor OCI não está instalado. Atualizar ProxMenux e tentar novamente.",
|
||||
"The OCI image storage was not kept": "O armazenamento de imagens OCI não foi mantido",
|
||||
"The OCI image user or group is ambiguous": "O valor do utilizador ou do grupo da imagem OCI é ambíguo",
|
||||
"The OCI image user or group is not numeric": "O valor do utilizador ou do grupo da imagem OCI não é numérico",
|
||||
"The OCI image user or group is outside the supported range": "O valor do utilizador ou do grupo da imagem OCI está fora do intervalo suportado",
|
||||
"The OCR language must use Tesseract codes, for example eng or eng+spa": "A linguagem OCR deve usar códigos Tesseract, por exemplo eng ou eng+spa",
|
||||
"The PATH of the new image is outside the reproducible profile": "O PATH da nova imagem está fora do perfil reprodutível",
|
||||
"The Paperless persistent volumes need at least 8 GB": "Os volumes persistentes sem papel precisam de pelo menos 8 GB",
|
||||
@@ -6850,6 +6857,8 @@
|
||||
"The application has been recreated with the new options.": "O aplicativo foi recriado com as novas opções.",
|
||||
"The application needs it for": "O aplicativo precisa disso para",
|
||||
"The application stopped during its first start:": "A aplicação parou durante o seu primeiro início:",
|
||||
"The application user or group is not numeric": "O valor do utilizador ou do grupo da aplicação não é numérico",
|
||||
"The application user or group is outside the supported range": "O valor do utilizador ou do grupo da aplicação está fora do intervalo suportado",
|
||||
"The application was not modified.": "O aplicativo não foi modificado.",
|
||||
"The application was removed": "O aplicativo foi removido",
|
||||
"The archive could not be extracted.": "O arquivo não pôde ser extraído.",
|
||||
@@ -7301,6 +7310,7 @@
|
||||
"The sysctl include is unknown or differs from the saved record": "A inclusão do sysctl é desconhecida ou difere do registro salvo",
|
||||
"The temporary password could not be retrieved.": "Não foi possível obter a senha temporária.",
|
||||
"The test will continue even if you close this terminal.": "O teste continuará mesmo se você fechar este terminal.",
|
||||
"The tmpfs mode is specified more than once": "O modo tmpfs foi especificado mais do que uma vez",
|
||||
"The tmpfs mounts of the container differ from the saved record": "As montagens tmpfs do recipiente diferem do registo gravado",
|
||||
"The tmpfs path or size is outside the supported profile": "O caminho ou tamanho do tmpfs está fora do perfil suportado",
|
||||
"The translated recipe changed during the preparation": "A receita traduzida mudou durante a preparação",
|
||||
@@ -7713,7 +7723,9 @@
|
||||
"Unsupported prlimit resource": "Recurso de prlimit não suportado",
|
||||
"Unsupported secret generator:": "Gerador secreto não suportado:",
|
||||
"Unsupported storage mode:": "Modo de armazenamento não suportado:",
|
||||
"Unsupported tmpfs mode": "Modo tmpfs não suportado",
|
||||
"Unsupported tmpfs options": "Opções de tmpfs não suportadas",
|
||||
"Unsupported tmpfs owner strategy": "Estratégia do proprietário do tmpfs não suportada",
|
||||
"Unsupported volume options:": "Opções de volume não suportadas:",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host and changes nothing otherwise.": "Até que sejam registrados novamente, eles não poderão ser atualizados ou gerenciados a partir daqui. A recuperação verifica primeiro se tudo o que eles precisam está neste host e não altera nada de outra forma.",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host; an application that lacks something is not registered.": "Até que sejam registrados novamente, eles não poderão ser atualizados ou gerenciados a partir daqui. A recuperação verifica primeiro se tudo o que precisa está neste host; um aplicativo que falta alguma coisa não está registrado.",
|
||||
|
||||
@@ -216,6 +216,7 @@
|
||||
"API returned an invalid timezone": "API vrátilo neplatné časové pásmo",
|
||||
"API tokens (Home Assistant, scripts) will keep working.": "API tokeny (Home Assistant, skripty) budú fungovať ďalej.",
|
||||
"APP_KEY: 32-byte Base64 key (generate with openssl rand -base64 32)": "APP_KEY: 32-bajtový kľúč zakódovaný v Base64 (vygenerujte príkazom openssl rand -base64 32)",
|
||||
"APP_KEY: use the upstream command openssl rand -base64 32 and prefix its output with base64:": "APP_KEY: spusti príkaz uvedený v projekte openssl rand -base64 32 a pred výsledok vlož base64:.",
|
||||
"APT IPv4 configuration completed": "Nastavenie APT pre IPv4 je dokončené",
|
||||
"APT IPv4 configuration removed": "Nastavenie APT pre IPv4 bolo odstránené",
|
||||
"APT and repositories": "APT a repozitáre",
|
||||
@@ -1465,6 +1466,7 @@
|
||||
"Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {ui} before retrying; no automatic rollback was attempted.": "Nepodarilo sa dokončiť alebo overiť zmenu repozitárov; zmeny môžu byť čiastočné a stav repozitárov je neznámy. Pred ďalším pokusom skontrolujte {ui}. Automatický návrat do pôvodného stavu sa nevykonal.",
|
||||
"Could not complete private network release:": "Nepodarilo sa dokončiť uvoľnenie súkromnej siete:",
|
||||
"Could not configure IOMMU kernel parameters automatically. Configure manually and reboot.": "Parametre kernelu pre IOMMU sa nepodarilo nastaviť automaticky. Nastavte ich ručne a reštartujte.",
|
||||
"Could not configure the tmpfs mounts": "Nepodarilo sa nakonfigurovať pripojenia tmpfs",
|
||||
"Could not convert the OCI rootfs to privileged": "Nepodarilo sa previesť OCI rootfs na privilegované",
|
||||
"Could not copy the PVE keyfile into place. Check permissions on:": "PVE keyfile sa nepodarilo skopírovať na správne miesto. Skontrolujte oprávnenia na:",
|
||||
"Could not copy the keyfile into place.": "Keyfile sa nepodarilo skopírovať na správne miesto.",
|
||||
@@ -3310,6 +3312,7 @@
|
||||
"Initiator IQN is authorised on the target": "IQN iniciátora je na targete povolené",
|
||||
"Initiator IQN:": "IQN iniciátora:",
|
||||
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape je profesionálne kvalitný vektorový grafický softvér, ktorý beží na počítačoch Linux, Mac OS X a Windows.",
|
||||
"Input": "Input",
|
||||
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "V LXC vytvorte správcu príkazom: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
|
||||
"Inspect disks before any action": "Skontrolovať disky pred akoukoľvek akciou",
|
||||
"Inspect host device nodes": "Skontrolovať device nodes na hostovi",
|
||||
@@ -6186,6 +6189,7 @@
|
||||
"Self-hosted file sharing with a modern web interface": "Zdieľanie súborov na vlastnom serveri s moderným webovým rozhraním",
|
||||
"Self-hosted file sync and sharing.": "Vlastná služba na synchronizáciu a zdieľanie súborov.",
|
||||
"Self-hosted file toolkit for images, video, audio, PDFs, and files": "Súprava nástrojov na vlastnom serveri na prácu s obrázkami, videom, zvukom, PDF a ďalšími súbormi",
|
||||
"Self-hosted forms and workflows.": "Samoobslužne hostované formuláre a pracovné postupy.",
|
||||
"Self-hosted household plant-care tracker with reminders, photos, and multiple user accounts.": "Prehľad starostlivosti o izbové rastliny prevádzkovaný na vlastnom serveri s pripomienkami, fotografiami a viacerými používateľskými účtami.",
|
||||
"Self-hosted image-hosting service with authenticated uploads, image processing, public links, and local storage.": "Služba na hosťovanie obrázkov prevádzkovaná na vlastnom serveri s overovaným nahrávaním, spracovaním obrázkov, verejnými odkazmi a lokálnym úložiskom.",
|
||||
"Self-hosted internet archiving solution": "Riešenie na archiváciu internetového obsahu na vlastnom serveri",
|
||||
@@ -6790,6 +6794,9 @@
|
||||
"The OCI contract changed while reviewing; nothing was adopted": "Definícia OCI sa počas kontroly zmenila; nič sa neprevzalo",
|
||||
"The OCI engine is not installed. Update ProxMenux and try again.": "Engine OCI nie je nainštalovaný. Aktualizujte ProxMenux a skúste to znova.",
|
||||
"The OCI image storage was not kept": "Uskladnenie obrazu OCI nebolo uchované",
|
||||
"The OCI image user or group is ambiguous": "Hodnota používateľa alebo skupiny v obraze OCI nie je jednoznačná",
|
||||
"The OCI image user or group is not numeric": "Hodnota používateľa alebo skupiny v obraze OCI nie je číselná",
|
||||
"The OCI image user or group is outside the supported range": "Hodnota používateľa alebo skupiny v obraze OCI je mimo podporovaného rozsahu",
|
||||
"The OCR language must use Tesseract codes, for example eng or eng+spa": "Jazyk OCR musí používať kódy Tesseract, napríklad eng alebo eng+spa",
|
||||
"The PATH of the new image is outside the reproducible profile": "Premenná PATH nového obrazu je mimo profilu, ktorý možno zopakovať",
|
||||
"The Paperless persistent volumes need at least 8 GB": "Trvalé dátové zväzky aplikácie Paperless musia mať najmenej 8 GB",
|
||||
@@ -6851,6 +6858,8 @@
|
||||
"The application has been recreated with the new options.": "Aplikácia bola znovu vytvorená s novými možnosťami.",
|
||||
"The application needs it for": "Aplikácia to potrebuje pre",
|
||||
"The application stopped during its first start:": "Aplikácia sa zastavila počas prvého začiatku:",
|
||||
"The application user or group is not numeric": "Hodnota používateľa alebo skupiny aplikácie nie je číselná",
|
||||
"The application user or group is outside the supported range": "Hodnota používateľa alebo skupiny aplikácie je mimo podporovaného rozsahu",
|
||||
"The application was not modified.": "Aplikácia nebola upravená.",
|
||||
"The application was removed": "Aplikácia bola odstránená",
|
||||
"The archive could not be extracted.": "Archív sa nepodarilo rozbaliť.",
|
||||
@@ -7302,6 +7311,7 @@
|
||||
"The sysctl include is unknown or differs from the saved record": "Pripojený konfiguračný súbor sysctl je neznámy alebo sa líši od uloženého záznamu",
|
||||
"The temporary password could not be retrieved.": "Dočasné heslo nebolo možné získať späť.",
|
||||
"The test will continue even if you close this terminal.": "Test bude pokračovať aj po zatvorení tohto terminálu.",
|
||||
"The tmpfs mode is specified more than once": "Režim tmpfs je zadaný viac než raz",
|
||||
"The tmpfs mounts of the container differ from the saved record": "Pripojenia tmpfs v kontajneri sa líšia od uloženého záznamu",
|
||||
"The tmpfs path or size is outside the supported profile": "Tmpfs cesta alebo veľkosť je mimo podporovaný profil",
|
||||
"The translated recipe changed during the preparation": "Preložený recept sa počas prípravy zmenil",
|
||||
@@ -7714,7 +7724,9 @@
|
||||
"Unsupported prlimit resource": "Nepodporovaný prlimit zdroj",
|
||||
"Unsupported secret generator:": "Nepodporovaný generátor tajných údajov:",
|
||||
"Unsupported storage mode:": "Nepodporovaný režim skladovania:",
|
||||
"Unsupported tmpfs mode": "Nepodporovaný režim tmpfs",
|
||||
"Unsupported tmpfs options": "Nepodporované možnosti tmpfs",
|
||||
"Unsupported tmpfs owner strategy": "Nepodporovaná stratégia vlastníka tmpfs",
|
||||
"Unsupported volume options:": "Nepodporované voľby dátového zväzku:",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host and changes nothing otherwise.": "Kým nie sú znova zaregistrované, nemožno ich aktualizovať ani spravovať odtiaľto. Obnova najprv skontroluje, či všetko, čo potrebujú, je na tomto hostiteľovi a inak nič nezmení.",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host; an application that lacks something is not registered.": "Kým nie sú znova zaregistrované, nemožno ich aktualizovať ani spravovať odtiaľto. Obnova najprv skontroluje, či všetko, čo potrebujú, je na tomto hostiteľovi; aplikácia, ktorej niečo chýba, nie je zaregistrovaná.",
|
||||
|
||||
@@ -216,6 +216,7 @@
|
||||
"API returned an invalid timezone": "API returnerade en ogiltig tidszon",
|
||||
"API tokens (Home Assistant, scripts) will keep working.": "API-tokens (Home Assistant, skript) fortsätter att fungera.",
|
||||
"APP_KEY: 32-byte Base64 key (generate with openssl rand -base64 32)": "APP_KEY: Base64-kodad nyckel på 32 byte (generera med openssl rand -base64 32)",
|
||||
"APP_KEY: use the upstream command openssl rand -base64 32 and prefix its output with base64:": "APP_KEY: kör kommandot som anges av projektet, openssl rand -base64 32, och lägg till base64: före resultatet.",
|
||||
"APT IPv4 configuration completed": "APT IPv4-konfiguration slutförd",
|
||||
"APT IPv4 configuration removed": "APT IPv4-konfiguration har tagits bort",
|
||||
"APT and repositories": "APT och arkiv",
|
||||
@@ -1465,6 +1466,7 @@
|
||||
"Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {ui} before retrying; no automatic rollback was attempted.": "Det gick inte att slutföra eller verifiera bytet av arkiv; ändringarna kan vara ofullständiga och arkivens tillstånd är okänt. Granska {ui} innan du försöker igen; ingen automatisk återställning gjordes.",
|
||||
"Could not complete private network release:": "Kunde inte slutföra det privata nätverket:",
|
||||
"Could not configure IOMMU kernel parameters automatically. Configure manually and reboot.": "Kunde inte konfigurera IOMMU kärnparametrar automatiskt. Konfigurera manuellt och starta om.",
|
||||
"Could not configure the tmpfs mounts": "Kunde inte konfigurera tmpfs-monteringarna",
|
||||
"Could not convert the OCI rootfs to privileged": "Kunde inte konvertera OCI rootfs till privilegierad",
|
||||
"Could not copy the PVE keyfile into place. Check permissions on:": "Det gick inte att kopiera PVE-nyckelfilen på plats. Kontrollera behörigheter på:",
|
||||
"Could not copy the keyfile into place.": "Kunde inte kopiera nyckelfilen på plats.",
|
||||
@@ -3310,6 +3312,7 @@
|
||||
"Initiator IQN is authorised on the target": "Initiator IQN är auktoriserad på målet",
|
||||
"Initiator IQN:": "Initiativtagare IQN:",
|
||||
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape är professionell kvalitet vektor grafik programvara som körs på Linux, Mac OS X och Windows stationära datorer.",
|
||||
"Input": "Input",
|
||||
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Skapa administratören inne i LXC-containern: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
|
||||
"Inspect disks before any action": "Inspektera diskar innan någon åtgärd",
|
||||
"Inspect host device nodes": "Inspektera värdenhetsnoder",
|
||||
@@ -6184,6 +6187,7 @@
|
||||
"Self-hosted file sharing with a modern web interface": "Självhäftad fildelning med ett modernt webbgränssnitt",
|
||||
"Self-hosted file sync and sharing.": "Självhostad filsynkronisering och delning.",
|
||||
"Self-hosted file toolkit for images, video, audio, PDFs, and files": "Självvärd filverktyg för bilder, video, ljud, PDF-filer och filer",
|
||||
"Self-hosted forms and workflows.": "Självhostade formulär och arbetsflöden.",
|
||||
"Self-hosted household plant-care tracker with reminders, photos, and multiple user accounts.": "Självhostad skötselapp för krukväxter med påminnelser, foton och flera användarkonton.",
|
||||
"Self-hosted image-hosting service with authenticated uploads, image processing, public links, and local storage.": "Självhostad bildtjänst med autentiserade uppladdningar, bildbehandling, offentliga länkar och lokal lagring.",
|
||||
"Self-hosted internet archiving solution": "Självhäftad internetarkiveringslösning",
|
||||
@@ -6789,6 +6793,9 @@
|
||||
"The OCI contract changed while reviewing; nothing was adopted": "OCI-kontraktet ändrades under granskning;ingenting antogs",
|
||||
"The OCI engine is not installed. Update ProxMenux and try again.": "OCI-motorn är inte installerad. Uppdatera ProxMenux och försök igen.",
|
||||
"The OCI image storage was not kept": "OCI-bildlagringen hölls inte",
|
||||
"The OCI image user or group is ambiguous": "Värdet för användaren eller gruppen i OCI-avbildningen är tvetydigt",
|
||||
"The OCI image user or group is not numeric": "Värdet för användaren eller gruppen i OCI-avbildningen är inte numeriskt",
|
||||
"The OCI image user or group is outside the supported range": "Värdet för användaren eller gruppen i OCI-avbildningen ligger utanför intervallet som stöds",
|
||||
"The OCR language must use Tesseract codes, for example eng or eng+spa": "OCR-språket måste använda Tesseract-koder, till exempel eng eller eng+spa",
|
||||
"The PATH of the new image is outside the reproducible profile": "Den nya bildens PATH ligger utanför den reproducerbara profilen.",
|
||||
"The Paperless persistent volumes need at least 8 GB": "De papperslösa ihållande volymerna behöver minst 8 GB",
|
||||
@@ -6850,6 +6857,8 @@
|
||||
"The application has been recreated with the new options.": "Applikationen har återskapats med de nya alternativen.",
|
||||
"The application needs it for": "Applikationen behöver den för",
|
||||
"The application stopped during its first start:": "Ansökan slutade under sin första start:",
|
||||
"The application user or group is not numeric": "Värdet för användaren eller gruppen i applikationen är inte numeriskt",
|
||||
"The application user or group is outside the supported range": "Värdet för användaren eller gruppen i applikationen ligger utanför intervallet som stöds",
|
||||
"The application was not modified.": "Applikationen ändrades inte.",
|
||||
"The application was removed": "Ansökan togs bort",
|
||||
"The archive could not be extracted.": "Arkivet kunde inte extraheras.",
|
||||
@@ -7301,6 +7310,7 @@
|
||||
"The sysctl include is unknown or differs from the saved record": "Sysctl inkluderar är okänt eller skiljer sig från det sparade rekordet",
|
||||
"The temporary password could not be retrieved.": "Det tillfälliga lösenordet kunde inte hämtas.",
|
||||
"The test will continue even if you close this terminal.": "Testet fortsätter även om du stänger denna terminal.",
|
||||
"The tmpfs mode is specified more than once": "tmpfs-läget anges fler än en gång",
|
||||
"The tmpfs mounts of the container differ from the saved record": "Tmpfs montage av behållaren skiljer sig från den sparade rekordet",
|
||||
"The tmpfs path or size is outside the supported profile": "Tmpfs-banan eller storleken ligger utanför den stödda profilen",
|
||||
"The translated recipe changed during the preparation": "Det översatta receptet ändrades under förberedelsen",
|
||||
@@ -7713,7 +7723,9 @@
|
||||
"Unsupported prlimit resource": "Prlimit-resurs som inte stöds",
|
||||
"Unsupported secret generator:": "Ostödd hemlig generator:",
|
||||
"Unsupported storage mode:": "Ostödd lagringsläge:",
|
||||
"Unsupported tmpfs mode": "Tmpfs-läge stöds inte",
|
||||
"Unsupported tmpfs options": "Ostödda tmpfs alternativ",
|
||||
"Unsupported tmpfs owner strategy": "Ägarstrategi för tmpfs stöds inte",
|
||||
"Unsupported volume options:": "Ostödda volymalternativ:",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host and changes nothing otherwise.": "Tills de är registrerade igen kan de inte uppdateras eller hanteras härifrån. Återställningen kontrollerar först att allt de behöver finns på denna värd och ändrar inget annat.",
|
||||
"Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host; an application that lacks something is not registered.": "Tills de är registrerade igen kan de inte uppdateras eller hanteras härifrån. Återställningen kontrollerar först att allt de behöver finns på denna värd; en applikation som saknar något registreras inte.",
|
||||
|
||||
+2
-1
@@ -509,8 +509,9 @@ A profile may also change the image, with `image`, for projects that publish one
|
||||
| `external_services` | Names of services the application needs already running, such as `["MariaDB"]`. They are announced before the installation asks how to reach them |
|
||||
| `generated_files` | Files written in the container before its first start, each with `container_path`, `content`, `owner` and `mode`. With `"only_if_missing": true` a file that already exists in a kept volume is not replaced |
|
||||
| `volume_owner` | `uid` and `gid` of the user the application writes with, when it is not 1000 |
|
||||
| `tmpfs_mounts` | Volatile paths with a size and mount options. An optional `owner_strategy` sets ownership to `mapped-root`, `mapped-application-user` (using `uid`/`gid` or `PUID`/`PGID`-style variables), or `image-user` (the effective `lxc.init.uid`/`lxc.init.gid`); `mode` sets permissions. The exact `/run` path requires an owner strategy. |
|
||||
| `non_persistent_image_volumes` | Paths the image declares as volumes that are left inside the rootfs |
|
||||
| `runtime`, `resources`, `security`, `device_requests`, `tmpfs_mounts`, `extra_hosts` | What the Compose keys of the [translation table](#from-a-compose-file-or-a-docker-run-command-to-an-oci-compose) become |
|
||||
| `runtime`, `resources`, `security`, `device_requests`, `extra_hosts` | What the Compose keys of the [translation table](#from-a-compose-file-or-a-docker-run-command-to-an-oci-compose) become |
|
||||
|
||||
### `proxmox.security_profile`: privileges
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
<!-- Generated from oci/catalog/verification.json by .github/scripts/oci_validation.py. Do not edit by hand. -->
|
||||
|
||||
43 of 514 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 2 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
|
||||
43 of 515 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 2 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
|
||||
|
||||
Each test describes the scenario that was run and names the image it ran on. It does not cover every possible configuration of the application, and a newer image has not been tested until someone reports it.
|
||||
|
||||
|
||||
@@ -0,0 +1,460 @@
|
||||
{
|
||||
"schema_version": "0.5.0",
|
||||
"kind": "proxmenux.oci-template",
|
||||
"id": "image-input",
|
||||
"status": "generated-unvalidated",
|
||||
"catalog_ui": {
|
||||
"title": {
|
||||
"en_US": "Input"
|
||||
},
|
||||
"tagline": {
|
||||
"en_US": "Self-hosted forms and workflows."
|
||||
},
|
||||
"description": {
|
||||
"en_US": "A self-hosted form builder using its bundled SQLite configuration for a single-container setup. The application key is required to encrypt stored data."
|
||||
},
|
||||
"category": "business",
|
||||
"category_label": "Business & ERP",
|
||||
"author": "Deck9",
|
||||
"developer": "Deck9",
|
||||
"icon": "https://raw.githubusercontent.com/deck9/input/main/docs/public/input-icon.png",
|
||||
"thumbnail": null,
|
||||
"screenshots": [],
|
||||
"architectures": [
|
||||
"amd64",
|
||||
"arm64"
|
||||
],
|
||||
"launch": {
|
||||
"scheme": "http",
|
||||
"port": 8080,
|
||||
"path": "/"
|
||||
},
|
||||
"website": "https://getinput.co/",
|
||||
"documentation": "https://github.com/deck9/input/blob/main/docs/quick-start.md",
|
||||
"repository": "https://github.com/deck9/input",
|
||||
"tips": [
|
||||
"Create your first account in the web interface. This profile uses the single-container SQLite quick start; production deployments may instead use MariaDB, Redis, and S3."
|
||||
],
|
||||
"mini_changelog": [],
|
||||
"display_version": null,
|
||||
"updated_at": null
|
||||
},
|
||||
"source": {
|
||||
"provider": "Deck9",
|
||||
"repository": "https://github.com/deck9/input",
|
||||
"revision": "2b08ab3b6bd73f12a03beedc19c6a561afe283c0",
|
||||
"readme_raw_url": "https://github.com/deck9/input/blob/main/docs/quick-start.md",
|
||||
"image_repository_url": "https://github.com/deck9/input/pkgs/container/input",
|
||||
"readme_pushed_at": "",
|
||||
"compose_sha256": "2153fd285760e5c8cd84b00efaa73641effa274e035ba86aa0ae153e7c17b8af",
|
||||
"generated_at": "2026-10-26T06:30:00+00:00"
|
||||
},
|
||||
"container_contract": {
|
||||
"service_name": "input",
|
||||
"container_name": "input",
|
||||
"image": {
|
||||
"reference": "ghcr.io/deck9/input:main",
|
||||
"registry": "ghcr.io",
|
||||
"repository": "ghcr.io/deck9/input",
|
||||
"tag": "main",
|
||||
"digest": null,
|
||||
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
|
||||
},
|
||||
"environment": [
|
||||
{
|
||||
"name": "APP_URL",
|
||||
"example": "http://@CONTAINER_ADDRESS@:8080",
|
||||
"required": true,
|
||||
"sensitive": false,
|
||||
"source": "upstream-documentation",
|
||||
"container_address": "http://@CONTAINER_ADDRESS@:8080"
|
||||
},
|
||||
{
|
||||
"name": "APP_KEY",
|
||||
"example": "",
|
||||
"required": true,
|
||||
"sensitive": true,
|
||||
"source": "upstream-documentation"
|
||||
}
|
||||
],
|
||||
"volumes": [
|
||||
{
|
||||
"id": "volume-0",
|
||||
"container_path": "/var/www/html/storage",
|
||||
"compose_source_example": "./storage",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"container_port": 8080,
|
||||
"published_example": 8080,
|
||||
"protocol": "tcp",
|
||||
"required": true,
|
||||
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
|
||||
}
|
||||
],
|
||||
"related_services": [],
|
||||
"restart": "unless-stopped",
|
||||
"stop_grace_period": null,
|
||||
"original_compose": "{\n \"name\": \"input\",\n \"services\": {\n \"input\": {\n \"image\": \"ghcr.io/deck9/input:main\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080\"\n ],\n \"volumes\": [\n \"./storage:/var/www/html/storage\"\n ],\n \"environment\": {\n \"APP_URL\": \"http://@CONTAINER_ADDRESS@:8080\",\n \"APP_KEY\": \"\"\n }\n }\n }\n}"
|
||||
},
|
||||
"compose_stack": {
|
||||
"project_name": "input",
|
||||
"deployment_model": "one-native-oci-lxc-per-compose-service",
|
||||
"user_experience": "single-application-install",
|
||||
"main_service": "input",
|
||||
"service_count": 1,
|
||||
"services": [
|
||||
{
|
||||
"name": "input",
|
||||
"image": "ghcr.io/deck9/input:main",
|
||||
"is_main": true,
|
||||
"role": "frontend",
|
||||
"vmid_offset": 0,
|
||||
"depends_on": [],
|
||||
"frontend_network": true,
|
||||
"private_network": false,
|
||||
"compose": {
|
||||
"image": "ghcr.io/deck9/input:main",
|
||||
"restart": "unless-stopped",
|
||||
"ports": [
|
||||
"8080:8080"
|
||||
],
|
||||
"volumes": [
|
||||
"./storage:/var/www/html/storage"
|
||||
],
|
||||
"environment": {
|
||||
"APP_URL": "http://@CONTAINER_ADDRESS@:8080",
|
||||
"APP_KEY": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"top_level": {
|
||||
"name": "input"
|
||||
},
|
||||
"networking": {
|
||||
"frontend": "selected-proxmox-bridge",
|
||||
"private_required": false,
|
||||
"private_creation": "automatic-create-if-missing",
|
||||
"private_address_allocation": "automatic-static-address-per-service",
|
||||
"service_discovery": "private-addresses-with-compose-service-host-aliases",
|
||||
"dependency_external_access": "disabled-unless-service-publishes-ports",
|
||||
"prompt_user_for_private_network": false
|
||||
},
|
||||
"storage": [
|
||||
{
|
||||
"id": "input-volume-0",
|
||||
"service": "input",
|
||||
"container_path": "/var/www/html/storage",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": true,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": "Host path for Input data at /var/www/html/storage"
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
"reserve_vmids_atomically": 1,
|
||||
"start_order": [
|
||||
"input"
|
||||
],
|
||||
"stop_order": [
|
||||
"input"
|
||||
],
|
||||
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
|
||||
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
|
||||
},
|
||||
"installer_inputs": {
|
||||
"prompted": [
|
||||
"stack_name",
|
||||
"base_vmid",
|
||||
"rootfs_storage",
|
||||
"persistent_data_destinations",
|
||||
"frontend_bridge",
|
||||
"frontend_ipv4_mode"
|
||||
],
|
||||
"automatic": [
|
||||
"dependent_vmids",
|
||||
"private_bridge",
|
||||
"private_subnet",
|
||||
"private_service_addresses",
|
||||
"compose_service_aliases",
|
||||
"generated_secrets",
|
||||
"dependency_start_and_stop_order"
|
||||
],
|
||||
"generated_secrets": [
|
||||
{
|
||||
"id": "app-key",
|
||||
"strategy": "generate-cryptographically-random-at-install",
|
||||
"bindings": [
|
||||
{
|
||||
"service": "input",
|
||||
"environment_variable": "APP_KEY"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"first_run": {
|
||||
"endpoints": [
|
||||
{
|
||||
"label": "Input",
|
||||
"scheme": "http",
|
||||
"port": 8080,
|
||||
"path": "/",
|
||||
"source": "upstream-documentation"
|
||||
}
|
||||
],
|
||||
"credentials": []
|
||||
},
|
||||
"proxmox": {
|
||||
"runtime": "native-oci-lxc",
|
||||
"technology_status": "proxmox-technology-preview",
|
||||
"defaults": {
|
||||
"unprivileged": true,
|
||||
"ostype": "auto-from-image",
|
||||
"cores": 2,
|
||||
"memory_mb": 2048,
|
||||
"swap_mb": 512,
|
||||
"rootfs_size_gb": 8,
|
||||
"rootfs_storage": "local-lvm",
|
||||
"volume_storage": "local-lvm",
|
||||
"template_storage": "local",
|
||||
"bridge": "vmbr0",
|
||||
"ipv4": "dhcp",
|
||||
"firewall": true,
|
||||
"host_managed_network": true,
|
||||
"onboot": false,
|
||||
"features": [
|
||||
"nesting=1"
|
||||
],
|
||||
"shutdown_timeout_seconds": 30
|
||||
},
|
||||
"image_metadata_policy": {
|
||||
"entrypoint": "import-from-oci-image",
|
||||
"cmd": "import-from-oci-image",
|
||||
"environment": "import-image-env-then-apply-compose-overrides",
|
||||
"user": "import-from-oci-image",
|
||||
"working_dir": "import-from-oci-image",
|
||||
"stop_signal": "import-from-oci-image"
|
||||
},
|
||||
"adaptations": [
|
||||
{
|
||||
"id": "imported-compose-source",
|
||||
"upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
|
||||
"native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
|
||||
"reason": "Catalog import must not imply runtime compatibility.",
|
||||
"behavioral_impact": "No automatic installation before review.",
|
||||
"validation": "pending-per-application"
|
||||
},
|
||||
{
|
||||
"id": "rolling-latest-image",
|
||||
"upstream_behavior": "A discovered Compose may pin a release tag or digest.",
|
||||
"native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
|
||||
"reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
|
||||
"behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
|
||||
"validation": "pending-per-application"
|
||||
},
|
||||
{
|
||||
"id": "dedicated-lxc-network",
|
||||
"upstream_behavior": "Docker publishes selected container ports on the Docker host.",
|
||||
"native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
|
||||
"reason": "A native LXC has its own address and does not require Docker port NAT.",
|
||||
"behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
|
||||
"validation": "pending-per-application"
|
||||
},
|
||||
{
|
||||
"id": "compose-shm-size",
|
||||
"upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
|
||||
"native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
|
||||
"reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
|
||||
"behavioral_impact": "None expected.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-command",
|
||||
"upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
|
||||
"native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
|
||||
"reason": "Proxmox stores the effective OCI process as one entrypoint string.",
|
||||
"behavioral_impact": "None expected.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-privileged-mode",
|
||||
"upstream_behavior": "Compose selects whether the container runs in privileged mode.",
|
||||
"native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
|
||||
"reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
|
||||
"behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
|
||||
"validation": "native-equivalent"
|
||||
},
|
||||
{
|
||||
"id": "compose-process-runtime",
|
||||
"upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
|
||||
"native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
|
||||
"reason": "The OCI process must start with the same identity, command and working directory without Docker.",
|
||||
"behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-healthcheck",
|
||||
"upstream_behavior": "Docker periodically executes the declared container healthcheck.",
|
||||
"native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
|
||||
"reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
|
||||
"behavioral_impact": "The check runs during installation rather than continuously after installation.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-cpu-priority",
|
||||
"upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
|
||||
"native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
|
||||
"reason": "Both settings express relative CPU priority on different scales.",
|
||||
"behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-resource-limits",
|
||||
"upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
|
||||
"native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
|
||||
"reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
|
||||
"behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-network-identity",
|
||||
"upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
|
||||
"native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
|
||||
"reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
|
||||
"behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-network-mode",
|
||||
"upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
|
||||
"native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
|
||||
"reason": "The LXC is the application host and already has its own address and port namespace.",
|
||||
"behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-capabilities-and-sysctls",
|
||||
"upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
|
||||
"native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
|
||||
"reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
|
||||
"behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "docker-engine-metadata",
|
||||
"upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
|
||||
"native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
|
||||
"reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
|
||||
"behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-device-passthrough",
|
||||
"upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
|
||||
"native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
|
||||
"reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
|
||||
"behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-host-ipc",
|
||||
"upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
|
||||
"native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
|
||||
"reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
|
||||
"behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
|
||||
"validation": "not-requested-by-compose"
|
||||
}
|
||||
],
|
||||
"installer_profile": {
|
||||
"volume_preparations": [
|
||||
{
|
||||
"container_path": "/var/www/html/storage",
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"startup_healthcheck": {
|
||||
"scheme": "http",
|
||||
"port": 8080,
|
||||
"path": "/",
|
||||
"timeout_seconds": 180,
|
||||
"request_timeout_seconds": 10,
|
||||
"stability_seconds": 4,
|
||||
"verify_tls": true
|
||||
},
|
||||
"tmpfs_mounts": [
|
||||
{
|
||||
"id": "runtime",
|
||||
"container_path": "/run",
|
||||
"default_size_mb": 64,
|
||||
"minimum_size_mb": 16,
|
||||
"prompt_size": false,
|
||||
"mount_options": [
|
||||
"rw",
|
||||
"nosuid",
|
||||
"nodev"
|
||||
],
|
||||
"owner_strategy": "image-user",
|
||||
"mode": "0755"
|
||||
}
|
||||
],
|
||||
"generated_sensitive_environment": {
|
||||
"APP_KEY": {
|
||||
"strategy": "token-hex",
|
||||
"bytes": 16,
|
||||
"prompt": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
"automatic_install_candidate": true,
|
||||
"validated": false,
|
||||
"supported_compose_keys": [
|
||||
"image",
|
||||
"restart",
|
||||
"ports",
|
||||
"volumes",
|
||||
"environment"
|
||||
],
|
||||
"untranslated_blockers": [],
|
||||
"policy": "The image runs nginx and PHP-FPM as its OCI image user. ProxMenux provides a native /run tmpfs owned by that effective LXC init user, with writable mode for the runtime files. The recipe remains unvalidated until a clean install passes its startup health check."
|
||||
},
|
||||
"validation": {
|
||||
"schema": "passed-at-generation",
|
||||
"clean_install": "pending",
|
||||
"service_health": "pending",
|
||||
"restart_persistence": "pending",
|
||||
"backup_restore": "pending",
|
||||
"update_preserves_data": "pending"
|
||||
},
|
||||
"lifecycle": {
|
||||
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
|
||||
"registry_state": {
|
||||
"resolved_architecture": null,
|
||||
"resolved_digest": null,
|
||||
"image_version_label": null,
|
||||
"image_created": null
|
||||
},
|
||||
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
|
||||
"automatic_unattended_updates": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
{
|
||||
"schema_version": "0.5.0",
|
||||
"kind": "proxmenux.oci-template",
|
||||
"id": "image-input",
|
||||
"status": "generated-unvalidated",
|
||||
"catalog_ui": {
|
||||
"title": {
|
||||
"en_US": "Input"
|
||||
},
|
||||
"tagline": {
|
||||
"en_US": "Self-hosted forms and workflows."
|
||||
},
|
||||
"description": {
|
||||
"en_US": "A self-hosted form builder using its bundled SQLite configuration for a single-container setup. The application key is required to encrypt stored data."
|
||||
},
|
||||
"category": "business",
|
||||
"category_label": "Business & ERP",
|
||||
"author": "Deck9",
|
||||
"developer": "Deck9",
|
||||
"icon": "https://raw.githubusercontent.com/deck9/input/main/docs/public/input-icon.png",
|
||||
"thumbnail": null,
|
||||
"screenshots": [],
|
||||
"architectures": [
|
||||
"amd64",
|
||||
"arm64"
|
||||
],
|
||||
"launch": {
|
||||
"scheme": "http",
|
||||
"port": 8080,
|
||||
"path": "/"
|
||||
},
|
||||
"website": "https://getinput.co/",
|
||||
"documentation": "https://github.com/deck9/input/blob/main/docs/quick-start.md",
|
||||
"repository": "https://github.com/deck9/input",
|
||||
"tips": [
|
||||
"Create your first account in the web interface. This profile uses the single-container SQLite quick start; production deployments may instead use MariaDB, Redis, and S3."
|
||||
],
|
||||
"mini_changelog": [],
|
||||
"display_version": null,
|
||||
"updated_at": null
|
||||
},
|
||||
"source": {
|
||||
"provider": "Deck9",
|
||||
"repository": "https://github.com/deck9/input",
|
||||
"revision": "2b08ab3b6bd73f12a03beedc19c6a561afe283c0",
|
||||
"readme_raw_url": "https://github.com/deck9/input/blob/main/docs/quick-start.md",
|
||||
"image_repository_url": "https://github.com/deck9/input/pkgs/container/input",
|
||||
"readme_pushed_at": "",
|
||||
"compose_sha256": "2153fd285760e5c8cd84b00efaa73641effa274e035ba86aa0ae153e7c17b8af",
|
||||
"generated_at": "2026-10-26T06:30:00+00:00"
|
||||
},
|
||||
"container_contract": {
|
||||
"service_name": "input",
|
||||
"container_name": "input",
|
||||
"image": {
|
||||
"reference": "ghcr.io/deck9/input:main",
|
||||
"registry": "ghcr.io",
|
||||
"repository": "ghcr.io/deck9/input",
|
||||
"tag": "main",
|
||||
"digest": null,
|
||||
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
|
||||
},
|
||||
"environment": [
|
||||
{
|
||||
"name": "APP_URL",
|
||||
"example": "http://@CONTAINER_ADDRESS@:8080",
|
||||
"required": true,
|
||||
"sensitive": false,
|
||||
"source": "upstream-documentation",
|
||||
"container_address": "http://@CONTAINER_ADDRESS@:8080"
|
||||
},
|
||||
{
|
||||
"name": "APP_KEY",
|
||||
"example": "",
|
||||
"required": true,
|
||||
"sensitive": true,
|
||||
"source": "upstream-documentation"
|
||||
}
|
||||
],
|
||||
"volumes": [
|
||||
{
|
||||
"id": "volume-0",
|
||||
"container_path": "/var/www/html/storage",
|
||||
"compose_source_example": "./storage",
|
||||
"read_only": false,
|
||||
"required": true,
|
||||
"installation_choice": [
|
||||
"managed-volume",
|
||||
"host-bind"
|
||||
],
|
||||
"default": "managed-volume",
|
||||
"managed_volume": {
|
||||
"backup": true,
|
||||
"default_size_gb": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"container_port": 8080,
|
||||
"published_example": 8080,
|
||||
"protocol": "tcp",
|
||||
"required": true,
|
||||
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
|
||||
}
|
||||
],
|
||||
"related_services": [],
|
||||
"restart": "unless-stopped",
|
||||
"stop_grace_period": null,
|
||||
"original_compose": "{\n \"name\": \"input\",\n \"services\": {\n \"input\": {\n \"image\": \"ghcr.io/deck9/input:main\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080\"\n ],\n \"volumes\": [\n \"./storage:/var/www/html/storage\"\n ],\n \"environment\": {\n \"APP_URL\": \"http://@CONTAINER_ADDRESS@:8080\",\n \"APP_KEY\": \"\"\n }\n }\n }\n}"
|
||||
},
|
||||
"compose_stack": {
|
||||
"project_name": "input",
|
||||
"deployment_model": "one-native-oci-lxc-per-compose-service",
|
||||
"user_experience": "single-application-install",
|
||||
"main_service": "input",
|
||||
"service_count": 1,
|
||||
"services": [
|
||||
{
|
||||
"name": "input",
|
||||
"image": "ghcr.io/deck9/input:main",
|
||||
"is_main": true,
|
||||
"role": "frontend",
|
||||
"vmid_offset": 0,
|
||||
"depends_on": [],
|
||||
"frontend_network": true,
|
||||
"private_network": false,
|
||||
"compose": {
|
||||
"image": "ghcr.io/deck9/input:main",
|
||||
"restart": "unless-stopped",
|
||||
"ports": [
|
||||
"8080:8080"
|
||||
],
|
||||
"volumes": [
|
||||
"./storage:/var/www/html/storage"
|
||||
],
|
||||
"environment": {
|
||||
"APP_URL": "http://@CONTAINER_ADDRESS@:8080",
|
||||
"APP_KEY": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"top_level": {
|
||||
"name": "input"
|
||||
},
|
||||
"networking": {
|
||||
"frontend": "selected-proxmox-bridge",
|
||||
"private_required": false,
|
||||
"private_creation": "automatic-create-if-missing",
|
||||
"private_address_allocation": "automatic-static-address-per-service",
|
||||
"service_discovery": "private-addresses-with-compose-service-host-aliases",
|
||||
"dependency_external_access": "disabled-unless-service-publishes-ports",
|
||||
"prompt_user_for_private_network": false
|
||||
},
|
||||
"storage": [
|
||||
{
|
||||
"id": "input-volume-0",
|
||||
"service": "input",
|
||||
"container_path": "/var/www/html/storage",
|
||||
"mode": "managed-volume",
|
||||
"user_selectable": true,
|
||||
"backup": true,
|
||||
"shared_with_other_lxc": false,
|
||||
"source_path": null,
|
||||
"source_path_prompt": "Host path for Input data at /var/www/html/storage"
|
||||
}
|
||||
],
|
||||
"orchestration": {
|
||||
"reserve_vmids_atomically": 1,
|
||||
"start_order": [
|
||||
"input"
|
||||
],
|
||||
"stop_order": [
|
||||
"input"
|
||||
],
|
||||
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
|
||||
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
|
||||
},
|
||||
"installer_inputs": {
|
||||
"prompted": [
|
||||
"stack_name",
|
||||
"base_vmid",
|
||||
"rootfs_storage",
|
||||
"persistent_data_destinations",
|
||||
"frontend_bridge",
|
||||
"frontend_ipv4_mode"
|
||||
],
|
||||
"automatic": [
|
||||
"dependent_vmids",
|
||||
"private_bridge",
|
||||
"private_subnet",
|
||||
"private_service_addresses",
|
||||
"compose_service_aliases",
|
||||
"generated_secrets",
|
||||
"dependency_start_and_stop_order"
|
||||
],
|
||||
"generated_secrets": [
|
||||
{
|
||||
"id": "app-key",
|
||||
"strategy": "generate-cryptographically-random-at-install",
|
||||
"bindings": [
|
||||
{
|
||||
"service": "input",
|
||||
"environment_variable": "APP_KEY"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"first_run": {
|
||||
"endpoints": [
|
||||
{
|
||||
"label": "Input",
|
||||
"scheme": "http",
|
||||
"port": 8080,
|
||||
"path": "/",
|
||||
"source": "upstream-documentation"
|
||||
}
|
||||
],
|
||||
"credentials": []
|
||||
},
|
||||
"proxmox": {
|
||||
"runtime": "native-oci-lxc",
|
||||
"technology_status": "proxmox-technology-preview",
|
||||
"defaults": {
|
||||
"unprivileged": true,
|
||||
"ostype": "auto-from-image",
|
||||
"cores": 2,
|
||||
"memory_mb": 2048,
|
||||
"swap_mb": 512,
|
||||
"rootfs_size_gb": 8,
|
||||
"rootfs_storage": "local-lvm",
|
||||
"volume_storage": "local-lvm",
|
||||
"template_storage": "local",
|
||||
"bridge": "vmbr0",
|
||||
"ipv4": "dhcp",
|
||||
"firewall": true,
|
||||
"host_managed_network": true,
|
||||
"onboot": false,
|
||||
"features": [
|
||||
"nesting=1"
|
||||
],
|
||||
"shutdown_timeout_seconds": 30
|
||||
},
|
||||
"image_metadata_policy": {
|
||||
"entrypoint": "import-from-oci-image",
|
||||
"cmd": "import-from-oci-image",
|
||||
"environment": "import-image-env-then-apply-compose-overrides",
|
||||
"user": "import-from-oci-image",
|
||||
"working_dir": "import-from-oci-image",
|
||||
"stop_signal": "import-from-oci-image"
|
||||
},
|
||||
"adaptations": [
|
||||
{
|
||||
"id": "imported-compose-source",
|
||||
"upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
|
||||
"native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
|
||||
"reason": "Catalog import must not imply runtime compatibility.",
|
||||
"behavioral_impact": "No automatic installation before review.",
|
||||
"validation": "pending-per-application"
|
||||
},
|
||||
{
|
||||
"id": "rolling-latest-image",
|
||||
"upstream_behavior": "A discovered Compose may pin a release tag or digest.",
|
||||
"native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
|
||||
"reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
|
||||
"behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
|
||||
"validation": "pending-per-application"
|
||||
},
|
||||
{
|
||||
"id": "dedicated-lxc-network",
|
||||
"upstream_behavior": "Docker publishes selected container ports on the Docker host.",
|
||||
"native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
|
||||
"reason": "A native LXC has its own address and does not require Docker port NAT.",
|
||||
"behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
|
||||
"validation": "pending-per-application"
|
||||
},
|
||||
{
|
||||
"id": "compose-shm-size",
|
||||
"upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
|
||||
"native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
|
||||
"reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
|
||||
"behavioral_impact": "None expected.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-command",
|
||||
"upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
|
||||
"native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
|
||||
"reason": "Proxmox stores the effective OCI process as one entrypoint string.",
|
||||
"behavioral_impact": "None expected.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-privileged-mode",
|
||||
"upstream_behavior": "Compose selects whether the container runs in privileged mode.",
|
||||
"native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
|
||||
"reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
|
||||
"behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
|
||||
"validation": "native-equivalent"
|
||||
},
|
||||
{
|
||||
"id": "compose-process-runtime",
|
||||
"upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
|
||||
"native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
|
||||
"reason": "The OCI process must start with the same identity, command and working directory without Docker.",
|
||||
"behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-healthcheck",
|
||||
"upstream_behavior": "Docker periodically executes the declared container healthcheck.",
|
||||
"native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
|
||||
"reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
|
||||
"behavioral_impact": "The check runs during installation rather than continuously after installation.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-cpu-priority",
|
||||
"upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
|
||||
"native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
|
||||
"reason": "Both settings express relative CPU priority on different scales.",
|
||||
"behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-resource-limits",
|
||||
"upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
|
||||
"native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
|
||||
"reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
|
||||
"behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-network-identity",
|
||||
"upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
|
||||
"native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
|
||||
"reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
|
||||
"behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-network-mode",
|
||||
"upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
|
||||
"native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
|
||||
"reason": "The LXC is the application host and already has its own address and port namespace.",
|
||||
"behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-capabilities-and-sysctls",
|
||||
"upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
|
||||
"native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
|
||||
"reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
|
||||
"behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "docker-engine-metadata",
|
||||
"upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
|
||||
"native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
|
||||
"reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
|
||||
"behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-device-passthrough",
|
||||
"upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
|
||||
"native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
|
||||
"reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
|
||||
"behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
|
||||
"validation": "not-requested-by-compose"
|
||||
},
|
||||
{
|
||||
"id": "compose-host-ipc",
|
||||
"upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
|
||||
"native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
|
||||
"reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
|
||||
"behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
|
||||
"validation": "not-requested-by-compose"
|
||||
}
|
||||
],
|
||||
"installer_profile": {
|
||||
"volume_preparations": [
|
||||
{
|
||||
"container_path": "/var/www/html/storage",
|
||||
"remove_lost_found": true,
|
||||
"owner_strategy": "mapped-application-user",
|
||||
"only_when_mount_type": "managed-volume"
|
||||
}
|
||||
],
|
||||
"startup_healthcheck": {
|
||||
"scheme": "http",
|
||||
"port": 8080,
|
||||
"path": "/",
|
||||
"timeout_seconds": 180,
|
||||
"request_timeout_seconds": 10,
|
||||
"stability_seconds": 4,
|
||||
"verify_tls": true
|
||||
},
|
||||
"tmpfs_mounts": [
|
||||
{
|
||||
"id": "runtime",
|
||||
"container_path": "/run",
|
||||
"default_size_mb": 64,
|
||||
"minimum_size_mb": 16,
|
||||
"prompt_size": false,
|
||||
"mount_options": [
|
||||
"rw",
|
||||
"nosuid",
|
||||
"nodev"
|
||||
],
|
||||
"owner_strategy": "image-user",
|
||||
"mode": "0755"
|
||||
}
|
||||
],
|
||||
"generated_sensitive_environment": {
|
||||
"APP_KEY": {
|
||||
"strategy": "token-hex",
|
||||
"bytes": 16,
|
||||
"prompt": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
"automatic_install_candidate": true,
|
||||
"validated": false,
|
||||
"supported_compose_keys": [
|
||||
"image",
|
||||
"restart",
|
||||
"ports",
|
||||
"volumes",
|
||||
"environment"
|
||||
],
|
||||
"untranslated_blockers": [],
|
||||
"policy": "The image runs nginx and PHP-FPM as its OCI image user. ProxMenux provides a native /run tmpfs owned by that effective LXC init user, with writable mode for the runtime files. The recipe remains unvalidated until a clean install passes its startup health check."
|
||||
},
|
||||
"validation": {
|
||||
"schema": "passed-at-generation",
|
||||
"clean_install": "pending",
|
||||
"service_health": "pending",
|
||||
"restart_persistence": "pending",
|
||||
"backup_restore": "pending",
|
||||
"update_preserves_data": "pending"
|
||||
},
|
||||
"lifecycle": {
|
||||
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
|
||||
"registry_state": {
|
||||
"resolved_architecture": null,
|
||||
"resolved_digest": null,
|
||||
"image_version_label": null,
|
||||
"image_created": null
|
||||
},
|
||||
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
|
||||
"automatic_unattended_updates": false
|
||||
}
|
||||
}
|
||||
@@ -6646,6 +6646,38 @@
|
||||
"hidden": false,
|
||||
"template_id": "linuxserver-inkscape"
|
||||
},
|
||||
{
|
||||
"id": "input",
|
||||
"provider": "official",
|
||||
"template_family": "curated-profile",
|
||||
"title": "Input",
|
||||
"repository": "https://github.com/deck9/input",
|
||||
"description": "A self-hosted form builder using its bundled SQLite configuration for a single-container setup. The application key is required to encrypt stored data.",
|
||||
"website": "https://getinput.co/",
|
||||
"icon": "https://raw.githubusercontent.com/deck9/input/main/docs/public/input-icon.png",
|
||||
"architectures": [
|
||||
"amd64",
|
||||
"arm64"
|
||||
],
|
||||
"updated_at": null,
|
||||
"main_image": "ghcr.io/deck9/input:main",
|
||||
"category": "business",
|
||||
"category_label": "Business & ERP",
|
||||
"replaces_discovered_ids": [],
|
||||
"curated_path": "catalog/curated/input.json",
|
||||
"template": "apps/input.json",
|
||||
"template_status": "generated-unvalidated",
|
||||
"automatic_install_candidate": true,
|
||||
"untranslated_blockers": [],
|
||||
"requires_privileged_lxc": false,
|
||||
"optional_privileged_lxc": false,
|
||||
"requires_host_pid_namespace": false,
|
||||
"requires_relaxed_confinement": false,
|
||||
"optional_relaxed_confinement": false,
|
||||
"requires_security_confirmation": false,
|
||||
"hidden": false,
|
||||
"template_id": "image-input"
|
||||
},
|
||||
{
|
||||
"id": "instatic",
|
||||
"provider": "CoreBunch",
|
||||
|
||||
@@ -2005,17 +2005,6 @@ for MOUNT_WARNING in ${MOUNT_WARNINGS[@]+"${MOUNT_WARNINGS[@]}"}; do
|
||||
msg_warn "$MOUNT_WARNING"
|
||||
done
|
||||
|
||||
while IFS=$'\t' read -r TARGET SIZE_MB OPTIONS; do
|
||||
[[ -n $TARGET ]] || continue
|
||||
[[ $TARGET == /* && $TARGET != *","* && $TARGET != *[[:space:]]* ]] \
|
||||
|| die "$(translate "Invalid tmpfs path:") $TARGET"
|
||||
[[ $SIZE_MB =~ ^[0-9]+$ && $SIZE_MB -gt 0 ]] || die "$(translate "Invalid tmpfs size:") $TARGET"
|
||||
[[ $OPTIONS =~ ^(rw|ro|nosuid|nodev|noexec|mode=0[0-7]{3})(,(rw|ro|nosuid|nodev|noexec|mode=0[0-7]{3}))*$ ]] || die "$(translate "Invalid tmpfs options:") $TARGET"
|
||||
TARGET_RELATIVE=${TARGET#/}
|
||||
printf 'lxc.mount.entry: tmpfs %s tmpfs %s,size=%sM,create=dir 0 0\n' \
|
||||
"$TARGET_RELATIVE" "$OPTIONS" "$SIZE_MB" >>"$CONF"
|
||||
done < <(jq -r '.tmpfs_mounts[]? | [.container_path,.size_mb,(.mount_options | join(","))] | @tsv' "$DEPLOYMENT_FILE")
|
||||
|
||||
DEVICE_INDEX=0
|
||||
RESOLVED_CHARACTER_DEVICES=()
|
||||
NVIDIA_RUNTIME_CONFIGURED=0
|
||||
@@ -2101,6 +2090,13 @@ prepare_managed_format_directories() {
|
||||
prepare_managed_format_directories "/var/lib/lxc/${VMID}/rootfs"
|
||||
apply_extra_hosts
|
||||
apply_installer_profile
|
||||
# Render tmpfs entries after the image and installer profile have set the
|
||||
# effective LXC init uid/gid. The same renderer validates and records them.
|
||||
TMPFS_CONFIG=$(mktemp)
|
||||
python3 "${SCRIPT_DIR}/oci_runtime_settings.py" --render-tmpfs "$DEPLOYMENT_FILE" "$CONF" \
|
||||
>"$TMPFS_CONFIG" || { rm -f "$TMPFS_CONFIG"; die "$(translate "Could not configure the tmpfs mounts")"; }
|
||||
cat "$TMPFS_CONFIG" >>"$CONF"
|
||||
rm -f "$TMPFS_CONFIG"
|
||||
ensure_runtime_home
|
||||
apply_rlimits
|
||||
apply_host_monitor
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
"""Validate declared volatile mounts and native network sysctl includes."""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
@@ -25,15 +27,63 @@ def sysctl_content(deployment):
|
||||
return ''.join(result)
|
||||
|
||||
|
||||
def tmpfs_lines(deployment):
|
||||
def _config_ids(config):
|
||||
"""Read the effective OCI init identity written by Proxmox."""
|
||||
if config is None:
|
||||
return 0, 0
|
||||
text = config.decode() if isinstance(config, bytes) else str(config)
|
||||
values = {}
|
||||
for line in text.splitlines():
|
||||
if ': ' not in line:
|
||||
continue
|
||||
key, value = line.split(': ', 1)
|
||||
if key in ('lxc.init.uid', 'lxc.init.gid'):
|
||||
if key in values:
|
||||
raise ValueError(translate('The OCI image user or group is ambiguous'))
|
||||
if not re.fullmatch(r'[0-9]{1,10}', value):
|
||||
raise ValueError(translate('The OCI image user or group is not numeric'))
|
||||
number = int(value)
|
||||
if number > 4294967294:
|
||||
raise ValueError(translate('The OCI image user or group is outside the supported range'))
|
||||
values[key] = number
|
||||
return values.get('lxc.init.uid', 0), values.get('lxc.init.gid', 0)
|
||||
|
||||
|
||||
def _application_ids(deployment, item):
|
||||
"""Resolve Compose-style application IDs inside the LXC user namespace."""
|
||||
environment = {}
|
||||
for variable in deployment.get('environment', []):
|
||||
environment[variable.get('name')] = variable.get('value')
|
||||
uid = item.get('uid')
|
||||
gid = item.get('gid')
|
||||
if uid is None:
|
||||
uid = next((environment[name] for name in ('PUID', 'USER_ID', 'UID')
|
||||
if environment.get(name) not in (None, '')), 0)
|
||||
if gid is None:
|
||||
gid = next((environment[name] for name in ('PGID', 'GROUP_ID', 'GID')
|
||||
if environment.get(name) not in (None, '')), 0)
|
||||
result = []
|
||||
for value in (uid, gid):
|
||||
if isinstance(value, bool) or not re.fullmatch(r'[0-9]{1,10}', str(value)):
|
||||
raise ValueError(translate('The application user or group is not numeric'))
|
||||
number = int(value)
|
||||
if number > 4294967294:
|
||||
raise ValueError(translate('The application user or group is outside the supported range'))
|
||||
result.append(number)
|
||||
return tuple(result)
|
||||
|
||||
|
||||
def tmpfs_lines(deployment, config=None):
|
||||
result = []
|
||||
targets = set()
|
||||
persistent = [m['container_path'].rstrip('/') for m in deployment.get('mounts', [])]
|
||||
for item in deployment.get('tmpfs_mounts', []):
|
||||
target, size = item['container_path'], item['size_mb']
|
||||
owner_strategy = item.get('owner_strategy')
|
||||
if (not re.fullmatch(r'/[A-Za-z0-9_./-]+', target) or '..' in target.split('/')
|
||||
or '//' in target or target.endswith('/') or target in ('/etc','/usr','/bin','/lib','/lib64','/sbin','/proc','/sys','/dev','/run')
|
||||
or not (target.startswith(('/run/', '/tmp/', '/var/cache/')) or target == '/dev/shm')
|
||||
or '//' in target or target.endswith('/') or target in ('/etc','/usr','/bin','/lib','/lib64','/sbin','/proc','/sys','/dev')
|
||||
or target == '/run' and owner_strategy not in ('mapped-root', 'mapped-application-user', 'image-user')
|
||||
or not (target == '/run' or target.startswith(('/run/', '/tmp/', '/var/cache/')) or target == '/dev/shm')
|
||||
or isinstance(size, bool) or not isinstance(size, int) or size < 1):
|
||||
raise ValueError(translate('The tmpfs path or size is outside the supported profile'))
|
||||
if any(target == p or target.startswith(p+'/') or p.startswith(target+'/') for p in [*persistent,*targets]):
|
||||
@@ -43,11 +93,35 @@ def tmpfs_lines(deployment):
|
||||
raise ValueError(translate('Unsupported tmpfs options'))
|
||||
if len(options) != len(set(options)) or ('rw' in options and 'ro' in options):
|
||||
raise ValueError(translate('Contradictory tmpfs options'))
|
||||
mode = item.get('mode')
|
||||
if mode is not None:
|
||||
if not isinstance(mode, str) or not re.fullmatch(r'0[0-7]{3}', mode):
|
||||
raise ValueError(translate('Unsupported tmpfs mode'))
|
||||
if any(option.startswith('mode=') for option in options):
|
||||
raise ValueError(translate('The tmpfs mode is specified more than once'))
|
||||
if owner_strategy is not None:
|
||||
if owner_strategy == 'mapped-root':
|
||||
uid, gid = 0, 0
|
||||
elif owner_strategy == 'mapped-application-user':
|
||||
uid, gid = _application_ids(deployment, item)
|
||||
elif owner_strategy == 'image-user':
|
||||
uid, gid = _config_ids(config)
|
||||
else:
|
||||
raise ValueError(translate('Unsupported tmpfs owner strategy'))
|
||||
options = [*options, f'uid={uid}', f'gid={gid}']
|
||||
if mode is not None:
|
||||
options.append(f'mode={mode}')
|
||||
targets.add(target)
|
||||
result.append(f'tmpfs {target.lstrip("/")} tmpfs {",".join(options)},size={size}M,create=dir 0 0')
|
||||
return result
|
||||
|
||||
|
||||
def render_tmpfs(deployment_path, config_path):
|
||||
deployment = json.loads(Path(deployment_path).read_text())
|
||||
config = Path(config_path).read_bytes()
|
||||
return ''.join(f'lxc.mount.entry: {line}\n' for line in tmpfs_lines(deployment, config))
|
||||
|
||||
|
||||
# /etc/pve/lxc is the folder of the local node only; this one is the same on
|
||||
# every node of a cluster, so a migrated container still finds its include.
|
||||
CLUSTER_DIR = Path('/etc/pve/proxmenux')
|
||||
@@ -85,7 +159,7 @@ def seccomp_path(vmid):
|
||||
|
||||
|
||||
def check(config, deployment, vmid):
|
||||
expected = tmpfs_lines(deployment)
|
||||
expected = tmpfs_lines(deployment, config)
|
||||
lines = config.decode().splitlines()
|
||||
actual = [line.split(': ',1)[1] for line in lines if line.startswith('lxc.mount.entry: tmpfs ')]
|
||||
if sorted(actual) != sorted(expected):
|
||||
@@ -123,11 +197,11 @@ def file_binds(config):
|
||||
return found
|
||||
|
||||
|
||||
def filter_config(config, deployment):
|
||||
def filter_config(config, deployment, runtime_config=None):
|
||||
"""The configuration without the entries of the mounts the deployment
|
||||
declares itself: what is left belongs to the acceleration profile."""
|
||||
config = nested_mounts.without(config)
|
||||
declared = set(tmpfs_lines(deployment))
|
||||
declared = set(tmpfs_lines(deployment, runtime_config if runtime_config is not None else config))
|
||||
files = {m['source'] for m in deployment.get('mounts', []) if m.get('type') == 'host-bind'}
|
||||
|
||||
def own(line):
|
||||
@@ -141,7 +215,11 @@ def filter_config(config, deployment):
|
||||
|
||||
def check_recovery(config, state):
|
||||
plans = [state.get(key, {}).get('deployment', {}) for key in ('record', 'candidate_contract')]
|
||||
permitted = {line for plan in plans for line in tmpfs_lines(plan)}
|
||||
configs = [config]
|
||||
before_config = state.get('before_config')
|
||||
if isinstance(before_config, str):
|
||||
configs.append(before_config.encode())
|
||||
permitted = {line for plan in plans for source in configs for line in tmpfs_lines(plan, source)}
|
||||
for line in config.decode().splitlines():
|
||||
if line.startswith('lxc.mount.entry: tmpfs ') and line.split(': ', 1)[1] not in permitted:
|
||||
raise ValueError(translate('A tmpfs mount is not part of the journal; recovery blocked'))
|
||||
@@ -180,3 +258,11 @@ def restore(deployment, vmid):
|
||||
finally:
|
||||
if os.path.exists(temporary):
|
||||
os.unlink(temporary)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--render-tmpfs', nargs=2, metavar=('DEPLOYMENT', 'CONFIG'))
|
||||
args = parser.parse_args()
|
||||
if args.render_tmpfs:
|
||||
print(render_tmpfs(*args.render_tmpfs), end='')
|
||||
|
||||
@@ -578,8 +578,16 @@ def build_deployment(
|
||||
"container_path": item["container_path"],
|
||||
"size_mb": size_mb,
|
||||
"mount_options": item.get("mount_options", ["rw", "nosuid", "nodev"]),
|
||||
**({"owner_strategy": item["owner_strategy"]} if item.get("owner_strategy") else {}),
|
||||
**({"mode": item["mode"]} if item.get("mode") is not None else {}),
|
||||
**({"uid": item["uid"]} if item.get("uid") is not None else {}),
|
||||
**({"gid": item["gid"]} if item.get("gid") is not None else {}),
|
||||
}
|
||||
)
|
||||
if item.get("owner_strategy") == "mapped-application-user":
|
||||
for key in ("uid", "gid"):
|
||||
if key not in tmpfs_mounts[-1] and installer_profile.get("volume_owner", {}).get(key) is not None:
|
||||
tmpfs_mounts[-1][key] = installer_profile["volume_owner"][key]
|
||||
|
||||
if advanced:
|
||||
bridge = ask_bridge(ui, translate("Network bridge"), defaults["bridge"])
|
||||
|
||||
@@ -326,8 +326,16 @@ def refresh_template(candidate, ui):
|
||||
size = item['default_size_mb']
|
||||
if item.get('prompt_size', True):
|
||||
size = positive_integer(ui, f"{translate('Tmpfs size in MiB for')} {item['container_path']}", size, item.get('minimum_size_mb', 1))
|
||||
tmpfs.append({'container_path': item['container_path'], 'size_mb': size,
|
||||
'mount_options': copy.deepcopy(item.get('mount_options', ['rw', 'nosuid', 'nodev']))})
|
||||
tmpfs_mount = {'container_path': item['container_path'], 'size_mb': size,
|
||||
'mount_options': copy.deepcopy(item.get('mount_options', ['rw', 'nosuid', 'nodev']))}
|
||||
for key in ('owner_strategy', 'mode', 'uid', 'gid'):
|
||||
if item.get(key) is not None:
|
||||
tmpfs_mount[key] = copy.deepcopy(item[key])
|
||||
if item.get('owner_strategy') == 'mapped-application-user':
|
||||
for key in ('uid', 'gid'):
|
||||
if key not in tmpfs_mount and profile.get('volume_owner', {}).get(key) is not None:
|
||||
tmpfs_mount[key] = copy.deepcopy(profile['volume_owner'][key])
|
||||
tmpfs.append(tmpfs_mount)
|
||||
deployment['tmpfs_mounts'] = tmpfs
|
||||
sysctls = deployment.get('security', {}).get('sysctls', [])
|
||||
for item in profile.get('security', {}).get('sysctls', []):
|
||||
|
||||
@@ -53,10 +53,13 @@ class RuntimeHomeInstallerContractTests(unittest.TestCase):
|
||||
self.assertIn('set_runtime_environment HOME "$home"', self.source)
|
||||
|
||||
def test_home_fallback_runs_after_runtime_user_is_applied(self):
|
||||
self.assertIn(
|
||||
"apply_extra_hosts\napply_installer_profile\nensure_runtime_home\napply_rlimits",
|
||||
self.source,
|
||||
)
|
||||
profile = self.source.index("apply_installer_profile\n", self.source.index("apply_extra_hosts\n"))
|
||||
tmpfs = self.source.index("--render-tmpfs \"$DEPLOYMENT_FILE\" \"$CONF\"", profile)
|
||||
home = self.source.index("ensure_runtime_home\n", tmpfs)
|
||||
limits = self.source.index("apply_rlimits\n", home)
|
||||
self.assertLess(profile, tmpfs)
|
||||
self.assertLess(tmpfs, home)
|
||||
self.assertLess(home, limits)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
"""Ownership and mode are preserved on volatile native OCI-LXC mounts."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / 'remote'))
|
||||
sys.path.insert(0, str(ROOT / 'src'))
|
||||
|
||||
import oci_runtime_settings as runtime_settings
|
||||
|
||||
|
||||
class TmpfsOwnershipTests(unittest.TestCase):
|
||||
def test_image_user_is_taken_from_the_effective_lxc_init_identity(self):
|
||||
plan = {'tmpfs_mounts': [{
|
||||
'container_path': '/run', 'size_mb': 64, 'mount_options': ['rw', 'nosuid', 'nodev'],
|
||||
'owner_strategy': 'image-user', 'mode': '0755',
|
||||
}]}
|
||||
config = b'lxc.init.uid: 65534\nlxc.init.gid: 65534\n'
|
||||
self.assertEqual(runtime_settings.tmpfs_lines(plan, config), [
|
||||
'tmpfs run tmpfs rw,nosuid,nodev,uid=65534,gid=65534,mode=0755,size=64M,create=dir 0 0'
|
||||
])
|
||||
|
||||
def test_mapped_root_and_application_user_strategies(self):
|
||||
root = {'tmpfs_mounts': [{'container_path': '/run/app', 'size_mb': 32,
|
||||
'mount_options': ['rw'], 'owner_strategy': 'mapped-root'}]}
|
||||
app = {'environment': [{'name': 'PUID', 'value': '1001'}, {'name': 'PGID', 'value': '1002'}],
|
||||
'tmpfs_mounts': [{'container_path': '/run/app', 'size_mb': 32,
|
||||
'mount_options': ['rw'], 'owner_strategy': 'mapped-application-user'}]}
|
||||
overridden = {'environment': [{'name': 'PUID', 'value': '1001'}, {'name': 'PGID', 'value': '1002'}],
|
||||
'tmpfs_mounts': [{'container_path': '/run/app', 'size_mb': 32,
|
||||
'mount_options': ['rw'], 'owner_strategy': 'mapped-application-user',
|
||||
'uid': 2001, 'gid': 2002}]}
|
||||
self.assertIn('uid=0,gid=0', runtime_settings.tmpfs_lines(root)[0])
|
||||
self.assertIn('uid=1001,gid=1002', runtime_settings.tmpfs_lines(app)[0])
|
||||
self.assertIn('uid=2001,gid=2002', runtime_settings.tmpfs_lines(overridden)[0])
|
||||
|
||||
def test_plain_run_mount_is_rejected_without_an_owner_strategy(self):
|
||||
plan = {'tmpfs_mounts': [{'container_path': '/run', 'size_mb': 64,
|
||||
'mount_options': ['rw', 'nosuid', 'nodev']}]}
|
||||
with self.assertRaises(ValueError):
|
||||
runtime_settings.tmpfs_lines(plan)
|
||||
|
||||
def test_invalid_owner_ids_modes_and_duplicate_init_identity_are_rejected(self):
|
||||
app = {'tmpfs_mounts': [{'container_path': '/run/app', 'size_mb': 32,
|
||||
'mount_options': ['rw'], 'owner_strategy': 'mapped-application-user',
|
||||
'uid': 'root', 'gid': 10}]}
|
||||
mode = {'tmpfs_mounts': [{'container_path': '/run/app', 'size_mb': 32,
|
||||
'mount_options': ['rw'], 'owner_strategy': 'mapped-root', 'mode': '777'}]}
|
||||
image = {'tmpfs_mounts': [{'container_path': '/run/app', 'size_mb': 32,
|
||||
'mount_options': ['rw'], 'owner_strategy': 'image-user'}]}
|
||||
with self.assertRaises(ValueError):
|
||||
runtime_settings.tmpfs_lines(app)
|
||||
with self.assertRaises(ValueError):
|
||||
runtime_settings.tmpfs_lines(mode)
|
||||
with self.assertRaises(ValueError):
|
||||
runtime_settings.tmpfs_lines(image, b'lxc.init.uid: 1000\nlxc.init.uid: 1001\n')
|
||||
|
||||
def test_runtime_validation_requires_the_exact_rendered_owner_and_mode(self):
|
||||
plan = {'vmid': 120, 'tmpfs_mounts': [{
|
||||
'container_path': '/run', 'size_mb': 64, 'mount_options': ['rw'],
|
||||
'owner_strategy': 'image-user', 'mode': '0755',
|
||||
}]}
|
||||
entry = 'tmpfs run tmpfs rw,uid=1000,gid=1001,mode=0755,size=64M,create=dir 0 0'
|
||||
runtime_settings.check(
|
||||
f'lxc.init.uid: 1000\nlxc.init.gid: 1001\nlxc.mount.entry: {entry}\n'.encode(), plan, 120
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
runtime_settings.check(
|
||||
f'lxc.init.uid: 1000\nlxc.init.gid: 1001\n'
|
||||
'lxc.mount.entry: tmpfs run tmpfs rw,uid=0,gid=0,mode=0755,size=64M,create=dir 0 0\n'.encode(),
|
||||
plan, 120,
|
||||
)
|
||||
|
||||
def test_acceleration_filter_recognises_image_owned_tmpfs_entry(self):
|
||||
plan = {'tmpfs_mounts': [{'container_path': '/run', 'size_mb': 64, 'mount_options': ['rw'],
|
||||
'owner_strategy': 'image-user', 'mode': '0755'}]}
|
||||
line = ('lxc.mount.entry: tmpfs run tmpfs rw,uid=1000,gid=1001,mode=0755,'
|
||||
'size=64M,create=dir 0 0\n')
|
||||
config = ('lxc.init.uid: 1000\nlxc.init.gid: 1001\n' + line).encode()
|
||||
filtered = runtime_settings.filter_config(config, plan).decode()
|
||||
self.assertNotIn('tmpfs', filtered)
|
||||
self.assertIn('lxc.init.uid: 1000', filtered)
|
||||
|
||||
def test_recovery_accepts_only_journalled_image_user_tmpfs_entries(self):
|
||||
plan = {'tmpfs_mounts': [{'container_path': '/run', 'size_mb': 64, 'mount_options': ['rw'],
|
||||
'owner_strategy': 'image-user', 'mode': '0755'}]}
|
||||
old = b'lxc.init.uid: 1000\nlxc.init.gid: 1001\n'
|
||||
current_line = ('lxc.mount.entry: tmpfs run tmpfs rw,uid=2000,gid=2001,mode=0755,'
|
||||
'size=64M,create=dir 0 0\n')
|
||||
current = ('lxc.init.uid: 2000\nlxc.init.gid: 2001\n' + current_line).encode()
|
||||
state = {'vmid': 120, 'before_config': old.decode(), 'record': {'deployment': plan},
|
||||
'candidate_contract': {'deployment': plan}}
|
||||
runtime_settings.check_recovery(current, state)
|
||||
unknown = current.replace(b'uid=2000,gid=2001', b'uid=2000,gid=3001')
|
||||
with self.assertRaises(ValueError):
|
||||
runtime_settings.check_recovery(unknown, state)
|
||||
|
||||
def test_input_is_visible_installable_and_uses_its_image_user_for_run(self):
|
||||
template = json.loads((ROOT / 'catalog/curated/input.json').read_text())
|
||||
profile = template['proxmox']['installer_profile']
|
||||
entry = next(item for item in profile['tmpfs_mounts'] if item['container_path'] == '/run')
|
||||
index = json.loads((ROOT / 'catalog/index.json').read_text())['applications']
|
||||
index_item = next(item for item in index if item['id'] == 'input')
|
||||
self.assertEqual(entry['owner_strategy'], 'image-user')
|
||||
self.assertEqual(entry['mode'], '0755')
|
||||
self.assertTrue(template['compatibility']['automatic_install_candidate'])
|
||||
self.assertTrue(index_item['automatic_install_candidate'])
|
||||
self.assertFalse(index_item['hidden'])
|
||||
self.assertFalse(template['compatibility']['validated'])
|
||||
|
||||
def test_installer_renders_tmpfs_after_the_profile_sets_the_image_identity(self):
|
||||
script = (ROOT / 'remote/install_oci.sh').read_text()
|
||||
profile = script.index('apply_installer_profile\n', script.index('prepare_managed_format_directories "/var/lib'))
|
||||
render = script.index('--render-tmpfs "$DEPLOYMENT_FILE" "$CONF"')
|
||||
self.assertLess(profile, render)
|
||||
|
||||
def test_recipe_renderer_uses_the_same_lines_as_runtime_validation(self):
|
||||
plan = {'tmpfs_mounts': [{'container_path': '/run', 'size_mb': 64, 'mount_options': ['rw'],
|
||||
'owner_strategy': 'image-user', 'mode': '0755'}]}
|
||||
config = b'lxc.init.uid: 65534\nlxc.init.gid: 65534\n'
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
deployment_path = Path(temporary) / 'deployment.json'
|
||||
config_path = Path(temporary) / 'container.conf'
|
||||
deployment_path.write_text(json.dumps(plan))
|
||||
config_path.write_bytes(config)
|
||||
self.assertEqual(runtime_settings.render_tmpfs(deployment_path, config_path),
|
||||
'lxc.mount.entry: ' + runtime_settings.tmpfs_lines(plan, config)[0] + '\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user