From ec5fbd668f43982c96f1b94076dfefa39fc11962 Mon Sep 17 00:00:00 2001 From: Jonatan Date: Tue, 27 Oct 2026 01:50:12 +0100 Subject: [PATCH] Add ten OCI application profiles --- lang/de.json | 33 +- lang/es.json | 33 +- lang/fr.json | 33 +- lang/it.json | 33 +- lang/pt.json | 33 +- lang/sk.json | 33 +- lang/sv.json | 33 +- oci/VALIDATION.md | 2 +- oci/catalog/apps/activepieces.json | 524 +++++++++++++++++++ oci/catalog/apps/bytebase.json | 445 ++++++++++++++++ oci/catalog/apps/campfire.json | 457 +++++++++++++++++ oci/catalog/apps/chhoto-url.json | 500 ++++++++++++++++++ oci/catalog/apps/ech0.json | 482 +++++++++++++++++ oci/catalog/apps/kopia.json | 540 ++++++++++++++++++++ oci/catalog/apps/metabase.json | 534 +++++++++++++++++++ oci/catalog/apps/openhabittracker.json | 533 +++++++++++++++++++ oci/catalog/apps/restic-rest-server.json | 315 ++++++++++++ oci/catalog/apps/watcharr.json | 453 ++++++++++++++++ oci/catalog/categories.json | 12 +- oci/catalog/curated/activepieces.json | 524 +++++++++++++++++++ oci/catalog/curated/bytebase.json | 445 ++++++++++++++++ oci/catalog/curated/campfire.json | 457 +++++++++++++++++ oci/catalog/curated/chhoto-url.json | 500 ++++++++++++++++++ oci/catalog/curated/ech0.json | 482 +++++++++++++++++ oci/catalog/curated/kopia.json | 540 ++++++++++++++++++++ oci/catalog/curated/metabase.json | 534 +++++++++++++++++++ oci/catalog/curated/openhabittracker.json | 533 +++++++++++++++++++ oci/catalog/curated/restic-rest-server.json | 315 ++++++++++++ oci/catalog/curated/watcharr.json | 453 ++++++++++++++++ oci/catalog/index.json | 320 ++++++++++++ oci/tests/test_next10_a_profiles.py | 54 ++ oci/tests/test_next10_b_profiles.py | 156 ++++++ oci/tests/test_next10_c_profiles.py | 176 +++++++ 33 files changed, 10508 insertions(+), 9 deletions(-) create mode 100644 oci/catalog/apps/activepieces.json create mode 100644 oci/catalog/apps/bytebase.json create mode 100644 oci/catalog/apps/campfire.json create mode 100644 oci/catalog/apps/chhoto-url.json create mode 100644 oci/catalog/apps/ech0.json create mode 100644 oci/catalog/apps/kopia.json create mode 100644 oci/catalog/apps/metabase.json create mode 100644 oci/catalog/apps/openhabittracker.json create mode 100644 oci/catalog/apps/restic-rest-server.json create mode 100644 oci/catalog/apps/watcharr.json create mode 100644 oci/catalog/curated/activepieces.json create mode 100644 oci/catalog/curated/bytebase.json create mode 100644 oci/catalog/curated/campfire.json create mode 100644 oci/catalog/curated/chhoto-url.json create mode 100644 oci/catalog/curated/ech0.json create mode 100644 oci/catalog/curated/kopia.json create mode 100644 oci/catalog/curated/metabase.json create mode 100644 oci/catalog/curated/openhabittracker.json create mode 100644 oci/catalog/curated/restic-rest-server.json create mode 100644 oci/catalog/curated/watcharr.json create mode 100644 oci/tests/test_next10_a_profiles.py create mode 100644 oci/tests/test_next10_b_profiles.py create mode 100644 oci/tests/test_next10_c_profiles.py diff --git a/lang/de.json b/lang/de.json index eefc9822..f4cafc49 100644 --- a/lang/de.json +++ b/lang/de.json @@ -8869,5 +8869,36 @@ "⚠ Disk data will NOT be erased.": "⚠ Festplattendaten werden NICHT gelöscht.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Die Festplatte wird ausgehängt und aus /etc/fstab entfernt.", "⚠ The /etc/fstab entry will be removed.": "⚠ Der /etc/fstab-Eintrag wird entfernt.", - "⚠ The disk will be unmounted.": "⚠ Die Festplatte wird ausgehängt." + "⚠ The disk will be unmounted.": "⚠ Die Festplatte wird ausgehängt.", + "A REST backend for restic backups; it has no web interface.": "Ein REST-Backend für restic-Sicherungen ohne Weboberfläche.", + "A lightweight self-hosted URL shortener with SQLite storage.": "Ein schlanker, selbst gehosteter URL-Kürzer mit SQLite-Speicher.", + "A personal, self-hosted microblog and timeline.": "Ein persönlicher, selbst gehosteter Microblog mit Chronik.", + "A self-hosted app for organizing tabletop role-playing campaigns.": "Eine selbst gehostete App zum Organisieren von Pen-and-Paper-Rollenspielkampagnen.", + "A self-hosted tracker for habits, tasks, notes and goals.": "Ein selbst gehosteter Tracker für Gewohnheiten, Aufgaben, Notizen und Ziele.", + "Activepieces": "Activepieces", + "Build reports and dashboards from your data.": "Erstellen Sie Berichte und Dashboards aus Ihren Daten.", + "Build visual automations for personal and hobby use.": "Erstellen Sie visuelle Automatisierungen für private Zwecke und Hobbys.", + "Campfire first-run setup": "Ersteinrichtung von Campfire", + "Chhoto URL": "Chhoto URL", + "Chhoto URL administrator password": "Administratorkennwort für Chhoto URL", + "Configured username": "Konfigurierter Benutzername", + "Create encrypted, deduplicated backups and restore files.": "Erstellen Sie verschlüsselte, deduplizierte Sicherungen und stellen Sie Dateien wieder her.", + "Create with create_user in the container console": "Erstellen Sie den Benutzer mit create_user in der Container-Konsole", + "Created during first-run setup": "Wird bei der Ersteinrichtung erstellt", + "Ech0": "Ech0", + "Email address for the OpenHabitTracker account": "E-Mail-Adresse für das OpenHabitTracker-Konto", + "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Geben Sie die Basis-URL ein, über die Clients und Webhook-Anbieter diese Activepieces-Instanz erreichen.", + "First Ech0 account (becomes Owner)": "Erstes Ech0-Konto (wird zum Eigentümer)", + "First Watcharr account (becomes administrator)": "Erstes Watcharr-Konto (wird zum Administrator)", + "Kopia Web UI": "Kopia-Weboberfläche", + "Kopia Web UI login": "Anmeldung an der Kopia-Weboberfläche", + "Manage database changes, access and workflows.": "Verwalten Sie Datenbankänderungen, Zugriffsrechte und Workflows.", + "OpenHabitTracker": "OpenHabitTracker", + "OpenHabitTracker account": "OpenHabitTracker-Konto", + "OpenHabitTracker initial username": "Erster OpenHabitTracker-Benutzername", + "REST API Basic Authentication user": "Benutzer für die HTTP-Basisauthentifizierung der REST-API", + "Repository encryption password": "Kennwort zur Verschlüsselung des Repositorys", + "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Setzen Sie den Wert nur in einem privaten LAN mit ausschließlich HTTP auf 1; andernfalls verwenden Sie HTTPS.", + "Track movies, television, anime and games you want to enjoy.": "Behalten Sie Filme, Serien, Anime und Spiele im Blick, die Sie ansehen oder spielen möchten.", + "Watcharr": "Watcharr" } diff --git a/lang/es.json b/lang/es.json index 8111306a..4eb4c627 100644 --- a/lang/es.json +++ b/lang/es.json @@ -8841,5 +8841,36 @@ "⚠ Disk data will NOT be erased.": "⚠ Los datos del disco NO se borrarán.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ El disco se desmontará y se eliminará de /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ Se eliminará la entrada /etc/fstab.", - "⚠ The disk will be unmounted.": "⚠ El disco se desmontará." + "⚠ The disk will be unmounted.": "⚠ El disco se desmontará.", + "A REST backend for restic backups; it has no web interface.": "Backend REST para copias de seguridad de restic, sin interfaz web.", + "A lightweight self-hosted URL shortener with SQLite storage.": "Acortador de URL ligero y autoalojado con almacenamiento SQLite.", + "A personal, self-hosted microblog and timeline.": "Microblog y cronología personales, autoalojados.", + "A self-hosted app for organizing tabletop role-playing campaigns.": "Aplicación autoalojada para organizar campañas de juegos de rol de mesa.", + "A self-hosted tracker for habits, tasks, notes and goals.": "Registro autoalojado de hábitos, tareas, notas y objetivos.", + "Activepieces": "Activepieces", + "Build reports and dashboards from your data.": "Crea informes y paneles a partir de tus datos.", + "Build visual automations for personal and hobby use.": "Crea automatizaciones visuales para uso personal o aficiones.", + "Campfire first-run setup": "Configuración inicial de Campfire", + "Chhoto URL": "Chhoto URL", + "Chhoto URL administrator password": "Contraseña de administrador de Chhoto URL", + "Configured username": "Nombre de usuario configurado", + "Create encrypted, deduplicated backups and restore files.": "Crea copias de seguridad cifradas y deduplicadas y restaura archivos.", + "Create with create_user in the container console": "Crea el usuario con create_user desde la consola del contenedor", + "Created during first-run setup": "Se crea durante la configuración inicial", + "Ech0": "Ech0", + "Email address for the OpenHabitTracker account": "Correo electrónico de la cuenta de OpenHabitTracker", + "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Introduce la URL base que usarán los clientes y proveedores de webhooks para acceder a esta instancia de Activepieces.", + "First Ech0 account (becomes Owner)": "Primera cuenta de Ech0 (será la propietaria)", + "First Watcharr account (becomes administrator)": "Primera cuenta de Watcharr (será la administradora)", + "Kopia Web UI": "Interfaz web de Kopia", + "Kopia Web UI login": "Acceso a la interfaz web de Kopia", + "Manage database changes, access and workflows.": "Gestiona cambios en bases de datos, accesos y flujos de trabajo.", + "OpenHabitTracker": "OpenHabitTracker", + "OpenHabitTracker account": "Cuenta de OpenHabitTracker", + "OpenHabitTracker initial username": "Nombre de usuario inicial de OpenHabitTracker", + "REST API Basic Authentication user": "Usuario de autenticación básica para la API REST", + "Repository encryption password": "Contraseña de cifrado del repositorio", + "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Establécelo en 1 solo en una LAN privada que utilice exclusivamente HTTP; en los demás casos, usa HTTPS.", + "Track movies, television, anime and games you want to enjoy.": "Lleva un registro de películas, series, anime y videojuegos que quieras disfrutar.", + "Watcharr": "Watcharr" } diff --git a/lang/fr.json b/lang/fr.json index 8a67e093..4ea0c21d 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -8868,5 +8868,36 @@ "⚠ Disk data will NOT be erased.": "⚠ Les données du disque ne seront PAS effacées.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Le disque sera démonté et supprimé de /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ L'entrée /etc/fstab sera supprimée.", - "⚠ The disk will be unmounted.": "⚠ Le disque sera démonté." + "⚠ The disk will be unmounted.": "⚠ Le disque sera démonté.", + "A REST backend for restic backups; it has no web interface.": "Un backend REST pour les sauvegardes restic, sans interface web.", + "A lightweight self-hosted URL shortener with SQLite storage.": "Un raccourcisseur d’URL léger et auto-hébergé avec stockage SQLite.", + "A personal, self-hosted microblog and timeline.": "Un microblog et un fil d’actualité personnels, auto-hébergés.", + "A self-hosted app for organizing tabletop role-playing campaigns.": "Une application auto-hébergée pour organiser des campagnes de jeux de rôle sur table.", + "A self-hosted tracker for habits, tasks, notes and goals.": "Un outil auto-hébergé pour suivre les habitudes, tâches, notes et objectifs.", + "Activepieces": "Activepieces", + "Build reports and dashboards from your data.": "Créez des rapports et des tableaux de bord à partir de vos données.", + "Build visual automations for personal and hobby use.": "Créez des automatisations visuelles pour un usage personnel ou vos loisirs.", + "Campfire first-run setup": "Configuration initiale de Campfire", + "Chhoto URL": "Chhoto URL", + "Chhoto URL administrator password": "Mot de passe administrateur de Chhoto URL", + "Configured username": "Nom d’utilisateur configuré", + "Create encrypted, deduplicated backups and restore files.": "Créez des sauvegardes chiffrées et dédupliquées, puis restaurez vos fichiers.", + "Create with create_user in the container console": "Créez l’utilisateur avec create_user dans la console du conteneur", + "Created during first-run setup": "Créé lors de la configuration initiale", + "Ech0": "Ech0", + "Email address for the OpenHabitTracker account": "Adresse e-mail du compte OpenHabitTracker", + "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Saisissez l’URL de base que les clients et fournisseurs de webhooks utiliseront pour accéder à cette instance d’Activepieces.", + "First Ech0 account (becomes Owner)": "Premier compte Ech0 (il devient propriétaire)", + "First Watcharr account (becomes administrator)": "Premier compte Watcharr (il devient administrateur)", + "Kopia Web UI": "Interface web de Kopia", + "Kopia Web UI login": "Connexion à l’interface web de Kopia", + "Manage database changes, access and workflows.": "Gérez les modifications de base de données, les accès et les flux de travail.", + "OpenHabitTracker": "OpenHabitTracker", + "OpenHabitTracker account": "Compte OpenHabitTracker", + "OpenHabitTracker initial username": "Nom d’utilisateur initial d’OpenHabitTracker", + "REST API Basic Authentication user": "Utilisateur de l’authentification HTTP Basic de l’API REST", + "Repository encryption password": "Mot de passe de chiffrement du dépôt", + "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Définissez la valeur sur 1 uniquement pour un réseau local privé utilisant exclusivement HTTP ; sinon, utilisez HTTPS.", + "Track movies, television, anime and games you want to enjoy.": "Suivez les films, séries, animés et jeux vidéo que vous souhaitez découvrir.", + "Watcharr": "Watcharr" } diff --git a/lang/it.json b/lang/it.json index 9e09689e..e30374da 100644 --- a/lang/it.json +++ b/lang/it.json @@ -8869,5 +8869,36 @@ "⚠ Disk data will NOT be erased.": "⚠ I dati del disco NON verranno cancellati.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Il disco verrà smontato e rimosso da /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ La voce /etc/fstab verrà rimossa.", - "⚠ The disk will be unmounted.": "⚠ Il disco verrà smontato." + "⚠ The disk will be unmounted.": "⚠ Il disco verrà smontato.", + "A REST backend for restic backups; it has no web interface.": "Un backend REST per i backup di restic, senza interfaccia web.", + "A lightweight self-hosted URL shortener with SQLite storage.": "Un accorciatore di URL leggero e self-hosted con archivio SQLite.", + "A personal, self-hosted microblog and timeline.": "Un microblog e una cronologia personali, self-hosted.", + "A self-hosted app for organizing tabletop role-playing campaigns.": "Un’app self-hosted per organizzare campagne di giochi di ruolo da tavolo.", + "A self-hosted tracker for habits, tasks, notes and goals.": "Un tracker self-hosted per abitudini, attività, note e obiettivi.", + "Activepieces": "Activepieces", + "Build reports and dashboards from your data.": "Crea report e dashboard a partire dai tuoi dati.", + "Build visual automations for personal and hobby use.": "Crea automazioni visive per uso personale e per i tuoi hobby.", + "Campfire first-run setup": "Configurazione iniziale di Campfire", + "Chhoto URL": "Chhoto URL", + "Chhoto URL administrator password": "Password amministratore di Chhoto URL", + "Configured username": "Nome utente configurato", + "Create encrypted, deduplicated backups and restore files.": "Crea backup cifrati e deduplicati e ripristina i file.", + "Create with create_user in the container console": "Crea l’utente con create_user nella console del container", + "Created during first-run setup": "Creato durante la configurazione iniziale", + "Ech0": "Ech0", + "Email address for the OpenHabitTracker account": "Indirizzo e-mail dell’account OpenHabitTracker", + "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Inserisci l’URL di base che client e provider webhook useranno per raggiungere questa istanza di Activepieces.", + "First Ech0 account (becomes Owner)": "Primo account Ech0 (diventa proprietario)", + "First Watcharr account (becomes administrator)": "Primo account Watcharr (diventa amministratore)", + "Kopia Web UI": "Interfaccia web di Kopia", + "Kopia Web UI login": "Accesso all’interfaccia web di Kopia", + "Manage database changes, access and workflows.": "Gestisci le modifiche al database, gli accessi e i flussi di lavoro.", + "OpenHabitTracker": "OpenHabitTracker", + "OpenHabitTracker account": "Account OpenHabitTracker", + "OpenHabitTracker initial username": "Nome utente iniziale di OpenHabitTracker", + "REST API Basic Authentication user": "Utente per l’autenticazione di base dell’API REST", + "Repository encryption password": "Password di cifratura del repository", + "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Imposta il valore su 1 solo per una LAN privata che usa esclusivamente HTTP; altrimenti usa HTTPS.", + "Track movies, television, anime and games you want to enjoy.": "Tieni traccia di film, serie TV, anime e videogiochi che vuoi guardare o provare.", + "Watcharr": "Watcharr" } diff --git a/lang/pt.json b/lang/pt.json index f81f2682..668e5714 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -8869,5 +8869,36 @@ "⚠ Disk data will NOT be erased.": "⚠ Os dados do disco NÃO serão apagados.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ O disco será desmontado e removido de /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ A entrada /etc/fstab será removida.", - "⚠ The disk will be unmounted.": "⚠ O disco será desmontado." + "⚠ The disk will be unmounted.": "⚠ O disco será desmontado.", + "A REST backend for restic backups; it has no web interface.": "Um backend REST para cópias de segurança do restic, sem interface web.", + "A lightweight self-hosted URL shortener with SQLite storage.": "Um encurtador de URLs leve e autoalojado, com armazenamento SQLite.", + "A personal, self-hosted microblog and timeline.": "Um microblog e uma cronologia pessoais, autoalojados.", + "A self-hosted app for organizing tabletop role-playing campaigns.": "Uma aplicação autoalojada para organizar campanhas de jogos de interpretação de mesa.", + "A self-hosted tracker for habits, tasks, notes and goals.": "Um gestor autoalojado de hábitos, tarefas, notas e objetivos.", + "Activepieces": "Activepieces", + "Build reports and dashboards from your data.": "Crie relatórios e painéis com os seus dados.", + "Build visual automations for personal and hobby use.": "Crie automações visuais para uso pessoal e passatempos.", + "Campfire first-run setup": "Configuração inicial do Campfire", + "Chhoto URL": "Chhoto URL", + "Chhoto URL administrator password": "Palavra-passe de administrador do Chhoto URL", + "Configured username": "Nome de utilizador configurado", + "Create encrypted, deduplicated backups and restore files.": "Crie cópias de segurança cifradas e deduplicadas e restaure ficheiros.", + "Create with create_user in the container console": "Crie o utilizador com create_user na consola do contentor", + "Created during first-run setup": "Criado durante a configuração inicial", + "Ech0": "Ech0", + "Email address for the OpenHabitTracker account": "Endereço de e-mail da conta OpenHabitTracker", + "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Introduza o URL base que os clientes e fornecedores de webhooks utilizarão para aceder a esta instância do Activepieces.", + "First Ech0 account (becomes Owner)": "Primeira conta Ech0 (passa a ser proprietária)", + "First Watcharr account (becomes administrator)": "Primeira conta Watcharr (passa a ser administradora)", + "Kopia Web UI": "Interface web do Kopia", + "Kopia Web UI login": "Acesso à interface web do Kopia", + "Manage database changes, access and workflows.": "Gira alterações à base de dados, acessos e fluxos de trabalho.", + "OpenHabitTracker": "OpenHabitTracker", + "OpenHabitTracker account": "Conta OpenHabitTracker", + "OpenHabitTracker initial username": "Nome de utilizador inicial do OpenHabitTracker", + "REST API Basic Authentication user": "Utilizador da autenticação básica da API REST", + "Repository encryption password": "Palavra-passe de encriptação do repositório", + "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Defina como 1 apenas numa LAN privada que utilize exclusivamente HTTP; caso contrário, utilize HTTPS.", + "Track movies, television, anime and games you want to enjoy.": "Acompanhe os filmes, séries, anime e videojogos que quer ver ou jogar.", + "Watcharr": "Watcharr" } diff --git a/lang/sk.json b/lang/sk.json index f0ded102..898894d2 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -8870,5 +8870,36 @@ "⚠ Disk data will NOT be erased.": "⚠ Dáta na disku sa NEVYMAŽÚ.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disk sa odpojí a odstráni z /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ Záznam v /etc/fstab bude odstránený.", - "⚠ The disk will be unmounted.": "⚠ Disk bude odpojený." + "⚠ The disk will be unmounted.": "⚠ Disk bude odpojený.", + "A REST backend for restic backups; it has no web interface.": "REST backend na zálohy restic bez webového rozhrania.", + "A lightweight self-hosted URL shortener with SQLite storage.": "Odľahčovač URL adries s vlastným hostingom a úložiskom SQLite.", + "A personal, self-hosted microblog and timeline.": "Osobný mikroblog a časová os s vlastným hostingom.", + "A self-hosted app for organizing tabletop role-playing campaigns.": "Aplikácia s vlastným hostingom na organizovanie kampaní stolových hier na hrdinov.", + "A self-hosted tracker for habits, tasks, notes and goals.": "Sledovanie návykov, úloh, poznámok a cieľov s vlastným hostingom.", + "Activepieces": "Activepieces", + "Build reports and dashboards from your data.": "Vytvárajte prehľady a dashboardy zo svojich údajov.", + "Build visual automations for personal and hobby use.": "Vytvárajte vizuálne automatizácie na osobné použitie a koníčky.", + "Campfire first-run setup": "Prvotné nastavenie Campfire", + "Chhoto URL": "Chhoto URL", + "Chhoto URL administrator password": "Heslo správcu pre Chhoto URL", + "Configured username": "Nastavené používateľské meno", + "Create encrypted, deduplicated backups and restore files.": "Vytvárajte šifrované, deduplikované zálohy a obnovujte súbory.", + "Create with create_user in the container console": "Používateľa vytvorte príkazom create_user v konzole kontajnera", + "Created during first-run setup": "Vytvorí sa počas prvotného nastavenia", + "Ech0": "Ech0", + "Email address for the OpenHabitTracker account": "E-mailová adresa účtu OpenHabitTracker", + "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Zadajte základnú adresu URL, ktorú klienti a poskytovatelia webhookov použijú na prístup k tejto inštancii Activepieces.", + "First Ech0 account (becomes Owner)": "Prvý účet Ech0 (stane sa vlastníkom)", + "First Watcharr account (becomes administrator)": "Prvý účet Watcharr (stane sa správcom)", + "Kopia Web UI": "Webové rozhranie Kopia", + "Kopia Web UI login": "Prihlásenie do webového rozhrania Kopia", + "Manage database changes, access and workflows.": "Spravujte zmeny v databáze, prístupy a pracovné postupy.", + "OpenHabitTracker": "OpenHabitTracker", + "OpenHabitTracker account": "Účet OpenHabitTracker", + "OpenHabitTracker initial username": "Počiatočné používateľské meno OpenHabitTracker", + "REST API Basic Authentication user": "Používateľ základného overenia REST API", + "Repository encryption password": "Heslo na šifrovanie úložiska", + "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Nastavte na 1 iba v súkromnej LAN používajúcej výhradne HTTP; inak použite HTTPS.", + "Track movies, television, anime and games you want to enjoy.": "Sledujte filmy, seriály, anime a hry, ktoré si chcete pozrieť alebo zahrať.", + "Watcharr": "Watcharr" } diff --git a/lang/sv.json b/lang/sv.json index 23f87aa8..b481b51e 100644 --- a/lang/sv.json +++ b/lang/sv.json @@ -8869,5 +8869,36 @@ "⚠ Disk data will NOT be erased.": "⚠ Diskdata kommer INTE att raderas.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disken kommer att avmonteras och tas bort från /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ /etc/fstab-posten kommer att tas bort.", - "⚠ The disk will be unmounted.": "⚠ Disken kommer att avmonteras." + "⚠ The disk will be unmounted.": "⚠ Disken kommer att avmonteras.", + "A REST backend for restic backups; it has no web interface.": "En REST-backend för restic-säkerhetskopior utan webbgränssnitt.", + "A lightweight self-hosted URL shortener with SQLite storage.": "En lättviktig, egenhostad URL-förkortare med SQLite-lagring.", + "A personal, self-hosted microblog and timeline.": "En personlig, egenhostad mikroblogg och tidslinje.", + "A self-hosted app for organizing tabletop role-playing campaigns.": "En egenhostad app för att organisera kampanjer för bordsrollspel.", + "A self-hosted tracker for habits, tasks, notes and goals.": "En egenhostad spårare för vanor, uppgifter, anteckningar och mål.", + "Activepieces": "Activepieces", + "Build reports and dashboards from your data.": "Skapa rapporter och instrumentpaneler från dina data.", + "Build visual automations for personal and hobby use.": "Skapa visuella automatiseringar för personligt bruk och hobbyprojekt.", + "Campfire first-run setup": "Första konfigurationen av Campfire", + "Chhoto URL": "Chhoto URL", + "Chhoto URL administrator password": "Administratörslösenord för Chhoto URL", + "Configured username": "Konfigurerat användarnamn", + "Create encrypted, deduplicated backups and restore files.": "Skapa krypterade, deduplicerade säkerhetskopior och återställ filer.", + "Create with create_user in the container console": "Skapa användaren med create_user i containerns konsol", + "Created during first-run setup": "Skapas under den första konfigurationen", + "Ech0": "Ech0", + "Email address for the OpenHabitTracker account": "E-postadress för OpenHabitTracker-kontot", + "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Ange bas-URL:en som klienter och webhook-leverantörer ska använda för att nå den här Activepieces-instansen.", + "First Ech0 account (becomes Owner)": "Första Ech0-kontot (blir ägare)", + "First Watcharr account (becomes administrator)": "Första Watcharr-kontot (blir administratör)", + "Kopia Web UI": "Kopia-webbgränssnitt", + "Kopia Web UI login": "Inloggning till Kopias webbgränssnitt", + "Manage database changes, access and workflows.": "Hantera databasändringar, åtkomst och arbetsflöden.", + "OpenHabitTracker": "OpenHabitTracker", + "OpenHabitTracker account": "OpenHabitTracker-konto", + "OpenHabitTracker initial username": "Första användarnamnet för OpenHabitTracker", + "REST API Basic Authentication user": "Användare för REST API:s grundläggande autentisering", + "Repository encryption password": "Lösenord för kryptering av arkivet", + "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Ange 1 endast för ett privat nätverk som enbart använder HTTP; använd annars HTTPS.", + "Track movies, television, anime and games you want to enjoy.": "Håll koll på filmer, tv-serier, anime och spel som du vill se eller spela.", + "Watcharr": "Watcharr" } diff --git a/oci/VALIDATION.md b/oci/VALIDATION.md index 69bfdee6..0466f97d 100644 --- a/oci/VALIDATION.md +++ b/oci/VALIDATION.md @@ -2,7 +2,7 @@ -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. +43 of 525 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. diff --git a/oci/catalog/apps/activepieces.json b/oci/catalog/apps/activepieces.json new file mode 100644 index 00000000..a1c694b2 --- /dev/null +++ b/oci/catalog/apps/activepieces.json @@ -0,0 +1,524 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-activepieces", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Activepieces", + "es_ES": "Activepieces" + }, + "tagline": { + "en_US": "Build visual automations for personal and hobby use.", + "es_ES": "Crea automatizaciones visuales para uso personal." + }, + "description": { + "en_US": "Build and run automation flows from a self-hosted visual interface. This single-container profile uses Activepieces’ documented personal mode with embedded storage and an in-memory queue.", + "es_ES": "Diseña y ejecuta flujos de automatización desde una interfaz visual autoalojada. Este perfil de un solo contenedor usa el modo personal documentado de Activepieces, con almacenamiento integrado y cola en memoria." + }, + "category": "automation", + "category_label": "Automation & Scheduling", + "author": "Activepieces", + "developer": "Activepieces", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/activepieces.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 80, + "path": "/" + }, + "website": "https://www.activepieces.com/", + "documentation": "https://www.activepieces.com/docs/install/options/docker", + "repository": "https://github.com/activepieces/activepieces", + "tips": [ + "The first account you create becomes the administrator; Activepieces has no default username or password.", + "Use the base URL entered during setup. Webhooks and app triggers need it to be reachable by third parties.", + "This single-container mode uses embedded PGLite and an in-memory queue for personal use on one machine. It is not the production or multi-instance setup." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "activepieces", + "repository": "https://github.com/activepieces/activepieces", + "revision": "bc26934b0a08842ec4fd24d83ecd08907cbaec1e4d7a985d30138e4f67ea6855", + "image_repository_url": "https://github.com/activepieces/activepieces/pkgs/container/activepieces", + "readme_pushed_at": "", + "compose_sha256": "bc26934b0a08842ec4fd24d83ecd08907cbaec1e4d7a985d30138e4f67ea6855", + "generated_at": "2026-10-26T10:22:17+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/activepieces/activepieces/main/README.md" + }, + "container_contract": { + "service_name": "activepieces", + "container_name": "activepieces", + "image": { + "reference": "ghcr.io/activepieces/activepieces:latest", + "registry": "ghcr.io", + "repository": "ghcr.io/activepieces/activepieces", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "AP_DB_TYPE", + "example": "PGLITE", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "AP_REDIS_TYPE", + "example": "MEMORY", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "AP_FRONTEND_URL", + "example": "", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt_user": true, + "prompt": "Enter the base URL clients and webhook providers will use to reach this Activepieces instance." + }, + { + "name": "AP_ENCRYPTION_KEY", + "example": "${GENERATED_AP_ENCRYPTION_KEY}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "AP_JWT_SECRET", + "example": "${GENERATED_AP_JWT_SECRET}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/root/.activepieces", + "compose_source_example": "./activepieces-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 80, + "published_example": 80, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [], + "restart": "unless-stopped", + "stop_grace_period": null, + "original_compose": "name: activepieces\nservices:\n activepieces:\n environment:\n AP_DB_TYPE: PGLITE\n AP_ENCRYPTION_KEY: ${GENERATED_GENERATED_AP_ENCRYPTION_KEY}\n AP_FRONTEND_URL: ''\n AP_JWT_SECRET: ${GENERATED_GENERATED_AP_JWT_SECRET}\n AP_REDIS_TYPE: MEMORY\n image: ghcr.io/activepieces/activepieces:latest\n ports:\n - 80:80\n volumes:\n - ./.activepieces:/root/.activepieces\n" + }, + "compose_stack": { + "project_name": "activepieces", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "activepieces", + "service_count": 1, + "services": [ + { + "name": "activepieces", + "image": "ghcr.io/activepieces/activepieces:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "environment": { + "AP_DB_TYPE": "PGLITE", + "AP_ENCRYPTION_KEY": "${GENERATED_GENERATED_AP_ENCRYPTION_KEY}", + "AP_FRONTEND_URL": "", + "AP_JWT_SECRET": "${GENERATED_GENERATED_AP_JWT_SECRET}", + "AP_REDIS_TYPE": "MEMORY" + }, + "image": "ghcr.io/activepieces/activepieces:latest", + "ports": [ + "80:80" + ], + "volumes": [ + "./.activepieces:/root/.activepieces" + ] + } + } + ], + "top_level": { + "name": "activepieces" + }, + "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": "activepieces-volume-0", + "service": "activepieces", + "container_path": "/root/.activepieces", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "activepieces" + ], + "stop_order": [ + "activepieces" + ], + "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": "generated-ap-encryption-key", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "activepieces", + "environment_variable": "AP_ENCRYPTION_KEY" + } + ] + }, + { + "id": "generated-ap-jwt-secret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "activepieces", + "environment_variable": "AP_JWT_SECRET" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Activepieces", + "scheme": "http", + "port": 80, + "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": "/root/.activepieces", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "only_when_mount_type": "managed-volume" + } + ], + "generated_sensitive_environment": { + "AP_ENCRYPTION_KEY": { + "strategy": "token-hex", + "bytes": 16 + }, + "AP_JWT_SECRET": { + "strategy": "token-hex", + "bytes": 32 + } + }, + "minimum_secret_length": { + "AP_ENCRYPTION_KEY": 32, + "AP_JWT_SECRET": 32 + }, + "volume_owner": { + "uid": 0, + "gid": 0 + }, + "startup_healthcheck": { + "scheme": "http", + "port": 80, + "path": "/api/v1/health", + "timeout_seconds": 300, + "request_timeout_seconds": 10, + "stability_seconds": 5, + "verify_tls": false + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/apps/bytebase.json b/oci/catalog/apps/bytebase.json new file mode 100644 index 00000000..7a9ee724 --- /dev/null +++ b/oci/catalog/apps/bytebase.json @@ -0,0 +1,445 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-bytebase", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Bytebase", + "es_ES": "Bytebase" + }, + "tagline": { + "en_US": "Manage database changes, access and workflows.", + "es_ES": "Administra cambios, accesos y flujos de trabajo de bases de datos." + }, + "description": { + "en_US": "Manage database changes, access and workflows through a browser-based workspace. The image contains its own database, so this profile does not require a second service.", + "es_ES": "Gestiona cambios, accesos y flujos de bases de datos desde un espacio de trabajo web. La imagen incluye su propia base de datos, por lo que este perfil no necesita otro servicio." + }, + "category": "databases", + "category_label": "Databases", + "author": "Bytebase", + "developer": "Bytebase", + "icon": "https://cdn.jsdelivr.net/gh/bytebase/bytebase@main/frontend/src/assets/logo-icon.svg", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/" + }, + "website": "https://www.bytebase.com/", + "documentation": "https://docs.bytebase.com/get-started/self-host/deploy-with-docker", + "repository": "https://github.com/bytebase/bytebase", + "tips": [ + "On first visit, complete the web setup wizard to create the initial administrator and workspace; the image has no default login credentials.", + "To use Bytebase for database changes, connect the database instances you want to manage from its web interface.", + "En la primera visita, completa el asistente web para crear el administrador y el espacio de trabajo inicial; la imagen no tiene credenciales de acceso predeterminadas.", + "Para gestionar cambios de bases de datos, conecta desde la interfaz las instancias que quieras administrar." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "Bytebase", + "repository": "https://github.com/bytebase/bytebase", + "revision": "e77473a25aa12a669dc15bc33d37c276d7478b47", + "image_repository_url": "https://hub.docker.com/r/bytebase/bytebase", + "readme_pushed_at": "", + "compose_sha256": "b3756c0ae20c2a82b17f7cd04f116c4aef1b4a49d6ccf651bd865d74ab865e22", + "generated_at": "2026-10-26T09:44:16+00:00", + "readme_raw_url": "https://github.com/bytebase/bytebase", + "default_branch": "main" + }, + "container_contract": { + "service_name": "bytebase", + "container_name": "bytebase", + "image": { + "reference": "bytebase/bytebase:latest", + "registry": "docker.io", + "repository": "bytebase/bytebase", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [], + "volumes": [ + { + "id": "volume-0", + "container_path": "/var/opt/bytebase", + "compose_source_example": "bytebase-data-0", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 16 + } + } + ], + "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\": \"bytebase\",\n \"services\": {\n \"bytebase\": {\n \"image\": \"bytebase/bytebase:latest\",\n \"restart\": \"unless-stopped\",\n \"command\": [\n \"--port\",\n \"8080\",\n \"--data\",\n \"/var/opt/bytebase\"\n ],\n \"ports\": [\n \"8080:8080/tcp\"\n ],\n \"volumes\": [\n \"bytebase-data-0:/var/opt/bytebase\"\n ]\n }\n },\n \"volumes\": {\n \"bytebase-data-0\": {}\n }\n}" + }, + "compose_stack": { + "project_name": "bytebase", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "bytebase", + "service_count": 1, + "services": [ + { + "name": "bytebase", + "image": "bytebase/bytebase:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "bytebase/bytebase:latest", + "restart": "unless-stopped", + "command": [ + "--port", + "8080", + "--data", + "/var/opt/bytebase" + ], + "ports": [ + "8080:8080/tcp" + ], + "volumes": [ + "bytebase-data-0:/var/opt/bytebase" + ] + } + } + ], + "top_level": { + "name": "bytebase", + "volumes": { + "bytebase-data-0": {} + } + }, + "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": "bytebase-volume-0", + "service": "bytebase", + "container_path": "/var/opt/bytebase", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "bytebase" + ], + "stop_order": [ + "bytebase" + ], + "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": [] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Web UI", + "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": 60 + }, + "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": { + "startup_healthcheck": { + "scheme": "http", + "port": 8080, + "path": "/healthz", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false + }, + "generated_sensitive_environment": {} + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "validation": { + "schema": "pending", + "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 + } +} diff --git a/oci/catalog/apps/campfire.json b/oci/catalog/apps/campfire.json new file mode 100644 index 00000000..6a93cb3f --- /dev/null +++ b/oci/catalog/apps/campfire.json @@ -0,0 +1,457 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-campfire", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Campfire", + "es_ES": "Campfire" + }, + "tagline": { + "en_US": "A self-hosted app for organizing tabletop role-playing campaigns.", + "es_ES": "Aplicación autoalojada para organizar partidas de rol de mesa." + }, + "description": { + "en_US": "A self-hosted web application for organizing tabletop role-playing games.", + "es_ES": "Una aplicación web autoalojada para organizar campañas y sesiones de juegos de rol." + }, + "category": "gaming", + "category_label": "Gaming & Leisure", + "author": "AwsomeFox", + "developer": "AwsomeFox", + "icon": "https://raw.githubusercontent.com/AwsomeFox/campfire/main/apps/web/public/pwa-512x512.png", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/auth/setup" + }, + "website": "https://github.com/AwsomeFox/campfire", + "documentation": "https://github.com/AwsomeFox/campfire#readme", + "repository": "https://github.com/AwsomeFox/campfire", + "tips": [ + "Create the first account at /auth/setup after startup.", + "For direct access over plain HTTP on a trusted LAN, set ALLOW_INSECURE_HTTP=1 in advanced settings. Leave it unset when using HTTPS." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "AwsomeFox", + "repository": "https://github.com/AwsomeFox/campfire", + "revision": "f0dd871f5d2ab0b949db8c8a15a90ec56d40d3dc", + "image_repository_url": "https://github.com/AwsomeFox/campfire/pkgs/container/campfire", + "readme_pushed_at": "", + "compose_sha256": "0cb682cac1fd76326b551b057b28256280b7d7a982f6f418526b39bf75f5da45", + "generated_at": "2026-10-26T06:55:28+00:00", + "readme_raw_url": "https://github.com/AwsomeFox/campfire#readme" + }, + "container_contract": { + "service_name": "campfire", + "container_name": "campfire", + "image": { + "reference": "ghcr.io/awsomefox/campfire:latest", + "registry": "ghcr.io", + "repository": "awsomefox/campfire", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "ALLOW_INSECURE_HTTP", + "example": null, + "required": false, + "sensitive": false, + "source": "upstream-documentation", + "prompt": "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise." + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "./data-0", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "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\": \"campfire\",\n \"services\": {\n \"campfire\": {\n \"image\": \"ghcr.io/awsomefox/campfire:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080/tcp\"\n ],\n \"volumes\": [\n \"./data-0:/data\"\n ]\n }\n }\n}" + }, + "compose_stack": { + "project_name": "campfire", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "campfire", + "service_count": 1, + "services": [ + { + "name": "campfire", + "image": "ghcr.io/awsomefox/campfire:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/awsomefox/campfire:latest", + "restart": "unless-stopped", + "ports": [ + "8080:8080/tcp" + ], + "volumes": [ + "./data-0:/data" + ] + } + } + ], + "top_level": { + "name": "campfire" + }, + "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": "campfire-volume-0", + "service": "campfire", + "container_path": "/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "campfire" + ], + "stop_order": [ + "campfire" + ], + "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": [] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Campfire first-run setup", + "scheme": "http", + "port": 8080, + "path": "/auth/setup", + "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": 1024, + "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": 120 + }, + "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": { + "startup_healthcheck": { + "scheme": "http", + "port": 8080, + "path": "/readyz", + "timeout_seconds": 240, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": false + }, + "volume_owner": { + "uid": 1000, + "gid": 1000 + }, + "volume_preparations": [ + { + "container_path": "/data", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "mode": "2775", + "only_when_mount_type": "managed-volume" + } + ] + }, + "catalog": { + "replaces_discovered_ids": [] + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/apps/chhoto-url.json b/oci/catalog/apps/chhoto-url.json new file mode 100644 index 00000000..26f8caec --- /dev/null +++ b/oci/catalog/apps/chhoto-url.json @@ -0,0 +1,500 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-chhoto-url", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Chhoto URL", + "es_ES": "Chhoto URL" + }, + "tagline": { + "en_US": "A lightweight self-hosted URL shortener with SQLite storage.", + "es_ES": "Un acortador de enlaces ligero y autoalojado con almacenamiento SQLite." + }, + "description": { + "en_US": "Create and manage short links from a small web interface. Chhoto URL stores its link database in SQLite and supports password-protected administration without a separate database service.", + "es_ES": "Crea y administra enlaces cortos desde una interfaz web sencilla. Chhoto URL guarda los enlaces en SQLite y protege la administración con contraseña, sin necesitar otra base de datos." + }, + "category": "tools", + "category_label": "Tools", + "author": "sintan1729", + "developer": "sintan1729", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/chhoto-url.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 4567, + "path": "/" + }, + "website": "https://github.com/SinTan1729/chhoto-url", + "documentation": "https://github.com/SinTan1729/chhoto-url/blob/main/docs/INSTALLATION.md", + "repository": "https://github.com/SinTan1729/chhoto-url", + "tips": [ + "The installer generates the required administrator password. Use HTTPS through a reverse proxy when sending the password over a network; upstream notes that it is not encrypted in plain HTTP." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "sintan1729", + "repository": "https://github.com/SinTan1729/chhoto-url", + "revision": "611ac292c2dc998fac78a3f726afc3c61a344991", + "image_repository_url": "https://hub.docker.com/r/sintan1729/chhoto-url", + "readme_pushed_at": "", + "default_branch": "main", + "compose_sha256": "ccfc1092edaa68277222f013a8c42a4f33c01f0e70bfffb78188c33643f28a87", + "generated_at": "2026-10-26T08:45:03+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/SinTan1729/chhoto-url/main/README.md" + }, + "container_contract": { + "service_name": "chhoto-url", + "container_name": "chhoto-url", + "image": { + "reference": "sintan1729/chhoto-url:latest", + "registry": "docker.io", + "repository": "sintan1729/chhoto-url", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "CHHOTO_PASSWORD", + "example": "${GENERATED_CHHOTO_PASSWORD}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "CHHOTO_DB_URL", + "example": "/data/urls.sqlite", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt_user": false + }, + { + "name": "CHHOTO_SQLITE_USE_WAL_MODE", + "example": "True", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt_user": false + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "data", + "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": 4567, + "published_example": 4567, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [], + "restart": "unless-stopped", + "stop_grace_period": null, + "original_compose": "services:\n chhoto-url:\n image: sintan1729/chhoto-url:latest\n container_name: chhoto-url\n restart: unless-stopped\n ports:\n - 4567:4567/tcp\n volumes:\n - ./data:/data\n environment:\n CHHOTO_PASSWORD: ${GENERATED_CHHOTO_PASSWORD}\n CHHOTO_DB_URL: /data/urls.sqlite\n CHHOTO_SQLITE_USE_WAL_MODE: 'True'\n" + }, + "compose_stack": { + "project_name": "chhoto-url", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "chhoto-url", + "service_count": 1, + "services": [ + { + "name": "chhoto-url", + "image": "sintan1729/chhoto-url:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "environment": { + "CHHOTO_PASSWORD": "${GENERATED_CHHOTO_PASSWORD}", + "CHHOTO_DB_URL": "/data/urls.sqlite", + "CHHOTO_SQLITE_USE_WAL_MODE": "True" + }, + "image": "sintan1729/chhoto-url:latest", + "ports": [ + "4567:4567/tcp" + ], + "volumes": [ + "./data:/data" + ], + "container_name": "chhoto-url", + "restart": "unless-stopped" + } + } + ], + "top_level": { + "name": "chhoto-url" + }, + "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": "chhoto-url-volume-0", + "service": "chhoto-url", + "container_path": "/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "chhoto-url" + ], + "stop_order": [ + "chhoto-url" + ], + "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": "generated-chhoto-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "chhoto-url", + "environment_variable": "CHHOTO_PASSWORD" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Chhoto URL", + "scheme": "http", + "port": 4567, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "Chhoto URL administrator password", + "type": "configured-or-installer-generated", + "username": "Not required (password only)", + "password": null, + "password_environment": "CHHOTO_PASSWORD", + "change_required": false, + "source": "proxmenux-installer-generated", + "retrieval": null + } + ] + }, + "proxmox": { + "runtime": "native-oci-lxc", + "technology_status": "proxmox-technology-preview", + "defaults": { + "unprivileged": true, + "ostype": "auto-from-image", + "cores": 2, + "memory_mb": 512, + "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": "/data", + "remove_lost_found": true, + "owner_strategy": "mapped-root", + "only_when_mount_type": "managed-volume" + } + ], + "generated_sensitive_environment": { + "CHHOTO_PASSWORD": { + "strategy": "token-hex", + "bytes": 24, + "prompt": false + } + }, + "startup_healthcheck": { + "scheme": "http", + "port": 4567, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/apps/ech0.json b/oci/catalog/apps/ech0.json new file mode 100644 index 00000000..c20f649e --- /dev/null +++ b/oci/catalog/apps/ech0.json @@ -0,0 +1,482 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-ech0", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Ech0", + "es_ES": "Ech0" + }, + "tagline": { + "en_US": "A personal, self-hosted microblog and timeline.", + "es_ES": "Un microblog y una cronología personal que alojas en tu propio servidor." + }, + "description": { + "en_US": "Publish short posts, links and media on a personal timeline that you control. Ech0 includes accounts, comments, RSS and local data storage in one lightweight service.", + "es_ES": "Publica textos breves, enlaces y contenido multimedia en una cronología propia. Ech0 incluye cuentas, comentarios, RSS y almacenamiento local en un único servicio." + }, + "category": "communication", + "category_label": "Communication & Community", + "author": "lin-snow", + "developer": "lin-snow", + "icon": "https://raw.githubusercontent.com/lin-snow/Ech0/main/docs/imgs/logo.svg", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 6277, + "path": "/" + }, + "website": "https://www.ech0.app/", + "documentation": "https://github.com/lin-snow/Ech0/blob/main/DEPLOYMENT.md", + "repository": "https://github.com/lin-snow/Ech0", + "tips": [ + "The first registered account becomes the Owner. Publishing is restricted to privileged accounts by default.", + "Keep the generated JWT secret and the /app/data volume persistent; back up the data volume and retain the same secret across restarts." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "lin-snow", + "repository": "https://github.com/lin-snow/Ech0", + "revision": "f2da207f4ab7bd89053898dd1e18ffb4ded1204d", + "image_repository_url": "https://hub.docker.com/r/sn0wl1n/ech0", + "readme_pushed_at": "", + "default_branch": "main", + "compose_sha256": "3ccb02dfdb7cd5198b2181a1a2f78e0b3a7e205efaf6920c1ca1257a4416b0bd", + "generated_at": "2026-10-26T09:32:51+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/lin-snow/Ech0/main/README.md" + }, + "container_contract": { + "service_name": "ech0", + "container_name": "ech0", + "image": { + "reference": "sn0wl1n/ech0:latest", + "registry": "docker.io", + "repository": "sn0wl1n/ech0", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "JWT_SECRET", + "example": "${GENERATED_JWT_SECRET}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/data", + "compose_source_example": "data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 6277, + "published_example": 6277, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [], + "restart": "unless-stopped", + "stop_grace_period": null, + "original_compose": "services:\n ech0:\n image: sn0wl1n/ech0:latest\n container_name: ech0\n restart: unless-stopped\n ports:\n - 6277:6277/tcp\n volumes:\n - ./data:/app/data\n environment:\n JWT_SECRET: ${GENERATED_JWT_SECRET}\n" + }, + "compose_stack": { + "project_name": "ech0", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "ech0", + "service_count": 1, + "services": [ + { + "name": "ech0", + "image": "sn0wl1n/ech0:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "sn0wl1n/ech0:latest", + "ports": [ + "6277:6277/tcp" + ], + "volumes": [ + "./data:/app/data" + ], + "container_name": "ech0", + "restart": "unless-stopped", + "environment": { + "JWT_SECRET": "${GENERATED_JWT_SECRET}" + } + } + } + ], + "top_level": { + "name": "ech0" + }, + "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": "ech0-volume-0", + "service": "ech0", + "container_path": "/app/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "ech0" + ], + "stop_order": [ + "ech0" + ], + "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": "generated-jwt-secret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "ech0", + "environment_variable": "JWT_SECRET" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Ech0", + "scheme": "http", + "port": 6277, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "First Ech0 account (becomes Owner)", + "type": "configured-or-installer-generated", + "username": "Created during first-run setup", + "password": null, + "change_required": false, + "source": "upstream-documentation", + "retrieval": null + } + ] + }, + "proxmox": { + "runtime": "native-oci-lxc", + "technology_status": "proxmox-technology-preview", + "defaults": { + "unprivileged": true, + "ostype": "auto-from-image", + "cores": 2, + "memory_mb": 512, + "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": "/app/data", + "remove_lost_found": true, + "owner_strategy": "mapped-root", + "only_when_mount_type": "managed-volume" + } + ], + "generated_sensitive_environment": { + "JWT_SECRET": { + "strategy": "token-hex", + "bytes": 32, + "prompt": false + } + }, + "startup_healthcheck": { + "scheme": "http", + "port": 6277, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/apps/kopia.json b/oci/catalog/apps/kopia.json new file mode 100644 index 00000000..89fd5d55 --- /dev/null +++ b/oci/catalog/apps/kopia.json @@ -0,0 +1,540 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-kopia", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Kopia", + "es_ES": "Kopia" + }, + "tagline": { + "en_US": "Create encrypted, deduplicated backups and restore files.", + "es_ES": "Crea copias cifradas y deduplicadas y restaura archivos." + }, + "description": { + "en_US": "Kopia provides encrypted, deduplicated backups with a web interface for repositories, policies, snapshots and restores. This profile supports browsing and restoring files through the UI; mounting snapshots with FUSE is not enabled.", + "es_ES": "Kopia ofrece copias cifradas y deduplicadas, con una interfaz web para repositorios, políticas, snapshots y restauraciones. Este perfil permite explorar y restaurar archivos desde la interfaz; no habilita el montaje de snapshots mediante FUSE." + }, + "category": "backup", + "category_label": "Backup & Recovery", + "author": "kopia", + "developer": "kopia", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kopia.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 51515, + "path": "/" + }, + "website": "https://github.com/kopia/kopia", + "documentation": "https://kopia.io/docs/installation/", + "repository": "https://github.com/kopia/kopia", + "tips": [ + "The installer generates a web UI password and a separate repository encryption password. Keep the Proxmox LXC configuration and /app/config volume in backups; without the repository password, encrypted snapshots cannot be opened.", + "On first visit, create a repository in Kopia UI at /repository, then add /data as a read-only source and choose /restore for restores. Choose a host bind for /data and for /repository if you want to back up a NAS or other host directory.", + "The recipe keeps the LXC unprivileged and does not configure FUSE snapshot mounting. Use Kopia UI restore workflows rather than mounting snapshots into the host.", + "The server uses HTTP on its LXC address. Keep it on a trusted LAN or put it behind an HTTPS reverse proxy before allowing access from untrusted networks.", + "This profile uses the web interface to browse and restore files. It does not enable mounting snapshots with FUSE or shared mount propagation.", + "Este perfil usa la interfaz web para explorar y restaurar archivos. No habilita el montaje de snapshots con FUSE ni la propagación compartida de montajes." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "kopia", + "repository": "https://github.com/kopia/kopia", + "revision": "0835005737025dc3e5203df0138c8265686cb4b3", + "image_repository_url": "https://hub.docker.com/r/kopia/kopia", + "readme_pushed_at": "", + "compose_sha256": "e17828b2fb52ceea822dc412d87549dabe2fb0f404ba16b21272e01d8a78b5c8", + "generated_at": "2026-10-26T06:50:27+00:00", + "readme_raw_url": "https://kopia.io/docs/installation/" + }, + "container_contract": { + "service_name": "kopia", + "container_name": "kopia", + "image": { + "reference": "kopia/kopia:latest", + "registry": "docker.io", + "repository": "kopia/kopia", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "KOPIA_PASSWORD", + "example": "${GENERATED_KOPIA_PASSWORD}", + "required": true, + "sensitive": true, + "source": "official-docker-documentation" + }, + { + "name": "KOPIA_SERVER_USERNAME", + "example": "kopia", + "required": true, + "sensitive": false, + "source": "official-kopia-cli", + "prompt_user": false + }, + { + "name": "KOPIA_SERVER_PASSWORD", + "example": "${GENERATED_KOPIA_SERVER_PASSWORD}", + "required": true, + "sensitive": true, + "source": "official-kopia-cli" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/config", + "compose_source_example": "kopia-config", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + }, + { + "id": "volume-1", + "container_path": "/app/cache", + "compose_source_example": "kopia-cache", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 4 + } + }, + { + "id": "volume-2", + "container_path": "/app/logs", + "compose_source_example": "kopia-logs", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + }, + { + "id": "volume-3", + "container_path": "/data", + "compose_source_example": "./backup-source", + "read_only": true, + "required": true, + "installation_choice": [ + "host-bind" + ], + "default": "host-bind", + "managed_volume": { + "backup": false, + "default_size_gb": 1 + } + }, + { + "id": "volume-4", + "container_path": "/repository", + "compose_source_example": "./kopia-repository", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 16 + } + }, + { + "id": "volume-5", + "container_path": "/restore", + "compose_source_example": "kopia-restore", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 51515, + "published_example": 51515, + "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\": \"kopia\",\n \"services\": {\n \"kopia\": {\n \"image\": \"kopia/kopia:latest\",\n \"container_name\": \"kopia\",\n \"hostname\": \"kopia\",\n \"restart\": \"unless-stopped\",\n \"command\": [\n \"server\",\n \"start\",\n \"--async-repo-connect\",\n \"--address=0.0.0.0:51515\",\n \"--server-username=kopia\"\n ],\n \"environment\": {\n \"KOPIA_PASSWORD\": \"${GENERATED_KOPIA_PASSWORD}\",\n \"KOPIA_SERVER_USERNAME\": \"kopia\",\n \"KOPIA_SERVER_PASSWORD\": \"${GENERATED_KOPIA_SERVER_PASSWORD}\"\n },\n \"ports\": [\n \"51515:51515/tcp\"\n ],\n \"volumes\": [\n \"kopia-config:/app/config\",\n \"kopia-cache:/app/cache\",\n \"kopia-logs:/app/logs\",\n \"./backup-source:/data:ro\",\n \"kopia-repository:/repository\",\n \"kopia-restore:/restore\"\n ]\n }\n }\n}\n" + }, + "compose_stack": { + "project_name": "kopia", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "kopia", + "service_count": 1, + "services": [ + { + "name": "kopia", + "image": "kopia/kopia:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "kopia/kopia:latest", + "container_name": "kopia", + "hostname": "kopia", + "restart": "unless-stopped", + "command": [ + "server", + "start", + "--async-repo-connect", + "--address=0.0.0.0:51515", + "--server-username=kopia", + "--insecure" + ], + "environment": { + "KOPIA_PASSWORD": "${GENERATED_KOPIA_PASSWORD}", + "KOPIA_SERVER_USERNAME": "kopia", + "KOPIA_SERVER_PASSWORD": "${GENERATED_KOPIA_SERVER_PASSWORD}" + }, + "ports": [ + "51515:51515/tcp" + ], + "volumes": [ + "kopia-config:/app/config", + "kopia-cache:/app/cache", + "kopia-logs:/app/logs", + "./backup-source:/data:ro", + "kopia-repository:/repository", + "kopia-restore:/restore" + ] + } + } + ], + "top_level": { + "name": "kopia" + }, + "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": "kopia-volume-0", + "service": "kopia", + "container_path": "/app/config", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "kopia-volume-1", + "service": "kopia", + "container_path": "/app/cache", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "kopia-volume-2", + "service": "kopia", + "container_path": "/app/logs", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "kopia-volume-3", + "service": "kopia", + "container_path": "/data", + "mode": "host-bind", + "user_selectable": false, + "backup": false, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "kopia-volume-4", + "service": "kopia", + "container_path": "/repository", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "kopia-volume-5", + "service": "kopia", + "container_path": "/restore", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "kopia" + ], + "stop_order": [ + "kopia" + ], + "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": "kopia-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "kopia", + "environment_variable": "KOPIA_PASSWORD" + } + ] + }, + { + "id": "kopia-server-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "kopia", + "environment_variable": "KOPIA_SERVER_PASSWORD" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Kopia Web UI", + "scheme": "http", + "port": 51515, + "path": "/", + "source": "official-docker-documentation" + } + ], + "credentials": [ + { + "label": "Kopia Web UI login", + "type": "configured-or-installer-generated", + "username": "kopia", + "password": null, + "password_environment": "KOPIA_SERVER_PASSWORD", + "change_required": false, + "source": "official-kopia-cli", + "retrieval": null + }, + { + "label": "Repository encryption password", + "type": "configured-or-installer-generated", + "username": "Not applicable", + "password": null, + "password_environment": "KOPIA_PASSWORD", + "change_required": false, + "source": "official-docker-documentation", + "retrieval": null + } + ] + }, + "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": 120 + }, + "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": "dedicated-lxc-network", + "upstream_behavior": "The Compose definition publishes service ports on its Docker host.", + "native_lxc_behavior": "ProxMenux keeps the image service ports and exposes them on this LXC address without Docker port NAT.", + "reason": "The application runs in its own Proxmox LXC network namespace.", + "behavioral_impact": "Connect to the LXC address and the service port shown in the install summary.", + "validation": "pending-per-application" + }, + { + "id": "compose-command", + "upstream_behavior": "Compose provides the command that starts the service.", + "native_lxc_behavior": "ProxMenux imports the image entrypoint and applies the reviewed command as the native OCI process.", + "reason": "The image runs directly as a native OCI LXC process.", + "behavioral_impact": "The process starts with the upstream server command.", + "validation": "pending-per-application" + } + ], + "installer_profile": { + "generated_sensitive_environment": { + "KOPIA_PASSWORD": { + "strategy": "token-hex", + "bytes": 32, + "prompt": false + }, + "KOPIA_SERVER_PASSWORD": { + "strategy": "token-hex", + "bytes": 24, + "prompt": true + } + }, + "runtime": { + "command": [ + "server", + "start", + "--async-repo-connect", + "--address=0.0.0.0:51515", + "--server-username=kopia", + "--insecure" + ], + "hostname": "kopia" + }, + "startup_healthcheck": { + "scheme": "http", + "port": 51515, + "path": "/", + "timeout_seconds": 240, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "command", + "container_name", + "environment", + "hostname", + "image", + "ports", + "restart", + "volumes" + ], + "untranslated_blockers": [], + "policy": "The single upstream image runs as a native Proxmox OCI LXC. Only the documented image command, environment, ports and persistent paths are translated." + }, + "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 + } +} diff --git a/oci/catalog/apps/metabase.json b/oci/catalog/apps/metabase.json new file mode 100644 index 00000000..8629d34b --- /dev/null +++ b/oci/catalog/apps/metabase.json @@ -0,0 +1,534 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-metabase", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Metabase", + "es_ES": "Metabase" + }, + "tagline": { + "en_US": "Build reports and dashboards from your data.", + "es_ES": "Crea informes y paneles a partir de tus datos." + }, + "description": { + "en_US": "An open-source business intelligence platform for querying data and building dashboards. This single-container profile uses embedded H2 for a home-lab trial or small instance; PostgreSQL is recommended for production.", + "es_ES": "Plataforma de inteligencia de negocio de código abierto para consultar datos y crear paneles. Este perfil de un solo contenedor usa H2 integrada para pruebas o instalaciones pequeñas; para producción se recomienda PostgreSQL." + }, + "category": "monitoring", + "category_label": "Monitoring & Analytics", + "author": "Metabase", + "developer": "Metabase", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/metabase.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 3000, + "path": "/" + }, + "website": "https://www.metabase.com/", + "documentation": "https://www.metabase.com/docs/latest/installation-and-operation/running-metabase-on-docker", + "repository": "https://github.com/metabase/metabase", + "tips": [ + "This profile uses the built-in H2 application database for a simple single-container home-lab setup. Metabase recommends PostgreSQL for production; its application database stores users, questions, dashboards and settings.", + "On first visit, complete the setup wizard and create the administrator account. Keep the service on a trusted LAN until setup and authentication are complete.", + "The H2 database is stored at /metabase-data/metabase.db. Keep the managed volume persistent and back it up before upgrades or migration.", + "ProxMenux generates MB_ENCRYPTION_SECRET_KEY at installation. Keep that key with your backups; losing it can prevent Metabase from decrypting saved database connection credentials.", + "The image follows the v0.64 LTS release track. The installer gives it 2 GiB RAM and caps the Java heap at 1 GiB, leaving memory for the rest of the JVM.", + "The embedded H2 application database is suitable for a trial or small instance. Metabase recommends PostgreSQL for production; this profile does not add a separate database service.", + "La base de datos H2 integrada sirve para pruebas o instalaciones pequeñas. Metabase recomienda PostgreSQL para producción; este perfil no añade una base de datos aparte." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "metabase", + "repository": "https://github.com/metabase/metabase", + "revision": "9fe55abf5c14d7b4a585be9736affcdd0890be36", + "image_repository_url": "https://hub.docker.com/r/metabase/metabase", + "readme_pushed_at": "", + "compose_sha256": "eb8044f4c7c178e640b24d7cd0e6ce2edf51aafcf4f30ee112f5f1e37b4a2dd3", + "generated_at": "2026-10-26T07:19:57+00:00", + "readme_raw_url": "https://www.metabase.com/docs/latest/installation-and-operation/running-metabase-on-docker" + }, + "container_contract": { + "service_name": "metabase", + "container_name": "metabase", + "image": { + "reference": "metabase/metabase:v0.64-lts", + "registry": "docker.io", + "repository": "metabase/metabase", + "tag": "v0.64-lts", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "MB_DB_TYPE", + "example": "h2", + "required": true, + "sensitive": false, + "source": "docker-compose" + }, + { + "name": "MB_DB_FILE", + "example": "/metabase-data/metabase.db", + "required": true, + "sensitive": false, + "source": "docker-compose" + }, + { + "name": "MB_ENCRYPTION_SECRET_KEY", + "example": "${GENERATED_MB_ENCRYPTION_SECRET_KEY}", + "required": true, + "sensitive": true, + "source": "docker-compose" + }, + { + "name": "MUID", + "example": "2000", + "required": true, + "sensitive": false, + "source": "docker-compose" + }, + { + "name": "MGID", + "example": "2000", + "required": true, + "sensitive": false, + "source": "docker-compose" + }, + { + "name": "JAVA_OPTS", + "example": "-Xmx1g", + "required": true, + "sensitive": false, + "source": "docker-compose" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/metabase-data", + "compose_source_example": "metabase-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 3000, + "published_example": 3000, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [], + "restart": "unless-stopped", + "stop_grace_period": null, + "original_compose": "name: metabase\nservices:\n metabase:\n image: metabase/metabase:v0.64-lts\n restart: unless-stopped\n ports:\n - 3000:3000/tcp\n volumes:\n - metabase-data:/metabase-data\n environment:\n MB_DB_TYPE: h2\n MB_DB_FILE: /metabase-data/metabase.db\n MB_ENCRYPTION_SECRET_KEY: ${GENERATED_MB_ENCRYPTION_SECRET_KEY}\n MUID: '2000'\n MGID: '2000'\n JAVA_OPTS: -Xmx1g\n healthcheck:\n test:\n - CMD-SHELL\n - curl --fail -I http://localhost:3000/api/health || exit 1\n interval: 15s\n timeout: 5s\n retries: 5\n start_period: 180s\nvolumes:\n metabase-data: {}\n" + }, + "compose_stack": { + "project_name": "metabase", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "metabase", + "service_count": 1, + "services": [ + { + "name": "metabase", + "image": "metabase/metabase:v0.64-lts", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "metabase/metabase:v0.64-lts", + "restart": "unless-stopped", + "ports": [ + "3000:3000/tcp" + ], + "volumes": [ + "metabase-data:/metabase-data" + ], + "environment": { + "MB_DB_TYPE": "h2", + "MB_DB_FILE": "/metabase-data/metabase.db", + "MB_ENCRYPTION_SECRET_KEY": "${GENERATED_MB_ENCRYPTION_SECRET_KEY}", + "MUID": "2000", + "MGID": "2000", + "JAVA_OPTS": "-Xmx1g" + }, + "healthcheck": { + "test": [ + "CMD-SHELL", + "curl --fail -I http://localhost:3000/api/health || exit 1" + ], + "interval": "15s", + "timeout": "5s", + "retries": 5, + "start_period": "180s" + } + } + } + ], + "top_level": { + "name": "metabase", + "volumes": { + "metabase-data": {} + } + }, + "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": "metabase-volume-0", + "service": "metabase", + "container_path": "/metabase-data", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "metabase" + ], + "stop_order": [ + "metabase" + ], + "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": "mb-encryption-secret-key", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "metabase", + "environment_variable": "MB_ENCRYPTION_SECRET_KEY" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Web UI", + "scheme": "http", + "port": 3000, + "path": "/", + "source": "compose-metadata" + } + ], + "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" + }, + "installer_profile": { + "startup_healthcheck": { + "type": "http", + "scheme": "http", + "port": 3000, + "path": "/api/health", + "timeout_seconds": 300, + "request_timeout_seconds": 5, + "stability_seconds": 0, + "verify_tls": false, + "required": true, + "source": "compose-healthcheck" + }, + "volume_preparations": [ + { + "container_path": "/metabase-data", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "only_when_mount_type": "managed-volume" + } + ], + "volume_owner": { + "uid": 2000, + "gid": 2000 + }, + "generated_sensitive_environment": { + "MB_ENCRYPTION_SECRET_KEY": { + "strategy": "token-hex", + "bytes": 32, + "prompt": false + } + } + }, + "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": "pending-per-application" + }, + { + "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" + } + ] + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/apps/openhabittracker.json b/oci/catalog/apps/openhabittracker.json new file mode 100644 index 00000000..4963b840 --- /dev/null +++ b/oci/catalog/apps/openhabittracker.json @@ -0,0 +1,533 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-openhabittracker", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "OpenHabitTracker", + "es_ES": "OpenHabitTracker" + }, + "tagline": { + "en_US": "A self-hosted tracker for habits, tasks, notes and goals.", + "es_ES": "Seguimiento autoalojado de hábitos, tareas, notas y objetivos." + }, + "description": { + "en_US": "The official image includes the Blazor Server application for one user. It persists application data and backups in /app/.OpenHabitTracker.", + "es_ES": "La imagen oficial incluye la aplicación Blazor Server para una persona. Guarda los datos y las copias de seguridad en /app/.OpenHabitTracker." + }, + "category": "productivity", + "category_label": "Productivity & Workflows", + "author": "Jinjinov", + "developer": "Jinjinov", + "icon": "https://cdn.jsdelivr.net/gh/Jinjinov/OpenHabitTracker@main/OpenHabitTracker.Web/icons/icon-512.png", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/login" + }, + "website": "https://github.com/Jinjinov/OpenHabitTracker", + "documentation": "https://github.com/Jinjinov/OpenHabitTracker/blob/main/README.md", + "repository": "https://github.com/Jinjinov/OpenHabitTracker", + "tips": [ + "The installer sets the initial username, email and password. Sign in at /login after startup." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "Jinjinov", + "repository": "https://github.com/Jinjinov/OpenHabitTracker", + "revision": "a285bf78c04ecebf3b3acc701f1776d4da326905", + "image_repository_url": "https://hub.docker.com/r/jinjinov/openhabittracker", + "readme_pushed_at": "", + "default_branch": "main", + "compose_sha256": "1405f9aea98cdf7cc414b9c00a13a1b5086cba2de51f3ba6d1d607e48c94a5d8", + "generated_at": "2026-10-26T08:45:03+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/Jinjinov/OpenHabitTracker/main/README.md" + }, + "container_contract": { + "service_name": "openhabittracker", + "container_name": "openhabittracker", + "image": { + "reference": "jinjinov/openhabittracker:latest", + "registry": "docker.io", + "repository": "jinjinov/openhabittracker", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "AppSettings__UserName", + "example": "", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt": "OpenHabitTracker initial username" + }, + { + "name": "AppSettings__Email", + "example": "", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt": "Email address for the OpenHabitTracker account" + }, + { + "name": "AppSettings__Password", + "example": "${GENERATED_APPSETTINGS_PASSWORD}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "AppSettings__JwtSecret", + "example": "${GENERATED_APPSETTINGS_JWT_SECRET}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "TZ", + "example": "Europe/Madrid", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt_user": false + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/.OpenHabitTracker", + "compose_source_example": "data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "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": "services:\n openhabittracker:\n image: jinjinov/openhabittracker:latest\n container_name: openhabittracker\n restart: unless-stopped\n ports:\n - 8080:8080/tcp\n volumes:\n - ./data:/app/.OpenHabitTracker\n environment:\n AppSettings__UserName: ${AppSettings__UserName}\n AppSettings__Email: ${AppSettings__Email}\n AppSettings__Password: ${GENERATED_APPSETTINGS_PASSWORD}\n AppSettings__JwtSecret: ${GENERATED_APPSETTINGS_JWT_SECRET}\n TZ: Europe/Madrid\n" + }, + "compose_stack": { + "project_name": "openhabittracker", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "openhabittracker", + "service_count": 1, + "services": [ + { + "name": "openhabittracker", + "image": "jinjinov/openhabittracker:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "environment": { + "AppSettings__UserName": "${AppSettings__UserName}", + "AppSettings__Email": "${AppSettings__Email}", + "AppSettings__Password": "${GENERATED_APPSETTINGS_PASSWORD}", + "AppSettings__JwtSecret": "${GENERATED_APPSETTINGS_JWT_SECRET}", + "TZ": "Europe/Madrid" + }, + "image": "jinjinov/openhabittracker:latest", + "ports": [ + "8080:8080/tcp" + ], + "volumes": [ + "./data:/app/.OpenHabitTracker" + ], + "container_name": "openhabittracker", + "restart": "unless-stopped" + } + } + ], + "top_level": { + "name": "openhabittracker" + }, + "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": "openhabittracker-volume-0", + "service": "openhabittracker", + "container_path": "/app/.OpenHabitTracker", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "openhabittracker" + ], + "stop_order": [ + "openhabittracker" + ], + "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": "generated-appsettings-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "openhabittracker", + "environment_variable": "AppSettings__Password" + } + ] + }, + { + "id": "generated-appsettings-jwtsecret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "openhabittracker", + "environment_variable": "AppSettings__JwtSecret" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "OpenHabitTracker", + "scheme": "http", + "port": 8080, + "path": "/login", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "OpenHabitTracker account", + "type": "configured-or-installer-generated", + "username": "Configured username", + "username_environment": "AppSettings__UserName", + "password": null, + "password_environment": "AppSettings__Password", + "change_required": false, + "source": "proxmenux-installer-generated", + "retrieval": null + } + ] + }, + "proxmox": { + "runtime": "native-oci-lxc", + "technology_status": "proxmox-technology-preview", + "defaults": { + "unprivileged": true, + "ostype": "auto-from-image", + "cores": 2, + "memory_mb": 512, + "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": "/app/.OpenHabitTracker", + "remove_lost_found": true, + "owner_strategy": "mapped-root", + "only_when_mount_type": "managed-volume" + } + ], + "generated_sensitive_environment": { + "AppSettings__Password": { + "strategy": "token-hex", + "bytes": 24, + "prompt": false + }, + "AppSettings__JwtSecret": { + "strategy": "token-hex", + "bytes": 32, + "prompt": false + } + }, + "startup_healthcheck": { + "scheme": "http", + "port": 8080, + "path": "/login", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/apps/restic-rest-server.json b/oci/catalog/apps/restic-rest-server.json new file mode 100644 index 00000000..4ebc9455 --- /dev/null +++ b/oci/catalog/apps/restic-rest-server.json @@ -0,0 +1,315 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-restic-rest-server", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Restic REST Server", + "es_ES": "Restic REST Server" + }, + "tagline": { + "en_US": "A REST backend for restic backups; it has no web interface.", + "es_ES": "Un backend REST para copias de Restic, sin interfaz web." + }, + "description": { + "en_US": "The official rest-server image provides an authenticated HTTP REST backend for restic clients, with persistent repositories and bcrypt-protected user accounts. It exposes an API, not a browser-based application.", + "es_ES": "La imagen oficial rest-server ofrece un backend HTTP REST autenticado para clientes Restic, con repositorios persistentes y cuentas protegidas con bcrypt. Expone una API, no una aplicación web." + }, + "category": "backup", + "category_label": "Backup & Recovery", + "author": "restic", + "developer": "restic", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/restic.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": null, + "path": "/" + }, + "website": "https://github.com/restic/rest-server", + "documentation": "https://github.com/restic/rest-server", + "repository": "https://github.com/restic/rest-server", + "tips": [ + "Authentication stays enabled. Before connecting any backup client, open the container console and run create_user ; enter the password at the hidden prompt. Do not pass the password as a command-line argument. The image stores the bcrypt hash in /data/.htpasswd.", + "The API has no web interface. Use a restic client with a repository URL that includes the username-specific path, for example rest:http://USER@HOST:8000/USER when private repositories are enabled.", + "The upstream default uses HTTP, which sends Basic Authentication credentials in clear text. Use a trusted LAN or route clients through an HTTPS reverse proxy before remote access.", + "With --private-repos enabled, each user can access only a matching repository subdirectory. Keep /data persistent; it contains both backups and the authentication file.", + "The upstream endpoint uses HTTP. Use an HTTPS reverse proxy for remote clients; the example API URL shown in this entry is HTTP.", + "El endpoint original usa HTTP. Para clientes remotos, coloca un proxy HTTPS delante; la URL de ejemplo de esta ficha usa HTTP." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "restic", + "repository": "https://github.com/restic/rest-server", + "revision": "cfcf3fea23edb404c860e8708e39306717fe3e85", + "image_repository_url": "https://hub.docker.com/r/restic/rest-server", + "readme_pushed_at": "", + "compose_sha256": "f346e67428e1c6e8e265d607401fe5d5289b5c7ad478170abb4984fa8dcb01ad", + "generated_at": "2026-10-26T06:50:27+00:00", + "readme_raw_url": "https://github.com/restic/rest-server" + }, + "container_contract": { + "service_name": "restic-rest-server", + "container_name": "restic-rest-server", + "image": { + "reference": "restic/rest-server:latest", + "registry": "docker.io", + "repository": "restic/rest-server", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "OPTIONS", + "example": "--private-repos", + "required": true, + "sensitive": false, + "source": "official-rest-server-documentation", + "prompt_user": false + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "restic-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 16 + } + } + ], + "ports": [ + { + "container_port": 8000, + "published_example": 8000, + "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\": \"restic-rest-server\",\n \"services\": {\n \"restic-rest-server\": {\n \"image\": \"restic/rest-server:latest\",\n \"container_name\": \"restic-rest-server\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8000:8000/tcp\"\n ],\n \"volumes\": [\n \"restic-data:/data\"\n ],\n \"environment\": {\n \"OPTIONS\": \"--private-repos\"\n }\n }\n },\n \"volumes\": {\n \"restic-data\": {}\n }\n}\n" + }, + "compose_stack": { + "project_name": "restic-rest-server", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "restic-rest-server", + "service_count": 1, + "services": [ + { + "name": "restic-rest-server", + "image": "restic/rest-server:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "restic/rest-server:latest", + "container_name": "restic-rest-server", + "restart": "unless-stopped", + "ports": [ + "8000:8000/tcp" + ], + "volumes": [ + "restic-data:/data" + ], + "environment": { + "OPTIONS": "--private-repos" + } + } + } + ], + "top_level": { + "name": "restic-rest-server", + "volumes": { + "restic-data": {} + } + }, + "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": "restic-rest-server-volume-0", + "service": "restic-rest-server", + "container_path": "/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "restic-rest-server" + ], + "stop_order": [ + "restic-rest-server" + ], + "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": [] + } + }, + "first_run": { + "endpoints": [], + "credentials": [ + { + "label": "REST API Basic Authentication user", + "username": "Create with create_user in the container console", + "password": null, + "change_required": false, + "source": "official-rest-server-documentation", + "retrieval": null + } + ] + }, + "proxmox": { + "runtime": "native-oci-lxc", + "technology_status": "proxmox-technology-preview", + "defaults": { + "unprivileged": true, + "ostype": "auto-from-image", + "cores": 1, + "memory_mb": 512, + "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": 120 + }, + "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": "dedicated-lxc-network", + "upstream_behavior": "The Compose definition publishes service ports on its Docker host.", + "native_lxc_behavior": "ProxMenux keeps the image service ports and exposes them on this LXC address without Docker port NAT.", + "reason": "The application runs in its own Proxmox LXC network namespace.", + "behavioral_impact": "Connect to the LXC address and the service port shown in the install summary.", + "validation": "pending-per-application" + }, + { + "id": "compose-command", + "upstream_behavior": "Compose provides the command that starts the service.", + "native_lxc_behavior": "ProxMenux imports the image entrypoint and applies the reviewed command as the native OCI process.", + "reason": "The image runs directly as a native OCI LXC process.", + "behavioral_impact": "The process starts with the upstream server command.", + "validation": "pending-per-application" + } + ], + "installer_profile": { + "startup_healthcheck": { + "scheme": "http", + "port": 8000, + "path": "/", + "timeout_seconds": 120, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "command", + "container_name", + "environment", + "hostname", + "image", + "ports", + "restart", + "volumes" + ], + "untranslated_blockers": [], + "policy": "The single upstream image runs as a native Proxmox OCI LXC. Only the documented image command, environment, ports and persistent paths are translated." + }, + "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 + } +} diff --git a/oci/catalog/apps/watcharr.json b/oci/catalog/apps/watcharr.json new file mode 100644 index 00000000..a72d08ff --- /dev/null +++ b/oci/catalog/apps/watcharr.json @@ -0,0 +1,453 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-watcharr", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Watcharr", + "es_ES": "Watcharr" + }, + "tagline": { + "en_US": "Track movies, television, anime and games you want to enjoy.", + "es_ES": "Lleva un registro de películas, series, anime y videojuegos." + }, + "description": { + "en_US": "Track movies, television shows, anime and games you want to watch or play. The single container stores its database and cache in /data.", + "es_ES": "Haz seguimiento de películas, series, anime y juegos que quieres ver o jugar. El contenedor guarda la base de datos y la caché en /data." + }, + "category": "media", + "category_label": "Media & Streaming", + "author": "sbondCo", + "developer": "sbondCo", + "icon": "https://raw.githubusercontent.com/sbondCo/Watcharr/dev/doc/static/img/logo-col.png", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 3080, + "path": "/" + }, + "website": "https://watcharr.app/", + "documentation": "https://watcharr.app/docs/installation/docker-compose", + "repository": "https://github.com/sbondCo/Watcharr", + "tips": [ + "The first account created becomes the administrator. Keep access limited to your trusted network until initial setup is complete." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "sbondCo", + "repository": "https://github.com/sbondCo/Watcharr", + "revision": "aa2a765788a4d0f6eb607e7f90acb0d89d62fcd7", + "image_repository_url": "https://github.com/orgs/sbondco/packages/container/package/watcharr", + "readme_pushed_at": "", + "default_branch": "dev", + "compose_sha256": "8c4aa4fc5f19e33730685e75e89a7d6a79253bc3ea7ebd6ea6e27e0981fe5f54", + "generated_at": "2026-10-26T08:45:03+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/sbondCo/Watcharr/dev/README.md" + }, + "container_contract": { + "service_name": "watcharr", + "container_name": "watcharr", + "image": { + "reference": "ghcr.io/sbondco/watcharr:latest", + "registry": "ghcr.io", + "repository": "ghcr.io/sbondco/watcharr", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 3080, + "published_example": 3080, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [], + "restart": "unless-stopped", + "stop_grace_period": null, + "original_compose": "services:\n watcharr:\n image: ghcr.io/sbondco/watcharr:latest\n container_name: watcharr\n restart: unless-stopped\n ports:\n - 3080:3080/tcp\n volumes:\n - ./data:/data\n environment: {}\n" + }, + "compose_stack": { + "project_name": "watcharr", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "watcharr", + "service_count": 1, + "services": [ + { + "name": "watcharr", + "image": "ghcr.io/sbondco/watcharr:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/sbondco/watcharr:latest", + "ports": [ + "3080:3080/tcp" + ], + "volumes": [ + "./data:/data" + ], + "container_name": "watcharr", + "restart": "unless-stopped", + "environment": {} + } + } + ], + "top_level": { + "name": "watcharr" + }, + "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": "watcharr-volume-0", + "service": "watcharr", + "container_path": "/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "watcharr" + ], + "stop_order": [ + "watcharr" + ], + "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": [] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Watcharr", + "scheme": "http", + "port": 3080, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "First Watcharr account (becomes administrator)", + "type": "configured-or-installer-generated", + "username": "Created during first-run setup", + "password": null, + "change_required": false, + "source": "upstream-documentation", + "retrieval": null + } + ] + }, + "proxmox": { + "runtime": "native-oci-lxc", + "technology_status": "proxmox-technology-preview", + "defaults": { + "unprivileged": true, + "ostype": "auto-from-image", + "cores": 2, + "memory_mb": 512, + "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": "/data", + "remove_lost_found": true, + "owner_strategy": "mapped-root", + "only_when_mount_type": "managed-volume" + } + ], + "startup_healthcheck": { + "scheme": "http", + "port": 3080, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/categories.json b/oci/catalog/categories.json index 8c36b757..92915b1e 100644 --- a/oci/catalog/categories.json +++ b/oci/catalog/categories.json @@ -419,6 +419,16 @@ "zipkin": "monitoring", "znc": "communication", "zoraxy": "web", - "zotero": "documents" + "zotero": "documents", + "campfire": "gaming", + "chhoto-url": "tools", + "openhabittracker": "productivity", + "watcharr": "media", + "ech0": "communication", + "activepieces": "automation", + "bytebase": "databases", + "metabase": "monitoring", + "kopia": "backup", + "restic-rest-server": "backup" } } diff --git a/oci/catalog/curated/activepieces.json b/oci/catalog/curated/activepieces.json new file mode 100644 index 00000000..a1c694b2 --- /dev/null +++ b/oci/catalog/curated/activepieces.json @@ -0,0 +1,524 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-activepieces", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Activepieces", + "es_ES": "Activepieces" + }, + "tagline": { + "en_US": "Build visual automations for personal and hobby use.", + "es_ES": "Crea automatizaciones visuales para uso personal." + }, + "description": { + "en_US": "Build and run automation flows from a self-hosted visual interface. This single-container profile uses Activepieces’ documented personal mode with embedded storage and an in-memory queue.", + "es_ES": "Diseña y ejecuta flujos de automatización desde una interfaz visual autoalojada. Este perfil de un solo contenedor usa el modo personal documentado de Activepieces, con almacenamiento integrado y cola en memoria." + }, + "category": "automation", + "category_label": "Automation & Scheduling", + "author": "Activepieces", + "developer": "Activepieces", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/activepieces.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 80, + "path": "/" + }, + "website": "https://www.activepieces.com/", + "documentation": "https://www.activepieces.com/docs/install/options/docker", + "repository": "https://github.com/activepieces/activepieces", + "tips": [ + "The first account you create becomes the administrator; Activepieces has no default username or password.", + "Use the base URL entered during setup. Webhooks and app triggers need it to be reachable by third parties.", + "This single-container mode uses embedded PGLite and an in-memory queue for personal use on one machine. It is not the production or multi-instance setup." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "activepieces", + "repository": "https://github.com/activepieces/activepieces", + "revision": "bc26934b0a08842ec4fd24d83ecd08907cbaec1e4d7a985d30138e4f67ea6855", + "image_repository_url": "https://github.com/activepieces/activepieces/pkgs/container/activepieces", + "readme_pushed_at": "", + "compose_sha256": "bc26934b0a08842ec4fd24d83ecd08907cbaec1e4d7a985d30138e4f67ea6855", + "generated_at": "2026-10-26T10:22:17+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/activepieces/activepieces/main/README.md" + }, + "container_contract": { + "service_name": "activepieces", + "container_name": "activepieces", + "image": { + "reference": "ghcr.io/activepieces/activepieces:latest", + "registry": "ghcr.io", + "repository": "ghcr.io/activepieces/activepieces", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "AP_DB_TYPE", + "example": "PGLITE", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "AP_REDIS_TYPE", + "example": "MEMORY", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "AP_FRONTEND_URL", + "example": "", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt_user": true, + "prompt": "Enter the base URL clients and webhook providers will use to reach this Activepieces instance." + }, + { + "name": "AP_ENCRYPTION_KEY", + "example": "${GENERATED_AP_ENCRYPTION_KEY}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "AP_JWT_SECRET", + "example": "${GENERATED_AP_JWT_SECRET}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/root/.activepieces", + "compose_source_example": "./activepieces-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 80, + "published_example": 80, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [], + "restart": "unless-stopped", + "stop_grace_period": null, + "original_compose": "name: activepieces\nservices:\n activepieces:\n environment:\n AP_DB_TYPE: PGLITE\n AP_ENCRYPTION_KEY: ${GENERATED_GENERATED_AP_ENCRYPTION_KEY}\n AP_FRONTEND_URL: ''\n AP_JWT_SECRET: ${GENERATED_GENERATED_AP_JWT_SECRET}\n AP_REDIS_TYPE: MEMORY\n image: ghcr.io/activepieces/activepieces:latest\n ports:\n - 80:80\n volumes:\n - ./.activepieces:/root/.activepieces\n" + }, + "compose_stack": { + "project_name": "activepieces", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "activepieces", + "service_count": 1, + "services": [ + { + "name": "activepieces", + "image": "ghcr.io/activepieces/activepieces:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "environment": { + "AP_DB_TYPE": "PGLITE", + "AP_ENCRYPTION_KEY": "${GENERATED_GENERATED_AP_ENCRYPTION_KEY}", + "AP_FRONTEND_URL": "", + "AP_JWT_SECRET": "${GENERATED_GENERATED_AP_JWT_SECRET}", + "AP_REDIS_TYPE": "MEMORY" + }, + "image": "ghcr.io/activepieces/activepieces:latest", + "ports": [ + "80:80" + ], + "volumes": [ + "./.activepieces:/root/.activepieces" + ] + } + } + ], + "top_level": { + "name": "activepieces" + }, + "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": "activepieces-volume-0", + "service": "activepieces", + "container_path": "/root/.activepieces", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "activepieces" + ], + "stop_order": [ + "activepieces" + ], + "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": "generated-ap-encryption-key", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "activepieces", + "environment_variable": "AP_ENCRYPTION_KEY" + } + ] + }, + { + "id": "generated-ap-jwt-secret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "activepieces", + "environment_variable": "AP_JWT_SECRET" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Activepieces", + "scheme": "http", + "port": 80, + "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": "/root/.activepieces", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "only_when_mount_type": "managed-volume" + } + ], + "generated_sensitive_environment": { + "AP_ENCRYPTION_KEY": { + "strategy": "token-hex", + "bytes": 16 + }, + "AP_JWT_SECRET": { + "strategy": "token-hex", + "bytes": 32 + } + }, + "minimum_secret_length": { + "AP_ENCRYPTION_KEY": 32, + "AP_JWT_SECRET": 32 + }, + "volume_owner": { + "uid": 0, + "gid": 0 + }, + "startup_healthcheck": { + "scheme": "http", + "port": 80, + "path": "/api/v1/health", + "timeout_seconds": 300, + "request_timeout_seconds": 10, + "stability_seconds": 5, + "verify_tls": false + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/curated/bytebase.json b/oci/catalog/curated/bytebase.json new file mode 100644 index 00000000..7a9ee724 --- /dev/null +++ b/oci/catalog/curated/bytebase.json @@ -0,0 +1,445 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-bytebase", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Bytebase", + "es_ES": "Bytebase" + }, + "tagline": { + "en_US": "Manage database changes, access and workflows.", + "es_ES": "Administra cambios, accesos y flujos de trabajo de bases de datos." + }, + "description": { + "en_US": "Manage database changes, access and workflows through a browser-based workspace. The image contains its own database, so this profile does not require a second service.", + "es_ES": "Gestiona cambios, accesos y flujos de bases de datos desde un espacio de trabajo web. La imagen incluye su propia base de datos, por lo que este perfil no necesita otro servicio." + }, + "category": "databases", + "category_label": "Databases", + "author": "Bytebase", + "developer": "Bytebase", + "icon": "https://cdn.jsdelivr.net/gh/bytebase/bytebase@main/frontend/src/assets/logo-icon.svg", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/" + }, + "website": "https://www.bytebase.com/", + "documentation": "https://docs.bytebase.com/get-started/self-host/deploy-with-docker", + "repository": "https://github.com/bytebase/bytebase", + "tips": [ + "On first visit, complete the web setup wizard to create the initial administrator and workspace; the image has no default login credentials.", + "To use Bytebase for database changes, connect the database instances you want to manage from its web interface.", + "En la primera visita, completa el asistente web para crear el administrador y el espacio de trabajo inicial; la imagen no tiene credenciales de acceso predeterminadas.", + "Para gestionar cambios de bases de datos, conecta desde la interfaz las instancias que quieras administrar." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "Bytebase", + "repository": "https://github.com/bytebase/bytebase", + "revision": "e77473a25aa12a669dc15bc33d37c276d7478b47", + "image_repository_url": "https://hub.docker.com/r/bytebase/bytebase", + "readme_pushed_at": "", + "compose_sha256": "b3756c0ae20c2a82b17f7cd04f116c4aef1b4a49d6ccf651bd865d74ab865e22", + "generated_at": "2026-10-26T09:44:16+00:00", + "readme_raw_url": "https://github.com/bytebase/bytebase", + "default_branch": "main" + }, + "container_contract": { + "service_name": "bytebase", + "container_name": "bytebase", + "image": { + "reference": "bytebase/bytebase:latest", + "registry": "docker.io", + "repository": "bytebase/bytebase", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [], + "volumes": [ + { + "id": "volume-0", + "container_path": "/var/opt/bytebase", + "compose_source_example": "bytebase-data-0", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 16 + } + } + ], + "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\": \"bytebase\",\n \"services\": {\n \"bytebase\": {\n \"image\": \"bytebase/bytebase:latest\",\n \"restart\": \"unless-stopped\",\n \"command\": [\n \"--port\",\n \"8080\",\n \"--data\",\n \"/var/opt/bytebase\"\n ],\n \"ports\": [\n \"8080:8080/tcp\"\n ],\n \"volumes\": [\n \"bytebase-data-0:/var/opt/bytebase\"\n ]\n }\n },\n \"volumes\": {\n \"bytebase-data-0\": {}\n }\n}" + }, + "compose_stack": { + "project_name": "bytebase", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "bytebase", + "service_count": 1, + "services": [ + { + "name": "bytebase", + "image": "bytebase/bytebase:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "bytebase/bytebase:latest", + "restart": "unless-stopped", + "command": [ + "--port", + "8080", + "--data", + "/var/opt/bytebase" + ], + "ports": [ + "8080:8080/tcp" + ], + "volumes": [ + "bytebase-data-0:/var/opt/bytebase" + ] + } + } + ], + "top_level": { + "name": "bytebase", + "volumes": { + "bytebase-data-0": {} + } + }, + "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": "bytebase-volume-0", + "service": "bytebase", + "container_path": "/var/opt/bytebase", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "bytebase" + ], + "stop_order": [ + "bytebase" + ], + "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": [] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Web UI", + "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": 60 + }, + "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": { + "startup_healthcheck": { + "scheme": "http", + "port": 8080, + "path": "/healthz", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false + }, + "generated_sensitive_environment": {} + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "validation": { + "schema": "pending", + "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 + } +} diff --git a/oci/catalog/curated/campfire.json b/oci/catalog/curated/campfire.json new file mode 100644 index 00000000..6a93cb3f --- /dev/null +++ b/oci/catalog/curated/campfire.json @@ -0,0 +1,457 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-campfire", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Campfire", + "es_ES": "Campfire" + }, + "tagline": { + "en_US": "A self-hosted app for organizing tabletop role-playing campaigns.", + "es_ES": "Aplicación autoalojada para organizar partidas de rol de mesa." + }, + "description": { + "en_US": "A self-hosted web application for organizing tabletop role-playing games.", + "es_ES": "Una aplicación web autoalojada para organizar campañas y sesiones de juegos de rol." + }, + "category": "gaming", + "category_label": "Gaming & Leisure", + "author": "AwsomeFox", + "developer": "AwsomeFox", + "icon": "https://raw.githubusercontent.com/AwsomeFox/campfire/main/apps/web/public/pwa-512x512.png", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/auth/setup" + }, + "website": "https://github.com/AwsomeFox/campfire", + "documentation": "https://github.com/AwsomeFox/campfire#readme", + "repository": "https://github.com/AwsomeFox/campfire", + "tips": [ + "Create the first account at /auth/setup after startup.", + "For direct access over plain HTTP on a trusted LAN, set ALLOW_INSECURE_HTTP=1 in advanced settings. Leave it unset when using HTTPS." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "AwsomeFox", + "repository": "https://github.com/AwsomeFox/campfire", + "revision": "f0dd871f5d2ab0b949db8c8a15a90ec56d40d3dc", + "image_repository_url": "https://github.com/AwsomeFox/campfire/pkgs/container/campfire", + "readme_pushed_at": "", + "compose_sha256": "0cb682cac1fd76326b551b057b28256280b7d7a982f6f418526b39bf75f5da45", + "generated_at": "2026-10-26T06:55:28+00:00", + "readme_raw_url": "https://github.com/AwsomeFox/campfire#readme" + }, + "container_contract": { + "service_name": "campfire", + "container_name": "campfire", + "image": { + "reference": "ghcr.io/awsomefox/campfire:latest", + "registry": "ghcr.io", + "repository": "awsomefox/campfire", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "ALLOW_INSECURE_HTTP", + "example": null, + "required": false, + "sensitive": false, + "source": "upstream-documentation", + "prompt": "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise." + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "./data-0", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "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\": \"campfire\",\n \"services\": {\n \"campfire\": {\n \"image\": \"ghcr.io/awsomefox/campfire:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080/tcp\"\n ],\n \"volumes\": [\n \"./data-0:/data\"\n ]\n }\n }\n}" + }, + "compose_stack": { + "project_name": "campfire", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "campfire", + "service_count": 1, + "services": [ + { + "name": "campfire", + "image": "ghcr.io/awsomefox/campfire:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/awsomefox/campfire:latest", + "restart": "unless-stopped", + "ports": [ + "8080:8080/tcp" + ], + "volumes": [ + "./data-0:/data" + ] + } + } + ], + "top_level": { + "name": "campfire" + }, + "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": "campfire-volume-0", + "service": "campfire", + "container_path": "/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "campfire" + ], + "stop_order": [ + "campfire" + ], + "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": [] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Campfire first-run setup", + "scheme": "http", + "port": 8080, + "path": "/auth/setup", + "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": 1024, + "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": 120 + }, + "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": { + "startup_healthcheck": { + "scheme": "http", + "port": 8080, + "path": "/readyz", + "timeout_seconds": 240, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": false + }, + "volume_owner": { + "uid": 1000, + "gid": 1000 + }, + "volume_preparations": [ + { + "container_path": "/data", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "mode": "2775", + "only_when_mount_type": "managed-volume" + } + ] + }, + "catalog": { + "replaces_discovered_ids": [] + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/curated/chhoto-url.json b/oci/catalog/curated/chhoto-url.json new file mode 100644 index 00000000..26f8caec --- /dev/null +++ b/oci/catalog/curated/chhoto-url.json @@ -0,0 +1,500 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-chhoto-url", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Chhoto URL", + "es_ES": "Chhoto URL" + }, + "tagline": { + "en_US": "A lightweight self-hosted URL shortener with SQLite storage.", + "es_ES": "Un acortador de enlaces ligero y autoalojado con almacenamiento SQLite." + }, + "description": { + "en_US": "Create and manage short links from a small web interface. Chhoto URL stores its link database in SQLite and supports password-protected administration without a separate database service.", + "es_ES": "Crea y administra enlaces cortos desde una interfaz web sencilla. Chhoto URL guarda los enlaces en SQLite y protege la administración con contraseña, sin necesitar otra base de datos." + }, + "category": "tools", + "category_label": "Tools", + "author": "sintan1729", + "developer": "sintan1729", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/chhoto-url.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 4567, + "path": "/" + }, + "website": "https://github.com/SinTan1729/chhoto-url", + "documentation": "https://github.com/SinTan1729/chhoto-url/blob/main/docs/INSTALLATION.md", + "repository": "https://github.com/SinTan1729/chhoto-url", + "tips": [ + "The installer generates the required administrator password. Use HTTPS through a reverse proxy when sending the password over a network; upstream notes that it is not encrypted in plain HTTP." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "sintan1729", + "repository": "https://github.com/SinTan1729/chhoto-url", + "revision": "611ac292c2dc998fac78a3f726afc3c61a344991", + "image_repository_url": "https://hub.docker.com/r/sintan1729/chhoto-url", + "readme_pushed_at": "", + "default_branch": "main", + "compose_sha256": "ccfc1092edaa68277222f013a8c42a4f33c01f0e70bfffb78188c33643f28a87", + "generated_at": "2026-10-26T08:45:03+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/SinTan1729/chhoto-url/main/README.md" + }, + "container_contract": { + "service_name": "chhoto-url", + "container_name": "chhoto-url", + "image": { + "reference": "sintan1729/chhoto-url:latest", + "registry": "docker.io", + "repository": "sintan1729/chhoto-url", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "CHHOTO_PASSWORD", + "example": "${GENERATED_CHHOTO_PASSWORD}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "CHHOTO_DB_URL", + "example": "/data/urls.sqlite", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt_user": false + }, + { + "name": "CHHOTO_SQLITE_USE_WAL_MODE", + "example": "True", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt_user": false + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "data", + "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": 4567, + "published_example": 4567, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [], + "restart": "unless-stopped", + "stop_grace_period": null, + "original_compose": "services:\n chhoto-url:\n image: sintan1729/chhoto-url:latest\n container_name: chhoto-url\n restart: unless-stopped\n ports:\n - 4567:4567/tcp\n volumes:\n - ./data:/data\n environment:\n CHHOTO_PASSWORD: ${GENERATED_CHHOTO_PASSWORD}\n CHHOTO_DB_URL: /data/urls.sqlite\n CHHOTO_SQLITE_USE_WAL_MODE: 'True'\n" + }, + "compose_stack": { + "project_name": "chhoto-url", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "chhoto-url", + "service_count": 1, + "services": [ + { + "name": "chhoto-url", + "image": "sintan1729/chhoto-url:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "environment": { + "CHHOTO_PASSWORD": "${GENERATED_CHHOTO_PASSWORD}", + "CHHOTO_DB_URL": "/data/urls.sqlite", + "CHHOTO_SQLITE_USE_WAL_MODE": "True" + }, + "image": "sintan1729/chhoto-url:latest", + "ports": [ + "4567:4567/tcp" + ], + "volumes": [ + "./data:/data" + ], + "container_name": "chhoto-url", + "restart": "unless-stopped" + } + } + ], + "top_level": { + "name": "chhoto-url" + }, + "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": "chhoto-url-volume-0", + "service": "chhoto-url", + "container_path": "/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "chhoto-url" + ], + "stop_order": [ + "chhoto-url" + ], + "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": "generated-chhoto-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "chhoto-url", + "environment_variable": "CHHOTO_PASSWORD" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Chhoto URL", + "scheme": "http", + "port": 4567, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "Chhoto URL administrator password", + "type": "configured-or-installer-generated", + "username": "Not required (password only)", + "password": null, + "password_environment": "CHHOTO_PASSWORD", + "change_required": false, + "source": "proxmenux-installer-generated", + "retrieval": null + } + ] + }, + "proxmox": { + "runtime": "native-oci-lxc", + "technology_status": "proxmox-technology-preview", + "defaults": { + "unprivileged": true, + "ostype": "auto-from-image", + "cores": 2, + "memory_mb": 512, + "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": "/data", + "remove_lost_found": true, + "owner_strategy": "mapped-root", + "only_when_mount_type": "managed-volume" + } + ], + "generated_sensitive_environment": { + "CHHOTO_PASSWORD": { + "strategy": "token-hex", + "bytes": 24, + "prompt": false + } + }, + "startup_healthcheck": { + "scheme": "http", + "port": 4567, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/curated/ech0.json b/oci/catalog/curated/ech0.json new file mode 100644 index 00000000..c20f649e --- /dev/null +++ b/oci/catalog/curated/ech0.json @@ -0,0 +1,482 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-ech0", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Ech0", + "es_ES": "Ech0" + }, + "tagline": { + "en_US": "A personal, self-hosted microblog and timeline.", + "es_ES": "Un microblog y una cronología personal que alojas en tu propio servidor." + }, + "description": { + "en_US": "Publish short posts, links and media on a personal timeline that you control. Ech0 includes accounts, comments, RSS and local data storage in one lightweight service.", + "es_ES": "Publica textos breves, enlaces y contenido multimedia en una cronología propia. Ech0 incluye cuentas, comentarios, RSS y almacenamiento local en un único servicio." + }, + "category": "communication", + "category_label": "Communication & Community", + "author": "lin-snow", + "developer": "lin-snow", + "icon": "https://raw.githubusercontent.com/lin-snow/Ech0/main/docs/imgs/logo.svg", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 6277, + "path": "/" + }, + "website": "https://www.ech0.app/", + "documentation": "https://github.com/lin-snow/Ech0/blob/main/DEPLOYMENT.md", + "repository": "https://github.com/lin-snow/Ech0", + "tips": [ + "The first registered account becomes the Owner. Publishing is restricted to privileged accounts by default.", + "Keep the generated JWT secret and the /app/data volume persistent; back up the data volume and retain the same secret across restarts." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "lin-snow", + "repository": "https://github.com/lin-snow/Ech0", + "revision": "f2da207f4ab7bd89053898dd1e18ffb4ded1204d", + "image_repository_url": "https://hub.docker.com/r/sn0wl1n/ech0", + "readme_pushed_at": "", + "default_branch": "main", + "compose_sha256": "3ccb02dfdb7cd5198b2181a1a2f78e0b3a7e205efaf6920c1ca1257a4416b0bd", + "generated_at": "2026-10-26T09:32:51+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/lin-snow/Ech0/main/README.md" + }, + "container_contract": { + "service_name": "ech0", + "container_name": "ech0", + "image": { + "reference": "sn0wl1n/ech0:latest", + "registry": "docker.io", + "repository": "sn0wl1n/ech0", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "JWT_SECRET", + "example": "${GENERATED_JWT_SECRET}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/data", + "compose_source_example": "data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 6277, + "published_example": 6277, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [], + "restart": "unless-stopped", + "stop_grace_period": null, + "original_compose": "services:\n ech0:\n image: sn0wl1n/ech0:latest\n container_name: ech0\n restart: unless-stopped\n ports:\n - 6277:6277/tcp\n volumes:\n - ./data:/app/data\n environment:\n JWT_SECRET: ${GENERATED_JWT_SECRET}\n" + }, + "compose_stack": { + "project_name": "ech0", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "ech0", + "service_count": 1, + "services": [ + { + "name": "ech0", + "image": "sn0wl1n/ech0:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "sn0wl1n/ech0:latest", + "ports": [ + "6277:6277/tcp" + ], + "volumes": [ + "./data:/app/data" + ], + "container_name": "ech0", + "restart": "unless-stopped", + "environment": { + "JWT_SECRET": "${GENERATED_JWT_SECRET}" + } + } + } + ], + "top_level": { + "name": "ech0" + }, + "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": "ech0-volume-0", + "service": "ech0", + "container_path": "/app/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "ech0" + ], + "stop_order": [ + "ech0" + ], + "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": "generated-jwt-secret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "ech0", + "environment_variable": "JWT_SECRET" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Ech0", + "scheme": "http", + "port": 6277, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "First Ech0 account (becomes Owner)", + "type": "configured-or-installer-generated", + "username": "Created during first-run setup", + "password": null, + "change_required": false, + "source": "upstream-documentation", + "retrieval": null + } + ] + }, + "proxmox": { + "runtime": "native-oci-lxc", + "technology_status": "proxmox-technology-preview", + "defaults": { + "unprivileged": true, + "ostype": "auto-from-image", + "cores": 2, + "memory_mb": 512, + "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": "/app/data", + "remove_lost_found": true, + "owner_strategy": "mapped-root", + "only_when_mount_type": "managed-volume" + } + ], + "generated_sensitive_environment": { + "JWT_SECRET": { + "strategy": "token-hex", + "bytes": 32, + "prompt": false + } + }, + "startup_healthcheck": { + "scheme": "http", + "port": 6277, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/curated/kopia.json b/oci/catalog/curated/kopia.json new file mode 100644 index 00000000..89fd5d55 --- /dev/null +++ b/oci/catalog/curated/kopia.json @@ -0,0 +1,540 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-kopia", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Kopia", + "es_ES": "Kopia" + }, + "tagline": { + "en_US": "Create encrypted, deduplicated backups and restore files.", + "es_ES": "Crea copias cifradas y deduplicadas y restaura archivos." + }, + "description": { + "en_US": "Kopia provides encrypted, deduplicated backups with a web interface for repositories, policies, snapshots and restores. This profile supports browsing and restoring files through the UI; mounting snapshots with FUSE is not enabled.", + "es_ES": "Kopia ofrece copias cifradas y deduplicadas, con una interfaz web para repositorios, políticas, snapshots y restauraciones. Este perfil permite explorar y restaurar archivos desde la interfaz; no habilita el montaje de snapshots mediante FUSE." + }, + "category": "backup", + "category_label": "Backup & Recovery", + "author": "kopia", + "developer": "kopia", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kopia.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 51515, + "path": "/" + }, + "website": "https://github.com/kopia/kopia", + "documentation": "https://kopia.io/docs/installation/", + "repository": "https://github.com/kopia/kopia", + "tips": [ + "The installer generates a web UI password and a separate repository encryption password. Keep the Proxmox LXC configuration and /app/config volume in backups; without the repository password, encrypted snapshots cannot be opened.", + "On first visit, create a repository in Kopia UI at /repository, then add /data as a read-only source and choose /restore for restores. Choose a host bind for /data and for /repository if you want to back up a NAS or other host directory.", + "The recipe keeps the LXC unprivileged and does not configure FUSE snapshot mounting. Use Kopia UI restore workflows rather than mounting snapshots into the host.", + "The server uses HTTP on its LXC address. Keep it on a trusted LAN or put it behind an HTTPS reverse proxy before allowing access from untrusted networks.", + "This profile uses the web interface to browse and restore files. It does not enable mounting snapshots with FUSE or shared mount propagation.", + "Este perfil usa la interfaz web para explorar y restaurar archivos. No habilita el montaje de snapshots con FUSE ni la propagación compartida de montajes." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "kopia", + "repository": "https://github.com/kopia/kopia", + "revision": "0835005737025dc3e5203df0138c8265686cb4b3", + "image_repository_url": "https://hub.docker.com/r/kopia/kopia", + "readme_pushed_at": "", + "compose_sha256": "e17828b2fb52ceea822dc412d87549dabe2fb0f404ba16b21272e01d8a78b5c8", + "generated_at": "2026-10-26T06:50:27+00:00", + "readme_raw_url": "https://kopia.io/docs/installation/" + }, + "container_contract": { + "service_name": "kopia", + "container_name": "kopia", + "image": { + "reference": "kopia/kopia:latest", + "registry": "docker.io", + "repository": "kopia/kopia", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "KOPIA_PASSWORD", + "example": "${GENERATED_KOPIA_PASSWORD}", + "required": true, + "sensitive": true, + "source": "official-docker-documentation" + }, + { + "name": "KOPIA_SERVER_USERNAME", + "example": "kopia", + "required": true, + "sensitive": false, + "source": "official-kopia-cli", + "prompt_user": false + }, + { + "name": "KOPIA_SERVER_PASSWORD", + "example": "${GENERATED_KOPIA_SERVER_PASSWORD}", + "required": true, + "sensitive": true, + "source": "official-kopia-cli" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/config", + "compose_source_example": "kopia-config", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + }, + { + "id": "volume-1", + "container_path": "/app/cache", + "compose_source_example": "kopia-cache", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 4 + } + }, + { + "id": "volume-2", + "container_path": "/app/logs", + "compose_source_example": "kopia-logs", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + }, + { + "id": "volume-3", + "container_path": "/data", + "compose_source_example": "./backup-source", + "read_only": true, + "required": true, + "installation_choice": [ + "host-bind" + ], + "default": "host-bind", + "managed_volume": { + "backup": false, + "default_size_gb": 1 + } + }, + { + "id": "volume-4", + "container_path": "/repository", + "compose_source_example": "./kopia-repository", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 16 + } + }, + { + "id": "volume-5", + "container_path": "/restore", + "compose_source_example": "kopia-restore", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 51515, + "published_example": 51515, + "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\": \"kopia\",\n \"services\": {\n \"kopia\": {\n \"image\": \"kopia/kopia:latest\",\n \"container_name\": \"kopia\",\n \"hostname\": \"kopia\",\n \"restart\": \"unless-stopped\",\n \"command\": [\n \"server\",\n \"start\",\n \"--async-repo-connect\",\n \"--address=0.0.0.0:51515\",\n \"--server-username=kopia\"\n ],\n \"environment\": {\n \"KOPIA_PASSWORD\": \"${GENERATED_KOPIA_PASSWORD}\",\n \"KOPIA_SERVER_USERNAME\": \"kopia\",\n \"KOPIA_SERVER_PASSWORD\": \"${GENERATED_KOPIA_SERVER_PASSWORD}\"\n },\n \"ports\": [\n \"51515:51515/tcp\"\n ],\n \"volumes\": [\n \"kopia-config:/app/config\",\n \"kopia-cache:/app/cache\",\n \"kopia-logs:/app/logs\",\n \"./backup-source:/data:ro\",\n \"kopia-repository:/repository\",\n \"kopia-restore:/restore\"\n ]\n }\n }\n}\n" + }, + "compose_stack": { + "project_name": "kopia", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "kopia", + "service_count": 1, + "services": [ + { + "name": "kopia", + "image": "kopia/kopia:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "kopia/kopia:latest", + "container_name": "kopia", + "hostname": "kopia", + "restart": "unless-stopped", + "command": [ + "server", + "start", + "--async-repo-connect", + "--address=0.0.0.0:51515", + "--server-username=kopia", + "--insecure" + ], + "environment": { + "KOPIA_PASSWORD": "${GENERATED_KOPIA_PASSWORD}", + "KOPIA_SERVER_USERNAME": "kopia", + "KOPIA_SERVER_PASSWORD": "${GENERATED_KOPIA_SERVER_PASSWORD}" + }, + "ports": [ + "51515:51515/tcp" + ], + "volumes": [ + "kopia-config:/app/config", + "kopia-cache:/app/cache", + "kopia-logs:/app/logs", + "./backup-source:/data:ro", + "kopia-repository:/repository", + "kopia-restore:/restore" + ] + } + } + ], + "top_level": { + "name": "kopia" + }, + "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": "kopia-volume-0", + "service": "kopia", + "container_path": "/app/config", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "kopia-volume-1", + "service": "kopia", + "container_path": "/app/cache", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "kopia-volume-2", + "service": "kopia", + "container_path": "/app/logs", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "kopia-volume-3", + "service": "kopia", + "container_path": "/data", + "mode": "host-bind", + "user_selectable": false, + "backup": false, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "kopia-volume-4", + "service": "kopia", + "container_path": "/repository", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "kopia-volume-5", + "service": "kopia", + "container_path": "/restore", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "kopia" + ], + "stop_order": [ + "kopia" + ], + "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": "kopia-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "kopia", + "environment_variable": "KOPIA_PASSWORD" + } + ] + }, + { + "id": "kopia-server-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "kopia", + "environment_variable": "KOPIA_SERVER_PASSWORD" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Kopia Web UI", + "scheme": "http", + "port": 51515, + "path": "/", + "source": "official-docker-documentation" + } + ], + "credentials": [ + { + "label": "Kopia Web UI login", + "type": "configured-or-installer-generated", + "username": "kopia", + "password": null, + "password_environment": "KOPIA_SERVER_PASSWORD", + "change_required": false, + "source": "official-kopia-cli", + "retrieval": null + }, + { + "label": "Repository encryption password", + "type": "configured-or-installer-generated", + "username": "Not applicable", + "password": null, + "password_environment": "KOPIA_PASSWORD", + "change_required": false, + "source": "official-docker-documentation", + "retrieval": null + } + ] + }, + "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": 120 + }, + "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": "dedicated-lxc-network", + "upstream_behavior": "The Compose definition publishes service ports on its Docker host.", + "native_lxc_behavior": "ProxMenux keeps the image service ports and exposes them on this LXC address without Docker port NAT.", + "reason": "The application runs in its own Proxmox LXC network namespace.", + "behavioral_impact": "Connect to the LXC address and the service port shown in the install summary.", + "validation": "pending-per-application" + }, + { + "id": "compose-command", + "upstream_behavior": "Compose provides the command that starts the service.", + "native_lxc_behavior": "ProxMenux imports the image entrypoint and applies the reviewed command as the native OCI process.", + "reason": "The image runs directly as a native OCI LXC process.", + "behavioral_impact": "The process starts with the upstream server command.", + "validation": "pending-per-application" + } + ], + "installer_profile": { + "generated_sensitive_environment": { + "KOPIA_PASSWORD": { + "strategy": "token-hex", + "bytes": 32, + "prompt": false + }, + "KOPIA_SERVER_PASSWORD": { + "strategy": "token-hex", + "bytes": 24, + "prompt": true + } + }, + "runtime": { + "command": [ + "server", + "start", + "--async-repo-connect", + "--address=0.0.0.0:51515", + "--server-username=kopia", + "--insecure" + ], + "hostname": "kopia" + }, + "startup_healthcheck": { + "scheme": "http", + "port": 51515, + "path": "/", + "timeout_seconds": 240, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "command", + "container_name", + "environment", + "hostname", + "image", + "ports", + "restart", + "volumes" + ], + "untranslated_blockers": [], + "policy": "The single upstream image runs as a native Proxmox OCI LXC. Only the documented image command, environment, ports and persistent paths are translated." + }, + "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 + } +} diff --git a/oci/catalog/curated/metabase.json b/oci/catalog/curated/metabase.json new file mode 100644 index 00000000..8629d34b --- /dev/null +++ b/oci/catalog/curated/metabase.json @@ -0,0 +1,534 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-metabase", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Metabase", + "es_ES": "Metabase" + }, + "tagline": { + "en_US": "Build reports and dashboards from your data.", + "es_ES": "Crea informes y paneles a partir de tus datos." + }, + "description": { + "en_US": "An open-source business intelligence platform for querying data and building dashboards. This single-container profile uses embedded H2 for a home-lab trial or small instance; PostgreSQL is recommended for production.", + "es_ES": "Plataforma de inteligencia de negocio de código abierto para consultar datos y crear paneles. Este perfil de un solo contenedor usa H2 integrada para pruebas o instalaciones pequeñas; para producción se recomienda PostgreSQL." + }, + "category": "monitoring", + "category_label": "Monitoring & Analytics", + "author": "Metabase", + "developer": "Metabase", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/metabase.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 3000, + "path": "/" + }, + "website": "https://www.metabase.com/", + "documentation": "https://www.metabase.com/docs/latest/installation-and-operation/running-metabase-on-docker", + "repository": "https://github.com/metabase/metabase", + "tips": [ + "This profile uses the built-in H2 application database for a simple single-container home-lab setup. Metabase recommends PostgreSQL for production; its application database stores users, questions, dashboards and settings.", + "On first visit, complete the setup wizard and create the administrator account. Keep the service on a trusted LAN until setup and authentication are complete.", + "The H2 database is stored at /metabase-data/metabase.db. Keep the managed volume persistent and back it up before upgrades or migration.", + "ProxMenux generates MB_ENCRYPTION_SECRET_KEY at installation. Keep that key with your backups; losing it can prevent Metabase from decrypting saved database connection credentials.", + "The image follows the v0.64 LTS release track. The installer gives it 2 GiB RAM and caps the Java heap at 1 GiB, leaving memory for the rest of the JVM.", + "The embedded H2 application database is suitable for a trial or small instance. Metabase recommends PostgreSQL for production; this profile does not add a separate database service.", + "La base de datos H2 integrada sirve para pruebas o instalaciones pequeñas. Metabase recomienda PostgreSQL para producción; este perfil no añade una base de datos aparte." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "metabase", + "repository": "https://github.com/metabase/metabase", + "revision": "9fe55abf5c14d7b4a585be9736affcdd0890be36", + "image_repository_url": "https://hub.docker.com/r/metabase/metabase", + "readme_pushed_at": "", + "compose_sha256": "eb8044f4c7c178e640b24d7cd0e6ce2edf51aafcf4f30ee112f5f1e37b4a2dd3", + "generated_at": "2026-10-26T07:19:57+00:00", + "readme_raw_url": "https://www.metabase.com/docs/latest/installation-and-operation/running-metabase-on-docker" + }, + "container_contract": { + "service_name": "metabase", + "container_name": "metabase", + "image": { + "reference": "metabase/metabase:v0.64-lts", + "registry": "docker.io", + "repository": "metabase/metabase", + "tag": "v0.64-lts", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "MB_DB_TYPE", + "example": "h2", + "required": true, + "sensitive": false, + "source": "docker-compose" + }, + { + "name": "MB_DB_FILE", + "example": "/metabase-data/metabase.db", + "required": true, + "sensitive": false, + "source": "docker-compose" + }, + { + "name": "MB_ENCRYPTION_SECRET_KEY", + "example": "${GENERATED_MB_ENCRYPTION_SECRET_KEY}", + "required": true, + "sensitive": true, + "source": "docker-compose" + }, + { + "name": "MUID", + "example": "2000", + "required": true, + "sensitive": false, + "source": "docker-compose" + }, + { + "name": "MGID", + "example": "2000", + "required": true, + "sensitive": false, + "source": "docker-compose" + }, + { + "name": "JAVA_OPTS", + "example": "-Xmx1g", + "required": true, + "sensitive": false, + "source": "docker-compose" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/metabase-data", + "compose_source_example": "metabase-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 3000, + "published_example": 3000, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [], + "restart": "unless-stopped", + "stop_grace_period": null, + "original_compose": "name: metabase\nservices:\n metabase:\n image: metabase/metabase:v0.64-lts\n restart: unless-stopped\n ports:\n - 3000:3000/tcp\n volumes:\n - metabase-data:/metabase-data\n environment:\n MB_DB_TYPE: h2\n MB_DB_FILE: /metabase-data/metabase.db\n MB_ENCRYPTION_SECRET_KEY: ${GENERATED_MB_ENCRYPTION_SECRET_KEY}\n MUID: '2000'\n MGID: '2000'\n JAVA_OPTS: -Xmx1g\n healthcheck:\n test:\n - CMD-SHELL\n - curl --fail -I http://localhost:3000/api/health || exit 1\n interval: 15s\n timeout: 5s\n retries: 5\n start_period: 180s\nvolumes:\n metabase-data: {}\n" + }, + "compose_stack": { + "project_name": "metabase", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "metabase", + "service_count": 1, + "services": [ + { + "name": "metabase", + "image": "metabase/metabase:v0.64-lts", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "metabase/metabase:v0.64-lts", + "restart": "unless-stopped", + "ports": [ + "3000:3000/tcp" + ], + "volumes": [ + "metabase-data:/metabase-data" + ], + "environment": { + "MB_DB_TYPE": "h2", + "MB_DB_FILE": "/metabase-data/metabase.db", + "MB_ENCRYPTION_SECRET_KEY": "${GENERATED_MB_ENCRYPTION_SECRET_KEY}", + "MUID": "2000", + "MGID": "2000", + "JAVA_OPTS": "-Xmx1g" + }, + "healthcheck": { + "test": [ + "CMD-SHELL", + "curl --fail -I http://localhost:3000/api/health || exit 1" + ], + "interval": "15s", + "timeout": "5s", + "retries": 5, + "start_period": "180s" + } + } + } + ], + "top_level": { + "name": "metabase", + "volumes": { + "metabase-data": {} + } + }, + "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": "metabase-volume-0", + "service": "metabase", + "container_path": "/metabase-data", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "metabase" + ], + "stop_order": [ + "metabase" + ], + "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": "mb-encryption-secret-key", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "metabase", + "environment_variable": "MB_ENCRYPTION_SECRET_KEY" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Web UI", + "scheme": "http", + "port": 3000, + "path": "/", + "source": "compose-metadata" + } + ], + "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" + }, + "installer_profile": { + "startup_healthcheck": { + "type": "http", + "scheme": "http", + "port": 3000, + "path": "/api/health", + "timeout_seconds": 300, + "request_timeout_seconds": 5, + "stability_seconds": 0, + "verify_tls": false, + "required": true, + "source": "compose-healthcheck" + }, + "volume_preparations": [ + { + "container_path": "/metabase-data", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "only_when_mount_type": "managed-volume" + } + ], + "volume_owner": { + "uid": 2000, + "gid": 2000 + }, + "generated_sensitive_environment": { + "MB_ENCRYPTION_SECRET_KEY": { + "strategy": "token-hex", + "bytes": 32, + "prompt": false + } + } + }, + "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": "pending-per-application" + }, + { + "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" + } + ] + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/curated/openhabittracker.json b/oci/catalog/curated/openhabittracker.json new file mode 100644 index 00000000..4963b840 --- /dev/null +++ b/oci/catalog/curated/openhabittracker.json @@ -0,0 +1,533 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-openhabittracker", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "OpenHabitTracker", + "es_ES": "OpenHabitTracker" + }, + "tagline": { + "en_US": "A self-hosted tracker for habits, tasks, notes and goals.", + "es_ES": "Seguimiento autoalojado de hábitos, tareas, notas y objetivos." + }, + "description": { + "en_US": "The official image includes the Blazor Server application for one user. It persists application data and backups in /app/.OpenHabitTracker.", + "es_ES": "La imagen oficial incluye la aplicación Blazor Server para una persona. Guarda los datos y las copias de seguridad en /app/.OpenHabitTracker." + }, + "category": "productivity", + "category_label": "Productivity & Workflows", + "author": "Jinjinov", + "developer": "Jinjinov", + "icon": "https://cdn.jsdelivr.net/gh/Jinjinov/OpenHabitTracker@main/OpenHabitTracker.Web/icons/icon-512.png", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/login" + }, + "website": "https://github.com/Jinjinov/OpenHabitTracker", + "documentation": "https://github.com/Jinjinov/OpenHabitTracker/blob/main/README.md", + "repository": "https://github.com/Jinjinov/OpenHabitTracker", + "tips": [ + "The installer sets the initial username, email and password. Sign in at /login after startup." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "Jinjinov", + "repository": "https://github.com/Jinjinov/OpenHabitTracker", + "revision": "a285bf78c04ecebf3b3acc701f1776d4da326905", + "image_repository_url": "https://hub.docker.com/r/jinjinov/openhabittracker", + "readme_pushed_at": "", + "default_branch": "main", + "compose_sha256": "1405f9aea98cdf7cc414b9c00a13a1b5086cba2de51f3ba6d1d607e48c94a5d8", + "generated_at": "2026-10-26T08:45:03+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/Jinjinov/OpenHabitTracker/main/README.md" + }, + "container_contract": { + "service_name": "openhabittracker", + "container_name": "openhabittracker", + "image": { + "reference": "jinjinov/openhabittracker:latest", + "registry": "docker.io", + "repository": "jinjinov/openhabittracker", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "AppSettings__UserName", + "example": "", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt": "OpenHabitTracker initial username" + }, + { + "name": "AppSettings__Email", + "example": "", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt": "Email address for the OpenHabitTracker account" + }, + { + "name": "AppSettings__Password", + "example": "${GENERATED_APPSETTINGS_PASSWORD}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "AppSettings__JwtSecret", + "example": "${GENERATED_APPSETTINGS_JWT_SECRET}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "TZ", + "example": "Europe/Madrid", + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "prompt_user": false + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/.OpenHabitTracker", + "compose_source_example": "data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "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": "services:\n openhabittracker:\n image: jinjinov/openhabittracker:latest\n container_name: openhabittracker\n restart: unless-stopped\n ports:\n - 8080:8080/tcp\n volumes:\n - ./data:/app/.OpenHabitTracker\n environment:\n AppSettings__UserName: ${AppSettings__UserName}\n AppSettings__Email: ${AppSettings__Email}\n AppSettings__Password: ${GENERATED_APPSETTINGS_PASSWORD}\n AppSettings__JwtSecret: ${GENERATED_APPSETTINGS_JWT_SECRET}\n TZ: Europe/Madrid\n" + }, + "compose_stack": { + "project_name": "openhabittracker", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "openhabittracker", + "service_count": 1, + "services": [ + { + "name": "openhabittracker", + "image": "jinjinov/openhabittracker:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "environment": { + "AppSettings__UserName": "${AppSettings__UserName}", + "AppSettings__Email": "${AppSettings__Email}", + "AppSettings__Password": "${GENERATED_APPSETTINGS_PASSWORD}", + "AppSettings__JwtSecret": "${GENERATED_APPSETTINGS_JWT_SECRET}", + "TZ": "Europe/Madrid" + }, + "image": "jinjinov/openhabittracker:latest", + "ports": [ + "8080:8080/tcp" + ], + "volumes": [ + "./data:/app/.OpenHabitTracker" + ], + "container_name": "openhabittracker", + "restart": "unless-stopped" + } + } + ], + "top_level": { + "name": "openhabittracker" + }, + "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": "openhabittracker-volume-0", + "service": "openhabittracker", + "container_path": "/app/.OpenHabitTracker", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "openhabittracker" + ], + "stop_order": [ + "openhabittracker" + ], + "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": "generated-appsettings-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "openhabittracker", + "environment_variable": "AppSettings__Password" + } + ] + }, + { + "id": "generated-appsettings-jwtsecret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "openhabittracker", + "environment_variable": "AppSettings__JwtSecret" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "OpenHabitTracker", + "scheme": "http", + "port": 8080, + "path": "/login", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "OpenHabitTracker account", + "type": "configured-or-installer-generated", + "username": "Configured username", + "username_environment": "AppSettings__UserName", + "password": null, + "password_environment": "AppSettings__Password", + "change_required": false, + "source": "proxmenux-installer-generated", + "retrieval": null + } + ] + }, + "proxmox": { + "runtime": "native-oci-lxc", + "technology_status": "proxmox-technology-preview", + "defaults": { + "unprivileged": true, + "ostype": "auto-from-image", + "cores": 2, + "memory_mb": 512, + "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": "/app/.OpenHabitTracker", + "remove_lost_found": true, + "owner_strategy": "mapped-root", + "only_when_mount_type": "managed-volume" + } + ], + "generated_sensitive_environment": { + "AppSettings__Password": { + "strategy": "token-hex", + "bytes": 24, + "prompt": false + }, + "AppSettings__JwtSecret": { + "strategy": "token-hex", + "bytes": 32, + "prompt": false + } + }, + "startup_healthcheck": { + "scheme": "http", + "port": 8080, + "path": "/login", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/curated/restic-rest-server.json b/oci/catalog/curated/restic-rest-server.json new file mode 100644 index 00000000..4ebc9455 --- /dev/null +++ b/oci/catalog/curated/restic-rest-server.json @@ -0,0 +1,315 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-restic-rest-server", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Restic REST Server", + "es_ES": "Restic REST Server" + }, + "tagline": { + "en_US": "A REST backend for restic backups; it has no web interface.", + "es_ES": "Un backend REST para copias de Restic, sin interfaz web." + }, + "description": { + "en_US": "The official rest-server image provides an authenticated HTTP REST backend for restic clients, with persistent repositories and bcrypt-protected user accounts. It exposes an API, not a browser-based application.", + "es_ES": "La imagen oficial rest-server ofrece un backend HTTP REST autenticado para clientes Restic, con repositorios persistentes y cuentas protegidas con bcrypt. Expone una API, no una aplicación web." + }, + "category": "backup", + "category_label": "Backup & Recovery", + "author": "restic", + "developer": "restic", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/restic.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": null, + "path": "/" + }, + "website": "https://github.com/restic/rest-server", + "documentation": "https://github.com/restic/rest-server", + "repository": "https://github.com/restic/rest-server", + "tips": [ + "Authentication stays enabled. Before connecting any backup client, open the container console and run create_user ; enter the password at the hidden prompt. Do not pass the password as a command-line argument. The image stores the bcrypt hash in /data/.htpasswd.", + "The API has no web interface. Use a restic client with a repository URL that includes the username-specific path, for example rest:http://USER@HOST:8000/USER when private repositories are enabled.", + "The upstream default uses HTTP, which sends Basic Authentication credentials in clear text. Use a trusted LAN or route clients through an HTTPS reverse proxy before remote access.", + "With --private-repos enabled, each user can access only a matching repository subdirectory. Keep /data persistent; it contains both backups and the authentication file.", + "The upstream endpoint uses HTTP. Use an HTTPS reverse proxy for remote clients; the example API URL shown in this entry is HTTP.", + "El endpoint original usa HTTP. Para clientes remotos, coloca un proxy HTTPS delante; la URL de ejemplo de esta ficha usa HTTP." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "restic", + "repository": "https://github.com/restic/rest-server", + "revision": "cfcf3fea23edb404c860e8708e39306717fe3e85", + "image_repository_url": "https://hub.docker.com/r/restic/rest-server", + "readme_pushed_at": "", + "compose_sha256": "f346e67428e1c6e8e265d607401fe5d5289b5c7ad478170abb4984fa8dcb01ad", + "generated_at": "2026-10-26T06:50:27+00:00", + "readme_raw_url": "https://github.com/restic/rest-server" + }, + "container_contract": { + "service_name": "restic-rest-server", + "container_name": "restic-rest-server", + "image": { + "reference": "restic/rest-server:latest", + "registry": "docker.io", + "repository": "restic/rest-server", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "OPTIONS", + "example": "--private-repos", + "required": true, + "sensitive": false, + "source": "official-rest-server-documentation", + "prompt_user": false + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "restic-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 16 + } + } + ], + "ports": [ + { + "container_port": 8000, + "published_example": 8000, + "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\": \"restic-rest-server\",\n \"services\": {\n \"restic-rest-server\": {\n \"image\": \"restic/rest-server:latest\",\n \"container_name\": \"restic-rest-server\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8000:8000/tcp\"\n ],\n \"volumes\": [\n \"restic-data:/data\"\n ],\n \"environment\": {\n \"OPTIONS\": \"--private-repos\"\n }\n }\n },\n \"volumes\": {\n \"restic-data\": {}\n }\n}\n" + }, + "compose_stack": { + "project_name": "restic-rest-server", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "restic-rest-server", + "service_count": 1, + "services": [ + { + "name": "restic-rest-server", + "image": "restic/rest-server:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "restic/rest-server:latest", + "container_name": "restic-rest-server", + "restart": "unless-stopped", + "ports": [ + "8000:8000/tcp" + ], + "volumes": [ + "restic-data:/data" + ], + "environment": { + "OPTIONS": "--private-repos" + } + } + } + ], + "top_level": { + "name": "restic-rest-server", + "volumes": { + "restic-data": {} + } + }, + "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": "restic-rest-server-volume-0", + "service": "restic-rest-server", + "container_path": "/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "restic-rest-server" + ], + "stop_order": [ + "restic-rest-server" + ], + "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": [] + } + }, + "first_run": { + "endpoints": [], + "credentials": [ + { + "label": "REST API Basic Authentication user", + "username": "Create with create_user in the container console", + "password": null, + "change_required": false, + "source": "official-rest-server-documentation", + "retrieval": null + } + ] + }, + "proxmox": { + "runtime": "native-oci-lxc", + "technology_status": "proxmox-technology-preview", + "defaults": { + "unprivileged": true, + "ostype": "auto-from-image", + "cores": 1, + "memory_mb": 512, + "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": 120 + }, + "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": "dedicated-lxc-network", + "upstream_behavior": "The Compose definition publishes service ports on its Docker host.", + "native_lxc_behavior": "ProxMenux keeps the image service ports and exposes them on this LXC address without Docker port NAT.", + "reason": "The application runs in its own Proxmox LXC network namespace.", + "behavioral_impact": "Connect to the LXC address and the service port shown in the install summary.", + "validation": "pending-per-application" + }, + { + "id": "compose-command", + "upstream_behavior": "Compose provides the command that starts the service.", + "native_lxc_behavior": "ProxMenux imports the image entrypoint and applies the reviewed command as the native OCI process.", + "reason": "The image runs directly as a native OCI LXC process.", + "behavioral_impact": "The process starts with the upstream server command.", + "validation": "pending-per-application" + } + ], + "installer_profile": { + "startup_healthcheck": { + "scheme": "http", + "port": 8000, + "path": "/", + "timeout_seconds": 120, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "command", + "container_name", + "environment", + "hostname", + "image", + "ports", + "restart", + "volumes" + ], + "untranslated_blockers": [], + "policy": "The single upstream image runs as a native Proxmox OCI LXC. Only the documented image command, environment, ports and persistent paths are translated." + }, + "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 + } +} diff --git a/oci/catalog/curated/watcharr.json b/oci/catalog/curated/watcharr.json new file mode 100644 index 00000000..a72d08ff --- /dev/null +++ b/oci/catalog/curated/watcharr.json @@ -0,0 +1,453 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-watcharr", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Watcharr", + "es_ES": "Watcharr" + }, + "tagline": { + "en_US": "Track movies, television, anime and games you want to enjoy.", + "es_ES": "Lleva un registro de películas, series, anime y videojuegos." + }, + "description": { + "en_US": "Track movies, television shows, anime and games you want to watch or play. The single container stores its database and cache in /data.", + "es_ES": "Haz seguimiento de películas, series, anime y juegos que quieres ver o jugar. El contenedor guarda la base de datos y la caché en /data." + }, + "category": "media", + "category_label": "Media & Streaming", + "author": "sbondCo", + "developer": "sbondCo", + "icon": "https://raw.githubusercontent.com/sbondCo/Watcharr/dev/doc/static/img/logo-col.png", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 3080, + "path": "/" + }, + "website": "https://watcharr.app/", + "documentation": "https://watcharr.app/docs/installation/docker-compose", + "repository": "https://github.com/sbondCo/Watcharr", + "tips": [ + "The first account created becomes the administrator. Keep access limited to your trusted network until initial setup is complete." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "sbondCo", + "repository": "https://github.com/sbondCo/Watcharr", + "revision": "aa2a765788a4d0f6eb607e7f90acb0d89d62fcd7", + "image_repository_url": "https://github.com/orgs/sbondco/packages/container/package/watcharr", + "readme_pushed_at": "", + "default_branch": "dev", + "compose_sha256": "8c4aa4fc5f19e33730685e75e89a7d6a79253bc3ea7ebd6ea6e27e0981fe5f54", + "generated_at": "2026-10-26T08:45:03+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/sbondCo/Watcharr/dev/README.md" + }, + "container_contract": { + "service_name": "watcharr", + "container_name": "watcharr", + "image": { + "reference": "ghcr.io/sbondco/watcharr:latest", + "registry": "ghcr.io", + "repository": "ghcr.io/sbondco/watcharr", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 3080, + "published_example": 3080, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [], + "restart": "unless-stopped", + "stop_grace_period": null, + "original_compose": "services:\n watcharr:\n image: ghcr.io/sbondco/watcharr:latest\n container_name: watcharr\n restart: unless-stopped\n ports:\n - 3080:3080/tcp\n volumes:\n - ./data:/data\n environment: {}\n" + }, + "compose_stack": { + "project_name": "watcharr", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "watcharr", + "service_count": 1, + "services": [ + { + "name": "watcharr", + "image": "ghcr.io/sbondco/watcharr:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/sbondco/watcharr:latest", + "ports": [ + "3080:3080/tcp" + ], + "volumes": [ + "./data:/data" + ], + "container_name": "watcharr", + "restart": "unless-stopped", + "environment": {} + } + } + ], + "top_level": { + "name": "watcharr" + }, + "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": "watcharr-volume-0", + "service": "watcharr", + "container_path": "/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "watcharr" + ], + "stop_order": [ + "watcharr" + ], + "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": [] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Watcharr", + "scheme": "http", + "port": 3080, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "First Watcharr account (becomes administrator)", + "type": "configured-or-installer-generated", + "username": "Created during first-run setup", + "password": null, + "change_required": false, + "source": "upstream-documentation", + "retrieval": null + } + ] + }, + "proxmox": { + "runtime": "native-oci-lxc", + "technology_status": "proxmox-technology-preview", + "defaults": { + "unprivileged": true, + "ostype": "auto-from-image", + "cores": 2, + "memory_mb": 512, + "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": "/data", + "remove_lost_found": true, + "owner_strategy": "mapped-root", + "only_when_mount_type": "managed-volume" + } + ], + "startup_healthcheck": { + "scheme": "http", + "port": 3080, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 3, + "verify_tls": false, + "accept_any_status": true + } + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "cap_add", + "command", + "container_name", + "cpu_shares", + "deploy", + "devices", + "entrypoint", + "environment", + "extra_hosts", + "group_add", + "healthcheck", + "hostname", + "image", + "init", + "ipc", + "labels", + "logging", + "mac_address", + "mem_limit", + "network_mode", + "networks", + "ports", + "privileged", + "restart", + "runtime", + "security_opt", + "shm_size", + "stdin_open", + "stop_grace_period", + "sysctls", + "tty", + "ulimits", + "user", + "volumes", + "working_dir" + ], + "untranslated_blockers": [], + "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available." + }, + "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 + } +} diff --git a/oci/catalog/index.json b/oci/catalog/index.json index 940b0c03..f77c4a7a 100644 --- a/oci/catalog/index.json +++ b/oci/catalog/index.json @@ -71,6 +71,37 @@ "hidden": false, "template_id": "image-acestream-http-proxy" }, + { + "id": "activepieces", + "provider": "activepieces", + "template_family": "curated-profile", + "title": "Activepieces", + "repository": "https://github.com/activepieces/activepieces", + "description": "Build and run automation flows from a self-hosted visual interface. This single-container profile uses Activepieces\u2019 documented personal mode with embedded storage and an in-memory queue.", + "website": "https://www.activepieces.com/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/activepieces.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "ghcr.io/activepieces/activepieces:latest", + "category": "automation", + "category_label": "Automation & Scheduling", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/activepieces.json", + "template_id": "image-activepieces", + "template": "apps/activepieces.json", + "template_status": "generated-unvalidated", + "automatic_install_candidate": true, + "requires_privileged_lxc": false, + "optional_privileged_lxc": false, + "requires_host_pid_namespace": false, + "requires_relaxed_confinement": false, + "requires_security_confirmation": false, + "hidden": false, + "untranslated_blockers": [] + }, { "id": "actualbudget", "source_app_id": "actualbudget", @@ -1439,6 +1470,38 @@ "hidden": false, "template_id": "linuxserver-budge" }, + { + "id": "bytebase", + "provider": "Bytebase", + "template_family": "curated-profile", + "title": "Bytebase", + "repository": "https://github.com/bytebase/bytebase", + "description": "Manage database changes, access and workflows through a browser-based workspace. The image contains its own database, so this profile does not require a second service.", + "website": "https://www.bytebase.com/", + "icon": "https://cdn.jsdelivr.net/gh/bytebase/bytebase@main/frontend/src/assets/logo-icon.svg", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "bytebase/bytebase:latest", + "category": "databases", + "category_label": "Databases", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/bytebase.json", + "template": "apps/bytebase.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-bytebase" + }, { "id": "caddy", "provider": "caddyserver", @@ -1599,6 +1662,39 @@ "hidden": false, "template_id": "image-calnode" }, + { + "id": "campfire", + "provider": "AwsomeFox", + "template_family": "curated-profile", + "title": "Campfire", + "repository": "https://github.com/AwsomeFox/campfire", + "description": "A self-hosted web application for organizing tabletop role-playing games.", + "website": "https://github.com/AwsomeFox/campfire", + "icon": "https://raw.githubusercontent.com/AwsomeFox/campfire/main/apps/web/public/pwa-512x512.png", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "ghcr.io/awsomefox/campfire:latest", + "category": "gaming", + "category_label": "Gaming & Leisure", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/campfire.json", + "template": "apps/campfire.json", + "template_status": "generated-unvalidated", + "hidden": false, + "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, + "multi_container": false, + "template_id": "image-campfire" + }, { "id": "changedetection.io", "provider": "linuxserver.io", @@ -1735,6 +1831,38 @@ "hidden": false, "template_id": "image-chatgpt-next-web" }, + { + "id": "chhoto-url", + "provider": "sintan1729", + "template_family": "curated-profile", + "title": "Chhoto URL", + "repository": "https://github.com/SinTan1729/chhoto-url", + "description": "Create and manage short links from a small web interface. Chhoto URL stores its link database in SQLite and supports password-protected administration without a separate database service.", + "website": "https://github.com/SinTan1729/chhoto-url", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/chhoto-url.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "sintan1729/chhoto-url:latest", + "category": "tools", + "category_label": "Tools", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/chhoto-url.json", + "template": "apps/chhoto-url.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, + "multi_container": false, + "template_id": "image-chhoto-url" + }, { "id": "chrome", "provider": "linuxserver.io", @@ -3668,6 +3796,38 @@ "hidden": false, "template_id": "image-duplicati-official" }, + { + "id": "ech0", + "provider": "lin-snow", + "template_family": "curated-profile", + "title": "Ech0", + "repository": "https://github.com/lin-snow/Ech0", + "description": "Publish short posts, links and media on a personal timeline that you control. Ech0 includes accounts, comments, RSS and local data storage in one lightweight service.", + "website": "https://www.ech0.app/", + "icon": "https://raw.githubusercontent.com/lin-snow/Ech0/main/docs/imgs/logo.svg", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "sn0wl1n/ech0:latest", + "category": "communication", + "category_label": "Communication & Community", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/ech0.json", + "template": "apps/ech0.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, + "multi_container": false, + "template_id": "image-ech0" + }, { "id": "eden", "provider": "linuxserver.io", @@ -7629,6 +7789,38 @@ "hidden": false, "template_id": "image-komga" }, + { + "id": "kopia", + "provider": "kopia", + "template_family": "curated-profile", + "title": "Kopia", + "repository": "https://github.com/kopia/kopia", + "description": "Kopia provides encrypted, deduplicated backups with a web interface for repositories, policies, snapshots and restores. This profile supports browsing and restoring files through the UI; mounting snapshots with FUSE is not enabled.", + "website": "https://github.com/kopia/kopia", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kopia.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "kopia/kopia:latest", + "category": "backup", + "category_label": "Backup & Recovery", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/kopia.json", + "template": "apps/kopia.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-kopia" + }, { "id": "krita", "provider": "linuxserver.io", @@ -9320,6 +9512,38 @@ "multi_container": false, "template_id": "image-meshcentral" }, + { + "id": "metabase", + "provider": "metabase", + "template_family": "curated-profile", + "title": "Metabase", + "repository": "https://github.com/metabase/metabase", + "description": "An open-source business intelligence platform for querying data and building dashboards. This single-container profile uses embedded H2 for a home-lab trial or small instance; PostgreSQL is recommended for production.", + "website": "https://www.metabase.com/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/metabase.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "metabase/metabase:v0.64-lts", + "category": "monitoring", + "category_label": "Monitoring & Analytics", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/metabase.json", + "template": "apps/metabase.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-metabase" + }, { "id": "metube", "provider": "alexta69", @@ -11210,6 +11434,38 @@ "hidden": false, "template_id": "image-openhab" }, + { + "id": "openhabittracker", + "provider": "Jinjinov", + "template_family": "curated-profile", + "title": "OpenHabitTracker", + "repository": "https://github.com/Jinjinov/OpenHabitTracker", + "description": "The official image includes the Blazor Server application for one user. It persists application data and backups in /app/.OpenHabitTracker.", + "website": "https://github.com/Jinjinov/OpenHabitTracker", + "icon": "https://cdn.jsdelivr.net/gh/Jinjinov/OpenHabitTracker@main/OpenHabitTracker.Web/icons/icon-512.png", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "jinjinov/openhabittracker:latest", + "category": "productivity", + "category_label": "Productivity & Workflows", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/openhabittracker.json", + "template": "apps/openhabittracker.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, + "multi_container": false, + "template_id": "image-openhabittracker" + }, { "id": "openhands", "source_app_id": "openhands", @@ -13732,6 +13988,38 @@ "hidden": false, "template_id": "linuxserver-resilio-sync" }, + { + "id": "restic-rest-server", + "provider": "restic", + "template_family": "curated-profile", + "title": "Restic REST Server", + "repository": "https://github.com/restic/rest-server", + "description": "The official rest-server image provides an authenticated HTTP REST backend for restic clients, with persistent repositories and bcrypt-protected user accounts. It exposes an API, not a browser-based application.", + "website": "https://github.com/restic/rest-server", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/restic.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "restic/rest-server:latest", + "category": "backup", + "category_label": "Backup & Recovery", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/restic-rest-server.json", + "template": "apps/restic-rest-server.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-restic-rest-server" + }, { "id": "retroarch", "provider": "linuxserver.io", @@ -17233,6 +17521,38 @@ "hidden": false, "template_id": "image-wallos" }, + { + "id": "watcharr", + "provider": "sbondCo", + "template_family": "curated-profile", + "title": "Watcharr", + "repository": "https://github.com/sbondCo/Watcharr", + "description": "Track movies, television shows, anime and games you want to watch or play. The single container stores its database and cache in /data.", + "website": "https://watcharr.app/", + "icon": "https://raw.githubusercontent.com/sbondCo/Watcharr/dev/doc/static/img/logo-col.png", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "ghcr.io/sbondco/watcharr:latest", + "category": "media", + "category_label": "Media & Streaming", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/watcharr.json", + "template": "apps/watcharr.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, + "multi_container": false, + "template_id": "image-watcharr" + }, { "id": "wbo", "provider": "lovasoa", diff --git a/oci/tests/test_next10_a_profiles.py b/oci/tests/test_next10_a_profiles.py new file mode 100644 index 00000000..97a27484 --- /dev/null +++ b/oci/tests/test_next10_a_profiles.py @@ -0,0 +1,54 @@ +"""Guard the Campfire recipe contract in the next-10 catalog profiles.""" +import json +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[1] +EXPECTED_IMAGE = "ghcr.io/awsomefox/campfire:latest" +EXPECTED_ICON = ( + "https://raw.githubusercontent.com/AwsomeFox/campfire/main/" + "apps/web/public/pwa-512x512.png" +) + + +class CampfireProfileTests(unittest.TestCase): + def test_app_and_curated_profiles_match_the_single_service_contract(self): + for family in ("apps", "curated"): + with self.subTest(family=family): + template = json.loads( + (ROOT / "catalog" / family / "campfire.json") + .read_text(encoding="utf-8") + ) + + contract = template["container_contract"] + stack = template["compose_stack"] + self.assertEqual(contract["image"]["reference"], EXPECTED_IMAGE) + self.assertEqual(stack["service_count"], 1) + self.assertEqual(len(stack["services"]), 1) + service = stack["services"][0] + self.assertEqual(service["image"], EXPECTED_IMAGE) + self.assertEqual(service["compose"]["image"], EXPECTED_IMAGE) + + volume_paths = { + volume["container_path"] for volume in contract["volumes"] + } + self.assertIn("/data", volume_paths) + self.assertIn("./data-0:/data", service["compose"]["volumes"]) + + setup = next( + endpoint for endpoint in template["first_run"]["endpoints"] + if endpoint["label"] == "Campfire first-run setup" + ) + self.assertEqual(setup["path"], "/auth/setup") + + healthcheck = template["proxmox"]["installer_profile"][ + "startup_healthcheck" + ] + self.assertEqual(healthcheck["path"], "/readyz") + + self.assertEqual(template["catalog_ui"]["icon"], EXPECTED_ICON) + + +if __name__ == "__main__": + unittest.main() diff --git a/oci/tests/test_next10_b_profiles.py b/oci/tests/test_next10_b_profiles.py new file mode 100644 index 00000000..dc769c3b --- /dev/null +++ b/oci/tests/test_next10_b_profiles.py @@ -0,0 +1,156 @@ +"""Static install contracts for the next OCI catalog profiles.""" + +import json +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[1] + + +PROFILES = { + "chhoto-url": { + "image": "sintan1729/chhoto-url:latest", + "data_path": "/data", + "port": 4567, + "health_path": "/", + }, + "openhabittracker": { + "image": "jinjinov/openhabittracker:latest", + "data_path": "/app/.OpenHabitTracker", + "port": 8080, + "health_path": "/login", + }, + "watcharr": { + "image": "ghcr.io/sbondco/watcharr:latest", + "data_path": "/data", + "port": 3080, + "health_path": "/", + }, + "ech0": { + "image": "sn0wl1n/ech0:latest", + "data_path": "/app/data", + "port": 6277, + "health_path": "/", + }, + "restic-rest-server": { + "image": "restic/rest-server:latest", + "data_path": "/data", + "port": 8000, + "health_path": "/", + }, +} + + +def load_profile(app_id): + path = ROOT / "catalog" / "apps" / f"{app_id}.json" + return json.loads(path.read_text(encoding="utf-8")) + + +class Next10BProfiles(unittest.TestCase): + def test_each_profile_is_one_image_with_an_icon_and_persistent_data(self): + for app_id, expected in PROFILES.items(): + with self.subTest(app=app_id): + profile = load_profile(app_id) + services = profile["compose_stack"]["services"] + image = profile["container_contract"]["image"]["reference"] + + self.assertEqual(profile["compose_stack"]["service_count"], 1) + self.assertEqual(len(services), 1) + self.assertEqual(services[0]["image"], expected["image"]) + self.assertEqual(image, expected["image"]) + self.assertEqual(services[0]["depends_on"], []) + + icon = profile["catalog_ui"].get("icon") + self.assertIsInstance(icon, str) + self.assertTrue(icon.startswith("https://"), icon) + + mount_paths = { + volume["container_path"] + for volume in profile["container_contract"]["volumes"] + } + self.assertIn(expected["data_path"], mount_paths) + + def test_each_profile_has_a_matching_first_start_check(self): + for app_id, expected in PROFILES.items(): + with self.subTest(app=app_id): + profile = load_profile(app_id) + check = profile["proxmox"]["installer_profile"]["startup_healthcheck"] + + self.assertEqual(check["scheme"], "http") + self.assertEqual(check["port"], expected["port"]) + self.assertEqual(check["path"], expected["health_path"]) + self.assertTrue(check["accept_any_status"]) + + def test_chhoto_url_uses_generated_password_and_data_database(self): + profile = load_profile("chhoto-url") + environment = { + item["name"]: item + for item in profile["container_contract"]["environment"] + } + generated = profile["proxmox"]["installer_profile"]["generated_sensitive_environment"] + credential = profile["first_run"]["credentials"][0] + + self.assertIn("CHHOTO_PASSWORD", environment) + self.assertTrue(environment["CHHOTO_PASSWORD"]["sensitive"]) + self.assertIn("CHHOTO_PASSWORD", generated) + self.assertEqual(credential["password_environment"], "CHHOTO_PASSWORD") + self.assertEqual(environment["CHHOTO_DB_URL"]["example"], "/data/urls.sqlite") + self.assertEqual(environment["CHHOTO_SQLITE_USE_WAL_MODE"]["example"], "True") + + def test_openhabittracker_prompts_for_user_and_generates_password_and_jwt(self): + profile = load_profile("openhabittracker") + environment = { + item["name"]: item + for item in profile["container_contract"]["environment"] + } + generated = profile["proxmox"]["installer_profile"]["generated_sensitive_environment"] + credential = profile["first_run"]["credentials"][0] + + self.assertIn("prompt", environment["AppSettings__UserName"]) + self.assertIn("prompt", environment["AppSettings__Email"]) + self.assertIn("AppSettings__Password", generated) + self.assertIn("AppSettings__JwtSecret", generated) + self.assertEqual(credential["username_environment"], "AppSettings__UserName") + self.assertEqual(credential["password_environment"], "AppSettings__Password") + + def test_watcharr_first_registered_user_becomes_admin(self): + profile = load_profile("watcharr") + credentials = profile["first_run"]["credentials"] + + self.assertEqual(profile["container_contract"]["environment"], []) + self.assertEqual(len(credentials), 1) + self.assertIn("administrator", credentials[0]["label"].lower()) + self.assertIn("first-run setup", credentials[0]["username"].lower()) + + def test_ech0_generates_jwt_and_marks_first_user_as_owner(self): + profile = load_profile("ech0") + generated = profile["proxmox"]["installer_profile"]["generated_sensitive_environment"] + credentials = profile["first_run"]["credentials"] + + self.assertIn("JWT_SECRET", generated) + self.assertEqual(profile["container_contract"]["environment"][0]["name"], "JWT_SECRET") + self.assertEqual(len(credentials), 1) + self.assertIn("Owner", credentials[0]["label"]) + self.assertIn("first-run setup", credentials[0]["username"].lower()) + + def test_restic_server_is_an_api_without_a_browser_endpoint(self): + profile = load_profile("restic-rest-server") + credentials = profile["first_run"]["credentials"] + ui = profile["catalog_ui"] + + self.assertEqual(profile["first_run"]["endpoints"], []) + self.assertIsNone(ui["launch"]["port"]) + self.assertIn("no web interface", ui["tagline"]["en_US"].lower()) + self.assertIn("not a browser-based application", ui["description"]["en_US"].lower()) + self.assertEqual(len(credentials), 1) + self.assertIn("create_user", credentials[0]["username"]) + self.assertIsNone(credentials[0]["password"]) + self.assertEqual( + profile["container_contract"]["environment"][0]["example"], + "--private-repos", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/oci/tests/test_next10_c_profiles.py b/oci/tests/test_next10_c_profiles.py new file mode 100644 index 00000000..defc6b3b --- /dev/null +++ b/oci/tests/test_next10_c_profiles.py @@ -0,0 +1,176 @@ +"""Guard the Activepieces, Bytebase, Metabase and Kopia OCI recipes.""" +import json +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[1] + +EXPECTED = { + "activepieces": { + "image": "ghcr.io/activepieces/activepieces:latest", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/activepieces.webp", + "health": (80, "/api/v1/health"), + "note_terms": ("personal", "memory"), + }, + "bytebase": { + "image": "bytebase/bytebase:latest", + "icon": "https://cdn.jsdelivr.net/gh/bytebase/bytebase@main/frontend/src/assets/logo-icon.svg", + "health": (8080, "/healthz"), + "note_terms": (), + }, + "metabase": { + "image": "metabase/metabase:v0.64-lts", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/metabase.webp", + "health": (3000, "/api/health"), + "note_terms": ("h2", "postgres"), + }, + "kopia": { + "image": "kopia/kopia:latest", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kopia.webp", + "health": (51515, "/"), + "note_terms": ("repository", "password"), + }, +} + + +def read_profile(family, app): + path = ROOT / "catalog" / family / f"{app}.json" + return json.loads(path.read_text(encoding="utf-8")) + + +class Next10CProfileTests(unittest.TestCase): + def test_image_single_service_and_icon_match_catalog_contract(self): + for app, expected in EXPECTED.items(): + with self.subTest(app=app): + for family in ("apps", "curated"): + profile = read_profile(family, app) + stack = profile["compose_stack"] + service = stack["services"][0] + self.assertEqual( + profile["container_contract"]["image"]["reference"], + expected["image"], + ) + self.assertEqual(stack["service_count"], 1) + self.assertEqual(len(stack["services"]), 1) + self.assertEqual(service["image"], expected["image"]) + self.assertEqual(service["compose"]["image"], expected["image"]) + self.assertEqual(profile["catalog_ui"]["icon"], expected["icon"]) + self.assertTrue(expected["icon"].startswith("https://")) + + def test_startup_healthcheck_targets_the_documented_service_endpoint(self): + for app, expected in EXPECTED.items(): + with self.subTest(app=app): + for family in ("apps", "curated"): + profile = read_profile(family, app) + health = profile["proxmox"]["installer_profile"][ + "startup_healthcheck" + ] + self.assertEqual(health["port"], expected["health"][0]) + self.assertEqual(health["path"], expected["health"][1]) + self.assertEqual(health["scheme"], "http") + + def test_required_persistent_paths_are_mounted_by_the_single_service(self): + for app in EXPECTED: + with self.subTest(app=app): + for family in ("apps", "curated"): + profile = read_profile(family, app) + contract_paths = { + volume["container_path"] + for volume in profile["container_contract"]["volumes"] + if volume.get("required", False) + } + mounts = profile["compose_stack"]["services"][0]["compose"].get( + "volumes", [] + ) + mounted_paths = { + mount.split(":")[1] + for mount in mounts + if len(mount.split(":")) >= 2 + } + self.assertTrue(contract_paths, "profile must declare persistent data") + self.assertTrue( + contract_paths.issubset(mounted_paths), + f"required paths missing from Compose mounts: " + f"{sorted(contract_paths - mounted_paths)}", + ) + + def test_secret_environment_is_sensitive_generated_and_wired_to_compose(self): + for app in EXPECTED: + with self.subTest(app=app): + for family in ("apps", "curated"): + profile = read_profile(family, app) + declared_sensitive = { + item["name"] + for item in profile["container_contract"].get("environment", []) + if item.get("sensitive") + } + runtime_generated = profile["proxmox"]["installer_profile"].get( + "generated_sensitive_environment", {} + ) + generated_bindings = { + binding["environment_variable"] + for secret in profile["compose_stack"]["installer_inputs"].get( + "generated_secrets", [] + ) + for binding in secret.get("bindings", []) + } + compose_environment = profile["compose_stack"]["services"][0][ + "compose" + ].get("environment", {}) + + if app == "bytebase": + self.assertEqual(generated_bindings, set()) + self.assertEqual(runtime_generated, {}) + self.assertNotIn("HBOX_AUTH_API_KEY_PEPPER", declared_sensitive) + self.assertNotIn("HBOX_AUTH_API_KEY_PEPPER", compose_environment) + self.assertNotIn("homebox", json.dumps(profile).casefold()) + continue + + self.assertTrue( + generated_bindings, + "profile must document generated credentials/secrets", + ) + self.assertEqual( + generated_bindings, + set(runtime_generated), + "generated secret bindings and runtime generators must match", + ) + self.assertTrue( + generated_bindings.issubset(declared_sensitive), + "every generated secret must be declared sensitive in the contract", + ) + for variable in generated_bindings: + self.assertIn(variable, compose_environment) + self.assertIn(variable, str(compose_environment[variable])) + + def test_app_notes_explain_the_key_limitations_or_setup_requirements(self): + for app, expected in EXPECTED.items(): + with self.subTest(app=app): + for family in ("apps", "curated"): + profile = read_profile(family, app) + if app == "bytebase": + endpoints = profile["first_run"]["endpoints"] + web_ui = next( + endpoint for endpoint in endpoints + if endpoint["label"] == "Web UI" + ) + self.assertEqual(web_ui["scheme"], "http") + self.assertEqual(web_ui["port"], 8080) + self.assertEqual(web_ui["path"], "/") + self.assertEqual(profile["first_run"]["credentials"], []) + self.assertEqual( + profile["catalog_ui"]["documentation"], + "https://docs.bytebase.com/get-started/self-host/deploy-with-docker", + ) + continue + + tips = profile["catalog_ui"].get("tips", []) + note = " ".join(tips).casefold() + self.assertTrue(tips, "profile must include app-specific notes") + for term in expected["note_terms"]: + self.assertIn(term, note) + + +if __name__ == "__main__": + unittest.main()