diff --git a/oci/VALIDATION.md b/oci/VALIDATION.md index d3de1209..2d1b5059 100644 --- a/oci/VALIDATION.md +++ b/oci/VALIDATION.md @@ -2,7 +2,7 @@ -43 of 354 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 364 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/gatus.json b/oci/catalog/apps/gatus.json new file mode 100644 index 00000000..58e9092b --- /dev/null +++ b/oci/catalog/apps/gatus.json @@ -0,0 +1,450 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-gatus", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Gatus" + }, + "tagline": { + "en_US": "Health dashboards and endpoint monitoring." + }, + "description": { + "en_US": "Monitor HTTP endpoints and display their status in a lightweight dashboard. The profile supplies a minimal configuration, stores history in SQLite and checks the local Gatus page." + }, + "category": "monitoring", + "category_label": "Monitoring & Analytics", + "author": "TwiN", + "developer": "TwiN", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/gatus.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/" + }, + "website": "https://gatus.io/", + "documentation": "https://github.com/TwiN/gatus#configuration", + "repository": "https://github.com/TwiN/gatus", + "tips": [ + "Edit /config/config.yaml to add the endpoints you want to monitor. This profile stores the status history in /data/gatus.db." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "TwiN", + "repository": "https://github.com/TwiN/gatus", + "revision": "fae6edef2f288c47c294fbe55453b92390551869", + "image_repository_url": "https://github.com/TwiN/gatus/pkgs/container/gatus", + "readme_pushed_at": "", + "compose_sha256": "db8d4362f0ff0c7bd9f43de656bee2c71eab1a3fad3551d1853b5e6b09d01109", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/TwiN/gatus" + }, + "container_contract": { + "service_name": "gatus", + "container_name": "gatus", + "image": { + "reference": "ghcr.io/twin/gatus:latest", + "registry": "ghcr.io", + "repository": "twin/gatus", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "GATUS_CONFIG_PATH", + "example": "/config/config.yaml", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "./gatus-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "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\": \"gatus\",\n \"services\": {\n \"gatus\": {\n \"image\": \"ghcr.io/twin/gatus:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080/tcp\"\n ],\n \"volumes\": [\n \"./gatus-data:/data\"\n ],\n \"environment\": {\n \"GATUS_CONFIG_PATH\": \"/config/config.yaml\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "gatus", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "gatus", + "service_count": 1, + "services": [ + { + "name": "gatus", + "image": "ghcr.io/twin/gatus:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/twin/gatus:latest", + "restart": "unless-stopped", + "ports": [ + "8080:8080/tcp" + ], + "volumes": [ + "./gatus-data:/data" + ], + "environment": { + "GATUS_CONFIG_PATH": "/config/config.yaml" + } + } + } + ], + "top_level": { + "name": "gatus" + }, + "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": "gatus-volume-0", + "service": "gatus", + "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": [ + "gatus" + ], + "stop_order": [ + "gatus" + ], + "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": "Gatus", + "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": 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": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "generated_files": [ + { + "container_path": "/config/config.yaml", + "mode": "0644", + "owner": "mapped-root", + "content": "storage:\n type: sqlite\n path: /data/gatus.db\nendpoints:\n - name: Gatus\n group: local\n url: http://127.0.0.1:8080/\n interval: 1m\n conditions:\n - \"[STATUS] == 200\"\n" + } + ] + }, + "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/gotify.json b/oci/catalog/apps/gotify.json new file mode 100644 index 00000000..dee20920 --- /dev/null +++ b/oci/catalog/apps/gotify.json @@ -0,0 +1,477 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-gotify", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Gotify" + }, + "tagline": { + "en_US": "Self-hosted push notification server." + }, + "description": { + "en_US": "Receive notifications from scripts and services through Gotify clients and its web interface. ProxMenux generates a unique initial administrator password." + }, + "category": "messaging", + "category_label": "Messaging & Notifications", + "author": "gotify", + "developer": "gotify", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/gotify.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 80, + "path": "/" + }, + "website": "https://gotify.net/", + "documentation": "https://gotify.net/docs/install", + "repository": "https://github.com/gotify/server", + "tips": [ + "The generated administrator password is shown when the installation finishes. Save it before leaving the terminal." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "gotify", + "repository": "https://github.com/gotify/server", + "revision": "dd06dc77190696e93d3297b9a733711516fb17d9", + "image_repository_url": "https://hub.docker.com/r/gotify/server", + "readme_pushed_at": "", + "compose_sha256": "68a4d6d32829ca5944e1bbdf79f0572e981dfcb218e1f14610feedc6b46f675d", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/gotify/server" + }, + "container_contract": { + "service_name": "gotify", + "container_name": "gotify", + "image": { + "reference": "gotify/server:latest", + "registry": "docker.io", + "repository": "gotify/server", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "GOTIFY_DEFAULTUSER_NAME", + "example": "admin", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "GOTIFY_DEFAULTUSER_PASS", + "example": "${GENERATED_GOTIFY_ADMIN_PASSWORD}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/data", + "compose_source_example": "./gotify-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": 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": "{\n \"name\": \"gotify\",\n \"services\": {\n \"gotify\": {\n \"image\": \"gotify/server:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"80:80/tcp\"\n ],\n \"volumes\": [\n \"./gotify-data:/app/data\"\n ],\n \"environment\": {\n \"GOTIFY_DEFAULTUSER_NAME\": \"admin\",\n \"GOTIFY_DEFAULTUSER_PASS\": \"${GENERATED_GOTIFY_ADMIN_PASSWORD}\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "gotify", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "gotify", + "service_count": 1, + "services": [ + { + "name": "gotify", + "image": "gotify/server:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "gotify/server:latest", + "restart": "unless-stopped", + "ports": [ + "80:80/tcp" + ], + "volumes": [ + "./gotify-data:/app/data" + ], + "environment": { + "GOTIFY_DEFAULTUSER_NAME": "admin", + "GOTIFY_DEFAULTUSER_PASS": "${GENERATED_GOTIFY_ADMIN_PASSWORD}" + } + } + } + ], + "top_level": { + "name": "gotify" + }, + "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": "gotify-volume-0", + "service": "gotify", + "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": [ + "gotify" + ], + "stop_order": [ + "gotify" + ], + "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-gotify-defaultuser-pass", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "gotify", + "environment_variable": "GOTIFY_DEFAULTUSER_PASS" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Gotify", + "scheme": "http", + "port": 80, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "Gotify administrator", + "type": "runtime-generated", + "username": "admin", + "password": null, + "password_environment": "GOTIFY_DEFAULTUSER_PASS", + "change_required": true, + "source": "proxmenux-installer-generated" + } + ] + }, + "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": 80, + "path": "/health", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "generated_sensitive_environment": { + "GOTIFY_DEFAULTUSER_PASS": { + "strategy": "token-hex", + "bytes": 24 + } + } + }, + "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/lldap.json b/oci/catalog/apps/lldap.json new file mode 100644 index 00000000..db682ba9 --- /dev/null +++ b/oci/catalog/apps/lldap.json @@ -0,0 +1,530 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-lldap", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "LLDAP" + }, + "tagline": { + "en_US": "Lightweight LDAP server with a web administration UI." + }, + "description": { + "en_US": "Provide LDAP authentication and manage users and groups through a compact web interface. ProxMenux generates the initial administrator password and the secrets required by LLDAP." + }, + "category": "security", + "category_label": "Authentication & Security", + "author": "lldap", + "developer": "lldap", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/lldap.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 17170, + "path": "/" + }, + "website": "https://github.com/lldap/lldap", + "documentation": "https://github.com/lldap/lldap/blob/main/docs/install.md", + "repository": "https://github.com/lldap/lldap", + "tips": [ + "The initial LDAP administrator is named admin. The generated password is used by the web interface and LDAP clients.", + "Replace dc=example,dc=com with your own directory base before connecting production clients. LDAP listens on TCP port 3890." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "lldap", + "repository": "https://github.com/lldap/lldap", + "revision": "99c510a7a603df1cdaca00a9ffff95ec73b296eb", + "image_repository_url": "https://hub.docker.com/r/lldap/lldap", + "readme_pushed_at": "", + "compose_sha256": "f722971b4fcc248da42e9cff50fdbfc85f5ba05354249572ec21b0152322229c", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/lldap/lldap" + }, + "container_contract": { + "service_name": "lldap", + "container_name": "lldap", + "image": { + "reference": "lldap/lldap:stable", + "registry": "docker.io", + "repository": "lldap/lldap", + "tag": "stable", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "LLDAP_JWT_SECRET", + "example": "${GENERATED_LLDAP_JWT_SECRET}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "LLDAP_KEY_SEED", + "example": "${GENERATED_LLDAP_KEY_SEED}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "LLDAP_LDAP_BASE_DN", + "example": "dc=example,dc=com", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "LLDAP_LDAP_USER_PASS", + "example": "${GENERATED_LLDAP_LDAP_USER_PASS}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "./lldap-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "ports": [ + { + "container_port": 17170, + "published_example": 17170, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + }, + { + "container_port": 3890, + "published_example": 3890, + "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\": \"lldap\",\n \"services\": {\n \"lldap\": {\n \"image\": \"lldap/lldap:stable\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"17170:17170/tcp\",\n \"3890:3890/tcp\"\n ],\n \"volumes\": [\n \"./lldap-data:/data\"\n ],\n \"environment\": {\n \"LLDAP_JWT_SECRET\": \"${GENERATED_LLDAP_JWT_SECRET}\",\n \"LLDAP_KEY_SEED\": \"${GENERATED_LLDAP_KEY_SEED}\",\n \"LLDAP_LDAP_BASE_DN\": \"dc=example,dc=com\",\n \"LLDAP_LDAP_USER_PASS\": \"${GENERATED_LLDAP_LDAP_USER_PASS}\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "lldap", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "lldap", + "service_count": 1, + "services": [ + { + "name": "lldap", + "image": "lldap/lldap:stable", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "lldap/lldap:stable", + "restart": "unless-stopped", + "ports": [ + "17170:17170/tcp", + "3890:3890/tcp" + ], + "volumes": [ + "./lldap-data:/data" + ], + "environment": { + "LLDAP_JWT_SECRET": "${GENERATED_LLDAP_JWT_SECRET}", + "LLDAP_KEY_SEED": "${GENERATED_LLDAP_KEY_SEED}", + "LLDAP_LDAP_BASE_DN": "dc=example,dc=com", + "LLDAP_LDAP_USER_PASS": "${GENERATED_LLDAP_LDAP_USER_PASS}" + } + } + } + ], + "top_level": { + "name": "lldap" + }, + "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": "lldap-volume-0", + "service": "lldap", + "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": [ + "lldap" + ], + "stop_order": [ + "lldap" + ], + "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-lldap-ldap-jwt-secret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "lldap", + "environment_variable": "LLDAP_JWT_SECRET" + } + ] + }, + { + "id": "generated-lldap-key-seed", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "lldap", + "environment_variable": "LLDAP_KEY_SEED" + } + ] + }, + { + "id": "generated-lldap-ldap-user-pass", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "lldap", + "environment_variable": "LLDAP_LDAP_USER_PASS" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "LLDAP", + "scheme": "http", + "port": 17170, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "LLDAP administrator", + "type": "runtime-generated", + "username": "admin", + "password": null, + "password_environment": "LLDAP_LDAP_USER_PASS", + "change_required": true, + "source": "proxmenux-installer-generated" + } + ] + }, + "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": 17170, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "generated_sensitive_environment": { + "LLDAP_JWT_SECRET": { + "strategy": "token-hex", + "bytes": 32 + }, + "LLDAP_KEY_SEED": { + "strategy": "token-hex", + "bytes": 32 + }, + "LLDAP_LDAP_USER_PASS": { + "strategy": "token-hex", + "bytes": 24 + } + } + }, + "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/ntfy.json b/oci/catalog/apps/ntfy.json new file mode 100644 index 00000000..b11afa0b --- /dev/null +++ b/oci/catalog/apps/ntfy.json @@ -0,0 +1,475 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-ntfy", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "ntfy" + }, + "tagline": { + "en_US": "Self-hosted push notifications with a web interface." + }, + "description": { + "en_US": "Send and receive push notifications through simple HTTP requests, mobile apps and a web interface. The profile stores the message cache and enables authentication with anonymous access denied by default." + }, + "category": "messaging", + "category_label": "Messaging & Notifications", + "author": "binwiederhier", + "developer": "binwiederhier", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/ntfy.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 80, + "path": "/" + }, + "website": "https://ntfy.sh/", + "documentation": "https://docs.ntfy.sh/install/", + "repository": "https://github.com/binwiederhier/ntfy", + "tips": [ + "Anonymous access is denied by default. Add users and topic permissions with the ntfy user command before using the server.", + "Use HTTPS before sending credentials over a network you do not fully trust." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "binwiederhier", + "repository": "https://github.com/binwiederhier/ntfy", + "revision": "9f1d2ea44e412fef4552382a47479fec1984e5ea", + "image_repository_url": "https://hub.docker.com/r/binwiederhier/ntfy", + "readme_pushed_at": "", + "compose_sha256": "6a34e5257e07c3cc83381088ea0dbcbde22e3cc9b927bd09df42dd617e211838", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/binwiederhier/ntfy" + }, + "container_contract": { + "service_name": "ntfy", + "container_name": "ntfy", + "image": { + "reference": "binwiederhier/ntfy:latest", + "registry": "docker.io", + "repository": "binwiederhier/ntfy", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "NTFY_CACHE_FILE", + "example": "/var/cache/ntfy/cache.db", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "NTFY_AUTH_FILE", + "example": "/var/cache/ntfy/user.db", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "NTFY_AUTH_DEFAULT_ACCESS", + "example": "deny-all", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "NTFY_ENABLE_LOGIN", + "example": "true", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/var/cache/ntfy", + "compose_source_example": "./ntfy-cache", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "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": "{\n \"name\": \"ntfy\",\n \"services\": {\n \"ntfy\": {\n \"image\": \"binwiederhier/ntfy:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"80:80/tcp\"\n ],\n \"volumes\": [\n \"./ntfy-cache:/var/cache/ntfy\"\n ],\n \"environment\": {\n \"NTFY_CACHE_FILE\": \"/var/cache/ntfy/cache.db\",\n \"NTFY_AUTH_FILE\": \"/var/cache/ntfy/user.db\",\n \"NTFY_AUTH_DEFAULT_ACCESS\": \"deny-all\",\n \"NTFY_ENABLE_LOGIN\": \"true\"\n },\n \"command\": [\n \"serve\"\n ]\n }\n }\n}" + }, + "compose_stack": { + "project_name": "ntfy", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "ntfy", + "service_count": 1, + "services": [ + { + "name": "ntfy", + "image": "binwiederhier/ntfy:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "binwiederhier/ntfy:latest", + "restart": "unless-stopped", + "ports": [ + "80:80/tcp" + ], + "volumes": [ + "./ntfy-cache:/var/cache/ntfy" + ], + "environment": { + "NTFY_CACHE_FILE": "/var/cache/ntfy/cache.db", + "NTFY_AUTH_FILE": "/var/cache/ntfy/user.db", + "NTFY_AUTH_DEFAULT_ACCESS": "deny-all", + "NTFY_ENABLE_LOGIN": "true" + }, + "command": [ + "serve" + ] + } + } + ], + "top_level": { + "name": "ntfy" + }, + "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": "ntfy-volume-0", + "service": "ntfy", + "container_path": "/var/cache/ntfy", + "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": [ + "ntfy" + ], + "stop_order": [ + "ntfy" + ], + "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": "ntfy", + "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": 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": { + "runtime": { + "command": [ + "serve" + ] + }, + "startup_healthcheck": { + "scheme": "http", + "port": 80, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + } + }, + "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/owncast.json b/oci/catalog/apps/owncast.json new file mode 100644 index 00000000..2f0542bc --- /dev/null +++ b/oci/catalog/apps/owncast.json @@ -0,0 +1,448 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-owncast", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Owncast" + }, + "tagline": { + "en_US": "Self-hosted live streaming and chat." + }, + "description": { + "en_US": "Host a live stream with an RTMP ingest endpoint and a public viewing page. The profile generates an administrator password and stream key, then passes both to Owncast at startup." + }, + "category": "communication", + "category_label": "Communication & Community", + "author": "owncast", + "developer": "owncast", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/owncast.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/" + }, + "website": "https://owncast.online/", + "documentation": "https://owncast.online/docs/configuration/runtime-flags/", + "repository": "https://github.com/owncast/owncast", + "tips": [ + "Owncast defaults to admin / abc123 for the dashboard and abc123 for the stream key. Change both immediately before exposing the service.", + "The administrator password is applied when Owncast starts; a password changed in its UI may be reset to the generated value on the next restart.", + "Streaming software connects to rtmp://:1935/live with the generated stream key." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "owncast", + "repository": "https://github.com/owncast/owncast", + "revision": "d724434a9313bf7f022822277046200e32669c59", + "image_repository_url": "https://hub.docker.com/r/owncast/owncast", + "readme_pushed_at": "", + "compose_sha256": "7e7f5ae85ee5d0f34d216bcdbc28df2a9dec9b0607e50de8c3afef3414f14420", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/owncast/owncast" + }, + "container_contract": { + "service_name": "owncast", + "container_name": "owncast", + "image": { + "reference": "owncast/owncast:latest", + "registry": "docker.io", + "repository": "owncast/owncast", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/data", + "compose_source_example": "./owncast-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" + }, + { + "container_port": 1935, + "published_example": 1935, + "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\": \"owncast\",\n \"services\": {\n \"owncast\": {\n \"image\": \"owncast/owncast:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080/tcp\",\n \"1935:1935/tcp\"\n ],\n \"volumes\": [\n \"./owncast-data:/app/data\"\n ]\n }\n }\n}" + }, + "compose_stack": { + "project_name": "owncast", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "owncast", + "service_count": 1, + "services": [ + { + "name": "owncast", + "image": "owncast/owncast:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "owncast/owncast:latest", + "restart": "unless-stopped", + "ports": [ + "8080:8080/tcp", + "1935:1935/tcp" + ], + "volumes": [ + "./owncast-data:/app/data" + ] + } + } + ], + "top_level": { + "name": "owncast" + }, + "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": "owncast-volume-0", + "service": "owncast", + "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": [ + "owncast" + ], + "stop_order": [ + "owncast" + ], + "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", + "dependency_start_and_stop_order" + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Owncast", + "scheme": "http", + "port": 8080, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "Owncast administrator", + "type": "static-default", + "username": "admin", + "password": "abc123", + "change_required": true, + "source": "upstream-documentation" + } + ] + }, + "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": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + } + }, + "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/picoshare.json b/oci/catalog/apps/picoshare.json new file mode 100644 index 00000000..0c627a42 --- /dev/null +++ b/oci/catalog/apps/picoshare.json @@ -0,0 +1,487 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-picoshare", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "PicoShare" + }, + "tagline": { + "en_US": "Simple self-hosted file sharing." + }, + "description": { + "en_US": "Upload files and share direct links through a small web interface. ProxMenux generates the required administrator passphrase and stores PicoShare data in a persistent volume." + }, + "category": "sharing", + "category_label": "Files & Sharing", + "author": "mtlynch", + "developer": "mtlynch", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/picoshare.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 4001, + "path": "/" + }, + "website": "https://github.com/mtlynch/picoshare", + "documentation": "https://github.com/mtlynch/picoshare#readme", + "repository": "https://github.com/mtlynch/picoshare", + "tips": [ + "The generated administrator passphrase is shown after installation. Keep it somewhere safe." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "mtlynch", + "repository": "https://github.com/mtlynch/picoshare", + "revision": "3b853b3cc208c2da8acb30426ce9eb9a2ef7e25a", + "image_repository_url": "https://hub.docker.com/r/mtlynch/picoshare", + "readme_pushed_at": "", + "compose_sha256": "c6d32862369ec88e58cb756f030b82b8bd85f4c2f6772d54c9a97f7021ae73b2", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/mtlynch/picoshare" + }, + "container_contract": { + "service_name": "picoshare", + "container_name": "picoshare", + "image": { + "reference": "mtlynch/picoshare:latest", + "registry": "docker.io", + "repository": "mtlynch/picoshare", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "PORT", + "example": "4001", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "PS_SHARED_SECRET", + "example": "${GENERATED_PS_SHARED_SECRET}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "./picoshare-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": 4001, + "published_example": 4001, + "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\": \"picoshare\",\n \"services\": {\n \"picoshare\": {\n \"image\": \"mtlynch/picoshare:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"4001:4001/tcp\"\n ],\n \"volumes\": [\n \"./picoshare-data:/data\"\n ],\n \"environment\": {\n \"PORT\": \"4001\",\n \"PS_SHARED_SECRET\": \"${GENERATED_PS_SHARED_SECRET}\"\n },\n \"command\": [\n \"-db\",\n \"/data/store.db\"\n ]\n }\n }\n}" + }, + "compose_stack": { + "project_name": "picoshare", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "picoshare", + "service_count": 1, + "services": [ + { + "name": "picoshare", + "image": "mtlynch/picoshare:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "mtlynch/picoshare:latest", + "restart": "unless-stopped", + "ports": [ + "4001:4001/tcp" + ], + "volumes": [ + "./picoshare-data:/data" + ], + "environment": { + "PORT": "4001", + "PS_SHARED_SECRET": "${GENERATED_PS_SHARED_SECRET}" + }, + "command": [ + "-db", + "/data/store.db" + ] + } + } + ], + "top_level": { + "name": "picoshare" + }, + "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": "picoshare-volume-0", + "service": "picoshare", + "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": [ + "picoshare" + ], + "stop_order": [ + "picoshare" + ], + "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-ps-shared-secret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "picoshare", + "environment_variable": "PS_SHARED_SECRET" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "PicoShare", + "scheme": "http", + "port": 4001, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "PicoShare administrator passphrase", + "type": "runtime-generated", + "username": "Not required", + "password": null, + "password_environment": "PS_SHARED_SECRET", + "change_required": false, + "source": "proxmenux-installer-generated" + } + ] + }, + "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": { + "runtime": { + "command": [ + "-db", + "/data/store.db" + ] + }, + "startup_healthcheck": { + "scheme": "http", + "port": 4001, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "generated_sensitive_environment": { + "PS_SHARED_SECRET": { + "strategy": "token-hex", + "bytes": 32 + } + } + }, + "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/pocket-id.json b/oci/catalog/apps/pocket-id.json new file mode 100644 index 00000000..ebb5ee0a --- /dev/null +++ b/oci/catalog/apps/pocket-id.json @@ -0,0 +1,469 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-pocket-id", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Pocket ID" + }, + "tagline": { + "en_US": "Passkey-based identity provider for self-hosted apps." + }, + "description": { + "en_US": "Run a lightweight identity provider that uses passkeys for sign-in and can provide OpenID Connect to other applications. ProxMenux creates an encryption key and sets the application URL to this container address." + }, + "category": "security", + "category_label": "Authentication & Security", + "author": "pocket-id", + "developer": "pocket-id", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/pocket-id.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 1411, + "path": "/" + }, + "website": "https://pocket-id.org/", + "documentation": "https://pocket-id.org/docs/setup/installation", + "repository": "https://github.com/pocket-id/pocket-id", + "tips": [ + "Pocket ID requires HTTPS for passkeys except when opened at localhost. Set up a domain and HTTPS reverse proxy before using passkey sign-in.", + "Keep the generated encryption key with your backups: it is needed to decrypt stored signing keys." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "pocket-id", + "repository": "https://github.com/pocket-id/pocket-id", + "revision": "32c4d7f7a4853b503752d00163074aa4440ae297", + "image_repository_url": "https://github.com/pocket-id/pocket-id/pkgs/container/pocket-id", + "readme_pushed_at": "", + "compose_sha256": "bd2a769f35c3c89402721ab55044a332ee165cb2fd37d166529bd76ce37fb1b5", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/pocket-id/pocket-id" + }, + "container_contract": { + "service_name": "pocket-id", + "container_name": "pocket-id", + "image": { + "reference": "ghcr.io/pocket-id/pocket-id:latest", + "registry": "ghcr.io", + "repository": "pocket-id/pocket-id", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "APP_URL", + "example": null, + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "container_address": "http://@CONTAINER_ADDRESS@:1411" + }, + { + "name": "ENCRYPTION_KEY", + "example": "${GENERATED_ENCRYPTION_KEY}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/data", + "compose_source_example": "./pocket-id-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "ports": [ + { + "container_port": 1411, + "published_example": 1411, + "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\": \"pocket-id\",\n \"services\": {\n \"pocket-id\": {\n \"image\": \"ghcr.io/pocket-id/pocket-id:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"1411:1411/tcp\"\n ],\n \"volumes\": [\n \"./pocket-id-data:/app/data\"\n ],\n \"environment\": {\n \"APP_URL\": \"http://@CONTAINER_ADDRESS@:1411\",\n \"ENCRYPTION_KEY\": \"${GENERATED_ENCRYPTION_KEY}\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "pocket-id", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "pocket-id", + "service_count": 1, + "services": [ + { + "name": "pocket-id", + "image": "ghcr.io/pocket-id/pocket-id:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/pocket-id/pocket-id:latest", + "restart": "unless-stopped", + "ports": [ + "1411:1411/tcp" + ], + "volumes": [ + "./pocket-id-data:/app/data" + ], + "environment": { + "APP_URL": "http://@CONTAINER_ADDRESS@:1411", + "ENCRYPTION_KEY": "${GENERATED_ENCRYPTION_KEY}" + } + } + } + ], + "top_level": { + "name": "pocket-id" + }, + "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": "pocket-id-volume-0", + "service": "pocket-id", + "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": [ + "pocket-id" + ], + "stop_order": [ + "pocket-id" + ], + "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-encryption-key", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "pocket-id", + "environment_variable": "ENCRYPTION_KEY" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Pocket ID", + "scheme": "http", + "port": 1411, + "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": 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": 1411, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "generated_sensitive_environment": { + "ENCRYPTION_KEY": { + "strategy": "token-hex", + "bytes": 32 + } + } + }, + "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/sftpgo.json b/oci/catalog/apps/sftpgo.json new file mode 100644 index 00000000..9591dade --- /dev/null +++ b/oci/catalog/apps/sftpgo.json @@ -0,0 +1,468 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-sftpgo", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "SFTPGo" + }, + "tagline": { + "en_US": "File transfer server with SFTP and a web administration interface." + }, + "description": { + "en_US": "Manage files and users through a web interface, and transfer files over SFTP. The first administrator is created through the setup page; the profile persists SFTPGo configuration and file storage." + }, + "category": "sharing", + "category_label": "Files & Sharing", + "author": "drakkan", + "developer": "drakkan", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/sftpgo.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/web/admin" + }, + "website": "https://sftpgo.com/", + "documentation": "https://docs.sftpgo.com/latest/docker/", + "repository": "https://github.com/drakkan/sftpgo", + "tips": [ + "Open /web/admin and create the initial administrator. SFTP listens on TCP port 2022.", + "The data directory is /srv/sftpgo; application settings and its SQLite database are stored under /var/lib/sftpgo." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "drakkan", + "repository": "https://github.com/drakkan/sftpgo", + "revision": "9712163e617744ed244da6d4d2a84cdbd25d8888", + "image_repository_url": "https://github.com/drakkan/sftpgo/pkgs/container/sftpgo", + "readme_pushed_at": "", + "compose_sha256": "46b9585c1f2db3df9c0b4aa1a7453e08897b016f8871a04ff587aaced0bb9ac4", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/drakkan/sftpgo" + }, + "container_contract": { + "service_name": "sftpgo", + "container_name": "sftpgo", + "image": { + "reference": "ghcr.io/drakkan/sftpgo:latest", + "registry": "ghcr.io", + "repository": "drakkan/sftpgo", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [], + "volumes": [ + { + "id": "volume-0", + "container_path": "/srv/sftpgo", + "compose_source_example": "./sftpgo-files", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 4 + } + }, + { + "id": "volume-1", + "container_path": "/var/lib/sftpgo", + "compose_source_example": "./sftpgo-state", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "ports": [ + { + "container_port": 8080, + "published_example": 8080, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + }, + { + "container_port": 2022, + "published_example": 2022, + "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\": \"sftpgo\",\n \"services\": {\n \"sftpgo\": {\n \"image\": \"ghcr.io/drakkan/sftpgo:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080/tcp\",\n \"2022:2022/tcp\"\n ],\n \"volumes\": [\n \"./sftpgo-files:/srv/sftpgo\",\n \"./sftpgo-state:/var/lib/sftpgo\"\n ]\n }\n }\n}" + }, + "compose_stack": { + "project_name": "sftpgo", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "sftpgo", + "service_count": 1, + "services": [ + { + "name": "sftpgo", + "image": "ghcr.io/drakkan/sftpgo:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/drakkan/sftpgo:latest", + "restart": "unless-stopped", + "ports": [ + "8080:8080/tcp", + "2022:2022/tcp" + ], + "volumes": [ + "./sftpgo-files:/srv/sftpgo", + "./sftpgo-state:/var/lib/sftpgo" + ] + } + } + ], + "top_level": { + "name": "sftpgo" + }, + "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": "sftpgo-volume-0", + "service": "sftpgo", + "container_path": "/srv/sftpgo", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "sftpgo-volume-1", + "service": "sftpgo", + "container_path": "/var/lib/sftpgo", + "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": [ + "sftpgo" + ], + "stop_order": [ + "sftpgo" + ], + "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": "SFTPGo", + "scheme": "http", + "port": 8080, + "path": "/web/admin", + "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": "/web/admin", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + } + }, + "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/silverbullet.json b/oci/catalog/apps/silverbullet.json new file mode 100644 index 00000000..bbd14206 --- /dev/null +++ b/oci/catalog/apps/silverbullet.json @@ -0,0 +1,456 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-silverbullet", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "SilverBullet" + }, + "tagline": { + "en_US": "Markdown-based personal knowledge management." + }, + "description": { + "en_US": "Create and connect Markdown notes in a self-hosted, wiki-like workspace. The required SB_USER variable enables the initial single-user login, and /data holds the workspace." + }, + "category": "documents", + "category_label": "Documents & Notes", + "author": "silverbulletmd", + "developer": "silverbulletmd", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/silverbullet.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 3000, + "path": "/" + }, + "website": "https://silverbullet.md/", + "documentation": "https://docs.silverbullet.md/Install/Docker", + "repository": "https://github.com/silverbulletmd/silverbullet", + "tips": [ + "Enter the login as username:password when installing. This profile enables SilverBullet single-space authentication." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "silverbulletmd", + "repository": "https://github.com/silverbulletmd/silverbullet", + "revision": "3ee1f82deedf4d083bb1adde43f131448082abc6", + "image_repository_url": "https://github.com/silverbulletmd/silverbullet/pkgs/container/silverbullet", + "readme_pushed_at": "", + "compose_sha256": "3cdfaf590e6db6122eee078b0c5ce083e3a83322ca8e53d6a4a80d97bb653c7c", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/silverbulletmd/silverbullet" + }, + "container_contract": { + "service_name": "silverbullet", + "container_name": "silverbullet", + "image": { + "reference": "ghcr.io/silverbulletmd/silverbullet:latest", + "registry": "ghcr.io", + "repository": "silverbulletmd/silverbullet", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "SB_USER", + "example": "${SB_USER}", + "required": true, + "sensitive": true, + "source": "upstream-documentation", + "prompt": "SilverBullet login in username:password format" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "./silverbullet-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": 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": "{\n \"name\": \"silverbullet\",\n \"services\": {\n \"silverbullet\": {\n \"image\": \"ghcr.io/silverbulletmd/silverbullet:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"3000:3000/tcp\"\n ],\n \"volumes\": [\n \"./silverbullet-data:/data\"\n ],\n \"environment\": {\n \"SB_USER\": \"${SB_USER}\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "silverbullet", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "silverbullet", + "service_count": 1, + "services": [ + { + "name": "silverbullet", + "image": "ghcr.io/silverbulletmd/silverbullet:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/silverbulletmd/silverbullet:latest", + "restart": "unless-stopped", + "ports": [ + "3000:3000/tcp" + ], + "volumes": [ + "./silverbullet-data:/data" + ], + "environment": { + "SB_USER": "${SB_USER}" + } + } + } + ], + "top_level": { + "name": "silverbullet" + }, + "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": "silverbullet-volume-0", + "service": "silverbullet", + "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": [ + "silverbullet" + ], + "stop_order": [ + "silverbullet" + ], + "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": "SilverBullet", + "scheme": "http", + "port": 3000, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "SilverBullet login", + "type": "configured-or-installer-generated", + "username": "Set in SB_USER", + "password": null, + "password_environment": "SB_USER", + "change_required": false, + "source": "https://docs.silverbullet.md/Authentication" + } + ] + }, + "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": 3000, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "minimum_secret_length": { + "SB_USER": 12 + } + }, + "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/wallos.json b/oci/catalog/apps/wallos.json new file mode 100644 index 00000000..c3a31f07 --- /dev/null +++ b/oci/catalog/apps/wallos.json @@ -0,0 +1,470 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-wallos", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Wallos" + }, + "tagline": { + "en_US": "Track subscriptions and recurring expenses." + }, + "description": { + "en_US": "Keep a private list of subscriptions, payment dates and recurring costs in a web interface. Create the first administrator account from the browser after installation." + }, + "category": "finance", + "category_label": "Finance & Budgeting", + "author": "ellite", + "developer": "ellite", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/wallos.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 80, + "path": "/" + }, + "website": "https://wallosapp.com/", + "documentation": "https://github.com/ellite/Wallos#readme", + "repository": "https://github.com/ellite/Wallos", + "tips": [ + "The first account is created from the web interface. Change the timezone if this server uses a different one." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "ellite", + "repository": "https://github.com/ellite/Wallos", + "revision": "d799b8faac0c81d0993f52a6a8b5cfed2902086c", + "image_repository_url": "https://hub.docker.com/r/bellamy/wallos", + "readme_pushed_at": "", + "compose_sha256": "66db6a6a29b28790185681316e88fde92cb5a39471cdd2c874ae445ebe1cf530", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/ellite/Wallos" + }, + "container_contract": { + "service_name": "wallos", + "container_name": "wallos", + "image": { + "reference": "bellamy/wallos:latest", + "registry": "docker.io", + "repository": "bellamy/wallos", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "TZ", + "example": "Europe/Madrid", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/var/www/html/db", + "compose_source_example": "./wallos-db", + "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": "/var/www/html/images/uploads/logos", + "compose_source_example": "./wallos-logos", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "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": "{\n \"name\": \"wallos\",\n \"services\": {\n \"wallos\": {\n \"image\": \"bellamy/wallos:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"80:80/tcp\"\n ],\n \"volumes\": [\n \"./wallos-db:/var/www/html/db\",\n \"./wallos-logos:/var/www/html/images/uploads/logos\"\n ],\n \"environment\": {\n \"TZ\": \"Europe/Madrid\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "wallos", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "wallos", + "service_count": 1, + "services": [ + { + "name": "wallos", + "image": "bellamy/wallos:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "bellamy/wallos:latest", + "restart": "unless-stopped", + "ports": [ + "80:80/tcp" + ], + "volumes": [ + "./wallos-db:/var/www/html/db", + "./wallos-logos:/var/www/html/images/uploads/logos" + ], + "environment": { + "TZ": "Europe/Madrid" + } + } + } + ], + "top_level": { + "name": "wallos" + }, + "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": "wallos-volume-0", + "service": "wallos", + "container_path": "/var/www/html/db", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "wallos-volume-1", + "service": "wallos", + "container_path": "/var/www/html/images/uploads/logos", + "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": [ + "wallos" + ], + "stop_order": [ + "wallos" + ], + "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": "Wallos", + "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": 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": 80, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + } + }, + "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/categories.json b/oci/catalog/categories.json index 2a6cacb7..957d2285 100644 --- a/oci/catalog/categories.json +++ b/oci/catalog/categories.json @@ -229,6 +229,16 @@ "your_spotify": "media", "zen": "desktop", "znc": "communication", - "zotero": "documents" + "zotero": "documents", + "ntfy": "messaging", + "gotify": "messaging", + "gatus": "monitoring", + "sftpgo": "sharing", + "picoshare": "sharing", + "owncast": "communication", + "silverbullet": "documents", + "wallos": "finance", + "pocket-id": "security", + "lldap": "security" } } diff --git a/oci/catalog/curated/gatus.json b/oci/catalog/curated/gatus.json new file mode 100644 index 00000000..58e9092b --- /dev/null +++ b/oci/catalog/curated/gatus.json @@ -0,0 +1,450 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-gatus", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Gatus" + }, + "tagline": { + "en_US": "Health dashboards and endpoint monitoring." + }, + "description": { + "en_US": "Monitor HTTP endpoints and display their status in a lightweight dashboard. The profile supplies a minimal configuration, stores history in SQLite and checks the local Gatus page." + }, + "category": "monitoring", + "category_label": "Monitoring & Analytics", + "author": "TwiN", + "developer": "TwiN", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/gatus.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/" + }, + "website": "https://gatus.io/", + "documentation": "https://github.com/TwiN/gatus#configuration", + "repository": "https://github.com/TwiN/gatus", + "tips": [ + "Edit /config/config.yaml to add the endpoints you want to monitor. This profile stores the status history in /data/gatus.db." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "TwiN", + "repository": "https://github.com/TwiN/gatus", + "revision": "fae6edef2f288c47c294fbe55453b92390551869", + "image_repository_url": "https://github.com/TwiN/gatus/pkgs/container/gatus", + "readme_pushed_at": "", + "compose_sha256": "db8d4362f0ff0c7bd9f43de656bee2c71eab1a3fad3551d1853b5e6b09d01109", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/TwiN/gatus" + }, + "container_contract": { + "service_name": "gatus", + "container_name": "gatus", + "image": { + "reference": "ghcr.io/twin/gatus:latest", + "registry": "ghcr.io", + "repository": "twin/gatus", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "GATUS_CONFIG_PATH", + "example": "/config/config.yaml", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "./gatus-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "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\": \"gatus\",\n \"services\": {\n \"gatus\": {\n \"image\": \"ghcr.io/twin/gatus:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080/tcp\"\n ],\n \"volumes\": [\n \"./gatus-data:/data\"\n ],\n \"environment\": {\n \"GATUS_CONFIG_PATH\": \"/config/config.yaml\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "gatus", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "gatus", + "service_count": 1, + "services": [ + { + "name": "gatus", + "image": "ghcr.io/twin/gatus:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/twin/gatus:latest", + "restart": "unless-stopped", + "ports": [ + "8080:8080/tcp" + ], + "volumes": [ + "./gatus-data:/data" + ], + "environment": { + "GATUS_CONFIG_PATH": "/config/config.yaml" + } + } + } + ], + "top_level": { + "name": "gatus" + }, + "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": "gatus-volume-0", + "service": "gatus", + "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": [ + "gatus" + ], + "stop_order": [ + "gatus" + ], + "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": "Gatus", + "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": 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": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "generated_files": [ + { + "container_path": "/config/config.yaml", + "mode": "0644", + "owner": "mapped-root", + "content": "storage:\n type: sqlite\n path: /data/gatus.db\nendpoints:\n - name: Gatus\n group: local\n url: http://127.0.0.1:8080/\n interval: 1m\n conditions:\n - \"[STATUS] == 200\"\n" + } + ] + }, + "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/gotify.json b/oci/catalog/curated/gotify.json new file mode 100644 index 00000000..dee20920 --- /dev/null +++ b/oci/catalog/curated/gotify.json @@ -0,0 +1,477 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-gotify", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Gotify" + }, + "tagline": { + "en_US": "Self-hosted push notification server." + }, + "description": { + "en_US": "Receive notifications from scripts and services through Gotify clients and its web interface. ProxMenux generates a unique initial administrator password." + }, + "category": "messaging", + "category_label": "Messaging & Notifications", + "author": "gotify", + "developer": "gotify", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/gotify.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 80, + "path": "/" + }, + "website": "https://gotify.net/", + "documentation": "https://gotify.net/docs/install", + "repository": "https://github.com/gotify/server", + "tips": [ + "The generated administrator password is shown when the installation finishes. Save it before leaving the terminal." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "gotify", + "repository": "https://github.com/gotify/server", + "revision": "dd06dc77190696e93d3297b9a733711516fb17d9", + "image_repository_url": "https://hub.docker.com/r/gotify/server", + "readme_pushed_at": "", + "compose_sha256": "68a4d6d32829ca5944e1bbdf79f0572e981dfcb218e1f14610feedc6b46f675d", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/gotify/server" + }, + "container_contract": { + "service_name": "gotify", + "container_name": "gotify", + "image": { + "reference": "gotify/server:latest", + "registry": "docker.io", + "repository": "gotify/server", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "GOTIFY_DEFAULTUSER_NAME", + "example": "admin", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "GOTIFY_DEFAULTUSER_PASS", + "example": "${GENERATED_GOTIFY_ADMIN_PASSWORD}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/data", + "compose_source_example": "./gotify-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": 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": "{\n \"name\": \"gotify\",\n \"services\": {\n \"gotify\": {\n \"image\": \"gotify/server:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"80:80/tcp\"\n ],\n \"volumes\": [\n \"./gotify-data:/app/data\"\n ],\n \"environment\": {\n \"GOTIFY_DEFAULTUSER_NAME\": \"admin\",\n \"GOTIFY_DEFAULTUSER_PASS\": \"${GENERATED_GOTIFY_ADMIN_PASSWORD}\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "gotify", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "gotify", + "service_count": 1, + "services": [ + { + "name": "gotify", + "image": "gotify/server:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "gotify/server:latest", + "restart": "unless-stopped", + "ports": [ + "80:80/tcp" + ], + "volumes": [ + "./gotify-data:/app/data" + ], + "environment": { + "GOTIFY_DEFAULTUSER_NAME": "admin", + "GOTIFY_DEFAULTUSER_PASS": "${GENERATED_GOTIFY_ADMIN_PASSWORD}" + } + } + } + ], + "top_level": { + "name": "gotify" + }, + "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": "gotify-volume-0", + "service": "gotify", + "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": [ + "gotify" + ], + "stop_order": [ + "gotify" + ], + "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-gotify-defaultuser-pass", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "gotify", + "environment_variable": "GOTIFY_DEFAULTUSER_PASS" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Gotify", + "scheme": "http", + "port": 80, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "Gotify administrator", + "type": "runtime-generated", + "username": "admin", + "password": null, + "password_environment": "GOTIFY_DEFAULTUSER_PASS", + "change_required": true, + "source": "proxmenux-installer-generated" + } + ] + }, + "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": 80, + "path": "/health", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "generated_sensitive_environment": { + "GOTIFY_DEFAULTUSER_PASS": { + "strategy": "token-hex", + "bytes": 24 + } + } + }, + "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/lldap.json b/oci/catalog/curated/lldap.json new file mode 100644 index 00000000..db682ba9 --- /dev/null +++ b/oci/catalog/curated/lldap.json @@ -0,0 +1,530 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-lldap", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "LLDAP" + }, + "tagline": { + "en_US": "Lightweight LDAP server with a web administration UI." + }, + "description": { + "en_US": "Provide LDAP authentication and manage users and groups through a compact web interface. ProxMenux generates the initial administrator password and the secrets required by LLDAP." + }, + "category": "security", + "category_label": "Authentication & Security", + "author": "lldap", + "developer": "lldap", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/lldap.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 17170, + "path": "/" + }, + "website": "https://github.com/lldap/lldap", + "documentation": "https://github.com/lldap/lldap/blob/main/docs/install.md", + "repository": "https://github.com/lldap/lldap", + "tips": [ + "The initial LDAP administrator is named admin. The generated password is used by the web interface and LDAP clients.", + "Replace dc=example,dc=com with your own directory base before connecting production clients. LDAP listens on TCP port 3890." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "lldap", + "repository": "https://github.com/lldap/lldap", + "revision": "99c510a7a603df1cdaca00a9ffff95ec73b296eb", + "image_repository_url": "https://hub.docker.com/r/lldap/lldap", + "readme_pushed_at": "", + "compose_sha256": "f722971b4fcc248da42e9cff50fdbfc85f5ba05354249572ec21b0152322229c", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/lldap/lldap" + }, + "container_contract": { + "service_name": "lldap", + "container_name": "lldap", + "image": { + "reference": "lldap/lldap:stable", + "registry": "docker.io", + "repository": "lldap/lldap", + "tag": "stable", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "LLDAP_JWT_SECRET", + "example": "${GENERATED_LLDAP_JWT_SECRET}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "LLDAP_KEY_SEED", + "example": "${GENERATED_LLDAP_KEY_SEED}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + }, + { + "name": "LLDAP_LDAP_BASE_DN", + "example": "dc=example,dc=com", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "LLDAP_LDAP_USER_PASS", + "example": "${GENERATED_LLDAP_LDAP_USER_PASS}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "./lldap-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "ports": [ + { + "container_port": 17170, + "published_example": 17170, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + }, + { + "container_port": 3890, + "published_example": 3890, + "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\": \"lldap\",\n \"services\": {\n \"lldap\": {\n \"image\": \"lldap/lldap:stable\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"17170:17170/tcp\",\n \"3890:3890/tcp\"\n ],\n \"volumes\": [\n \"./lldap-data:/data\"\n ],\n \"environment\": {\n \"LLDAP_JWT_SECRET\": \"${GENERATED_LLDAP_JWT_SECRET}\",\n \"LLDAP_KEY_SEED\": \"${GENERATED_LLDAP_KEY_SEED}\",\n \"LLDAP_LDAP_BASE_DN\": \"dc=example,dc=com\",\n \"LLDAP_LDAP_USER_PASS\": \"${GENERATED_LLDAP_LDAP_USER_PASS}\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "lldap", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "lldap", + "service_count": 1, + "services": [ + { + "name": "lldap", + "image": "lldap/lldap:stable", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "lldap/lldap:stable", + "restart": "unless-stopped", + "ports": [ + "17170:17170/tcp", + "3890:3890/tcp" + ], + "volumes": [ + "./lldap-data:/data" + ], + "environment": { + "LLDAP_JWT_SECRET": "${GENERATED_LLDAP_JWT_SECRET}", + "LLDAP_KEY_SEED": "${GENERATED_LLDAP_KEY_SEED}", + "LLDAP_LDAP_BASE_DN": "dc=example,dc=com", + "LLDAP_LDAP_USER_PASS": "${GENERATED_LLDAP_LDAP_USER_PASS}" + } + } + } + ], + "top_level": { + "name": "lldap" + }, + "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": "lldap-volume-0", + "service": "lldap", + "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": [ + "lldap" + ], + "stop_order": [ + "lldap" + ], + "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-lldap-ldap-jwt-secret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "lldap", + "environment_variable": "LLDAP_JWT_SECRET" + } + ] + }, + { + "id": "generated-lldap-key-seed", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "lldap", + "environment_variable": "LLDAP_KEY_SEED" + } + ] + }, + { + "id": "generated-lldap-ldap-user-pass", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "lldap", + "environment_variable": "LLDAP_LDAP_USER_PASS" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "LLDAP", + "scheme": "http", + "port": 17170, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "LLDAP administrator", + "type": "runtime-generated", + "username": "admin", + "password": null, + "password_environment": "LLDAP_LDAP_USER_PASS", + "change_required": true, + "source": "proxmenux-installer-generated" + } + ] + }, + "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": 17170, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "generated_sensitive_environment": { + "LLDAP_JWT_SECRET": { + "strategy": "token-hex", + "bytes": 32 + }, + "LLDAP_KEY_SEED": { + "strategy": "token-hex", + "bytes": 32 + }, + "LLDAP_LDAP_USER_PASS": { + "strategy": "token-hex", + "bytes": 24 + } + } + }, + "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/ntfy.json b/oci/catalog/curated/ntfy.json new file mode 100644 index 00000000..b11afa0b --- /dev/null +++ b/oci/catalog/curated/ntfy.json @@ -0,0 +1,475 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-ntfy", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "ntfy" + }, + "tagline": { + "en_US": "Self-hosted push notifications with a web interface." + }, + "description": { + "en_US": "Send and receive push notifications through simple HTTP requests, mobile apps and a web interface. The profile stores the message cache and enables authentication with anonymous access denied by default." + }, + "category": "messaging", + "category_label": "Messaging & Notifications", + "author": "binwiederhier", + "developer": "binwiederhier", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/ntfy.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 80, + "path": "/" + }, + "website": "https://ntfy.sh/", + "documentation": "https://docs.ntfy.sh/install/", + "repository": "https://github.com/binwiederhier/ntfy", + "tips": [ + "Anonymous access is denied by default. Add users and topic permissions with the ntfy user command before using the server.", + "Use HTTPS before sending credentials over a network you do not fully trust." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "binwiederhier", + "repository": "https://github.com/binwiederhier/ntfy", + "revision": "9f1d2ea44e412fef4552382a47479fec1984e5ea", + "image_repository_url": "https://hub.docker.com/r/binwiederhier/ntfy", + "readme_pushed_at": "", + "compose_sha256": "6a34e5257e07c3cc83381088ea0dbcbde22e3cc9b927bd09df42dd617e211838", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/binwiederhier/ntfy" + }, + "container_contract": { + "service_name": "ntfy", + "container_name": "ntfy", + "image": { + "reference": "binwiederhier/ntfy:latest", + "registry": "docker.io", + "repository": "binwiederhier/ntfy", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "NTFY_CACHE_FILE", + "example": "/var/cache/ntfy/cache.db", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "NTFY_AUTH_FILE", + "example": "/var/cache/ntfy/user.db", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "NTFY_AUTH_DEFAULT_ACCESS", + "example": "deny-all", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "NTFY_ENABLE_LOGIN", + "example": "true", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/var/cache/ntfy", + "compose_source_example": "./ntfy-cache", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "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": "{\n \"name\": \"ntfy\",\n \"services\": {\n \"ntfy\": {\n \"image\": \"binwiederhier/ntfy:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"80:80/tcp\"\n ],\n \"volumes\": [\n \"./ntfy-cache:/var/cache/ntfy\"\n ],\n \"environment\": {\n \"NTFY_CACHE_FILE\": \"/var/cache/ntfy/cache.db\",\n \"NTFY_AUTH_FILE\": \"/var/cache/ntfy/user.db\",\n \"NTFY_AUTH_DEFAULT_ACCESS\": \"deny-all\",\n \"NTFY_ENABLE_LOGIN\": \"true\"\n },\n \"command\": [\n \"serve\"\n ]\n }\n }\n}" + }, + "compose_stack": { + "project_name": "ntfy", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "ntfy", + "service_count": 1, + "services": [ + { + "name": "ntfy", + "image": "binwiederhier/ntfy:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "binwiederhier/ntfy:latest", + "restart": "unless-stopped", + "ports": [ + "80:80/tcp" + ], + "volumes": [ + "./ntfy-cache:/var/cache/ntfy" + ], + "environment": { + "NTFY_CACHE_FILE": "/var/cache/ntfy/cache.db", + "NTFY_AUTH_FILE": "/var/cache/ntfy/user.db", + "NTFY_AUTH_DEFAULT_ACCESS": "deny-all", + "NTFY_ENABLE_LOGIN": "true" + }, + "command": [ + "serve" + ] + } + } + ], + "top_level": { + "name": "ntfy" + }, + "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": "ntfy-volume-0", + "service": "ntfy", + "container_path": "/var/cache/ntfy", + "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": [ + "ntfy" + ], + "stop_order": [ + "ntfy" + ], + "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": "ntfy", + "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": 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": { + "runtime": { + "command": [ + "serve" + ] + }, + "startup_healthcheck": { + "scheme": "http", + "port": 80, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + } + }, + "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/owncast.json b/oci/catalog/curated/owncast.json new file mode 100644 index 00000000..2f0542bc --- /dev/null +++ b/oci/catalog/curated/owncast.json @@ -0,0 +1,448 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-owncast", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Owncast" + }, + "tagline": { + "en_US": "Self-hosted live streaming and chat." + }, + "description": { + "en_US": "Host a live stream with an RTMP ingest endpoint and a public viewing page. The profile generates an administrator password and stream key, then passes both to Owncast at startup." + }, + "category": "communication", + "category_label": "Communication & Community", + "author": "owncast", + "developer": "owncast", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/owncast.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/" + }, + "website": "https://owncast.online/", + "documentation": "https://owncast.online/docs/configuration/runtime-flags/", + "repository": "https://github.com/owncast/owncast", + "tips": [ + "Owncast defaults to admin / abc123 for the dashboard and abc123 for the stream key. Change both immediately before exposing the service.", + "The administrator password is applied when Owncast starts; a password changed in its UI may be reset to the generated value on the next restart.", + "Streaming software connects to rtmp://:1935/live with the generated stream key." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "owncast", + "repository": "https://github.com/owncast/owncast", + "revision": "d724434a9313bf7f022822277046200e32669c59", + "image_repository_url": "https://hub.docker.com/r/owncast/owncast", + "readme_pushed_at": "", + "compose_sha256": "7e7f5ae85ee5d0f34d216bcdbc28df2a9dec9b0607e50de8c3afef3414f14420", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/owncast/owncast" + }, + "container_contract": { + "service_name": "owncast", + "container_name": "owncast", + "image": { + "reference": "owncast/owncast:latest", + "registry": "docker.io", + "repository": "owncast/owncast", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/data", + "compose_source_example": "./owncast-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" + }, + { + "container_port": 1935, + "published_example": 1935, + "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\": \"owncast\",\n \"services\": {\n \"owncast\": {\n \"image\": \"owncast/owncast:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080/tcp\",\n \"1935:1935/tcp\"\n ],\n \"volumes\": [\n \"./owncast-data:/app/data\"\n ]\n }\n }\n}" + }, + "compose_stack": { + "project_name": "owncast", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "owncast", + "service_count": 1, + "services": [ + { + "name": "owncast", + "image": "owncast/owncast:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "owncast/owncast:latest", + "restart": "unless-stopped", + "ports": [ + "8080:8080/tcp", + "1935:1935/tcp" + ], + "volumes": [ + "./owncast-data:/app/data" + ] + } + } + ], + "top_level": { + "name": "owncast" + }, + "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": "owncast-volume-0", + "service": "owncast", + "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": [ + "owncast" + ], + "stop_order": [ + "owncast" + ], + "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", + "dependency_start_and_stop_order" + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Owncast", + "scheme": "http", + "port": 8080, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "Owncast administrator", + "type": "static-default", + "username": "admin", + "password": "abc123", + "change_required": true, + "source": "upstream-documentation" + } + ] + }, + "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": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + } + }, + "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/picoshare.json b/oci/catalog/curated/picoshare.json new file mode 100644 index 00000000..0c627a42 --- /dev/null +++ b/oci/catalog/curated/picoshare.json @@ -0,0 +1,487 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-picoshare", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "PicoShare" + }, + "tagline": { + "en_US": "Simple self-hosted file sharing." + }, + "description": { + "en_US": "Upload files and share direct links through a small web interface. ProxMenux generates the required administrator passphrase and stores PicoShare data in a persistent volume." + }, + "category": "sharing", + "category_label": "Files & Sharing", + "author": "mtlynch", + "developer": "mtlynch", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/picoshare.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 4001, + "path": "/" + }, + "website": "https://github.com/mtlynch/picoshare", + "documentation": "https://github.com/mtlynch/picoshare#readme", + "repository": "https://github.com/mtlynch/picoshare", + "tips": [ + "The generated administrator passphrase is shown after installation. Keep it somewhere safe." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "mtlynch", + "repository": "https://github.com/mtlynch/picoshare", + "revision": "3b853b3cc208c2da8acb30426ce9eb9a2ef7e25a", + "image_repository_url": "https://hub.docker.com/r/mtlynch/picoshare", + "readme_pushed_at": "", + "compose_sha256": "c6d32862369ec88e58cb756f030b82b8bd85f4c2f6772d54c9a97f7021ae73b2", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/mtlynch/picoshare" + }, + "container_contract": { + "service_name": "picoshare", + "container_name": "picoshare", + "image": { + "reference": "mtlynch/picoshare:latest", + "registry": "docker.io", + "repository": "mtlynch/picoshare", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "PORT", + "example": "4001", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + }, + { + "name": "PS_SHARED_SECRET", + "example": "${GENERATED_PS_SHARED_SECRET}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "./picoshare-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": 4001, + "published_example": 4001, + "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\": \"picoshare\",\n \"services\": {\n \"picoshare\": {\n \"image\": \"mtlynch/picoshare:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"4001:4001/tcp\"\n ],\n \"volumes\": [\n \"./picoshare-data:/data\"\n ],\n \"environment\": {\n \"PORT\": \"4001\",\n \"PS_SHARED_SECRET\": \"${GENERATED_PS_SHARED_SECRET}\"\n },\n \"command\": [\n \"-db\",\n \"/data/store.db\"\n ]\n }\n }\n}" + }, + "compose_stack": { + "project_name": "picoshare", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "picoshare", + "service_count": 1, + "services": [ + { + "name": "picoshare", + "image": "mtlynch/picoshare:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "mtlynch/picoshare:latest", + "restart": "unless-stopped", + "ports": [ + "4001:4001/tcp" + ], + "volumes": [ + "./picoshare-data:/data" + ], + "environment": { + "PORT": "4001", + "PS_SHARED_SECRET": "${GENERATED_PS_SHARED_SECRET}" + }, + "command": [ + "-db", + "/data/store.db" + ] + } + } + ], + "top_level": { + "name": "picoshare" + }, + "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": "picoshare-volume-0", + "service": "picoshare", + "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": [ + "picoshare" + ], + "stop_order": [ + "picoshare" + ], + "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-ps-shared-secret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "picoshare", + "environment_variable": "PS_SHARED_SECRET" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "PicoShare", + "scheme": "http", + "port": 4001, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "PicoShare administrator passphrase", + "type": "runtime-generated", + "username": "Not required", + "password": null, + "password_environment": "PS_SHARED_SECRET", + "change_required": false, + "source": "proxmenux-installer-generated" + } + ] + }, + "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": { + "runtime": { + "command": [ + "-db", + "/data/store.db" + ] + }, + "startup_healthcheck": { + "scheme": "http", + "port": 4001, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "generated_sensitive_environment": { + "PS_SHARED_SECRET": { + "strategy": "token-hex", + "bytes": 32 + } + } + }, + "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/pocket-id.json b/oci/catalog/curated/pocket-id.json new file mode 100644 index 00000000..ebb5ee0a --- /dev/null +++ b/oci/catalog/curated/pocket-id.json @@ -0,0 +1,469 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-pocket-id", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Pocket ID" + }, + "tagline": { + "en_US": "Passkey-based identity provider for self-hosted apps." + }, + "description": { + "en_US": "Run a lightweight identity provider that uses passkeys for sign-in and can provide OpenID Connect to other applications. ProxMenux creates an encryption key and sets the application URL to this container address." + }, + "category": "security", + "category_label": "Authentication & Security", + "author": "pocket-id", + "developer": "pocket-id", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/pocket-id.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 1411, + "path": "/" + }, + "website": "https://pocket-id.org/", + "documentation": "https://pocket-id.org/docs/setup/installation", + "repository": "https://github.com/pocket-id/pocket-id", + "tips": [ + "Pocket ID requires HTTPS for passkeys except when opened at localhost. Set up a domain and HTTPS reverse proxy before using passkey sign-in.", + "Keep the generated encryption key with your backups: it is needed to decrypt stored signing keys." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "pocket-id", + "repository": "https://github.com/pocket-id/pocket-id", + "revision": "32c4d7f7a4853b503752d00163074aa4440ae297", + "image_repository_url": "https://github.com/pocket-id/pocket-id/pkgs/container/pocket-id", + "readme_pushed_at": "", + "compose_sha256": "bd2a769f35c3c89402721ab55044a332ee165cb2fd37d166529bd76ce37fb1b5", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/pocket-id/pocket-id" + }, + "container_contract": { + "service_name": "pocket-id", + "container_name": "pocket-id", + "image": { + "reference": "ghcr.io/pocket-id/pocket-id:latest", + "registry": "ghcr.io", + "repository": "pocket-id/pocket-id", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "APP_URL", + "example": null, + "required": true, + "sensitive": false, + "source": "upstream-documentation", + "container_address": "http://@CONTAINER_ADDRESS@:1411" + }, + { + "name": "ENCRYPTION_KEY", + "example": "${GENERATED_ENCRYPTION_KEY}", + "required": true, + "sensitive": true, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/app/data", + "compose_source_example": "./pocket-id-data", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "ports": [ + { + "container_port": 1411, + "published_example": 1411, + "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\": \"pocket-id\",\n \"services\": {\n \"pocket-id\": {\n \"image\": \"ghcr.io/pocket-id/pocket-id:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"1411:1411/tcp\"\n ],\n \"volumes\": [\n \"./pocket-id-data:/app/data\"\n ],\n \"environment\": {\n \"APP_URL\": \"http://@CONTAINER_ADDRESS@:1411\",\n \"ENCRYPTION_KEY\": \"${GENERATED_ENCRYPTION_KEY}\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "pocket-id", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "pocket-id", + "service_count": 1, + "services": [ + { + "name": "pocket-id", + "image": "ghcr.io/pocket-id/pocket-id:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/pocket-id/pocket-id:latest", + "restart": "unless-stopped", + "ports": [ + "1411:1411/tcp" + ], + "volumes": [ + "./pocket-id-data:/app/data" + ], + "environment": { + "APP_URL": "http://@CONTAINER_ADDRESS@:1411", + "ENCRYPTION_KEY": "${GENERATED_ENCRYPTION_KEY}" + } + } + } + ], + "top_level": { + "name": "pocket-id" + }, + "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": "pocket-id-volume-0", + "service": "pocket-id", + "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": [ + "pocket-id" + ], + "stop_order": [ + "pocket-id" + ], + "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-encryption-key", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "pocket-id", + "environment_variable": "ENCRYPTION_KEY" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Pocket ID", + "scheme": "http", + "port": 1411, + "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": 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": 1411, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "generated_sensitive_environment": { + "ENCRYPTION_KEY": { + "strategy": "token-hex", + "bytes": 32 + } + } + }, + "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/sftpgo.json b/oci/catalog/curated/sftpgo.json new file mode 100644 index 00000000..9591dade --- /dev/null +++ b/oci/catalog/curated/sftpgo.json @@ -0,0 +1,468 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-sftpgo", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "SFTPGo" + }, + "tagline": { + "en_US": "File transfer server with SFTP and a web administration interface." + }, + "description": { + "en_US": "Manage files and users through a web interface, and transfer files over SFTP. The first administrator is created through the setup page; the profile persists SFTPGo configuration and file storage." + }, + "category": "sharing", + "category_label": "Files & Sharing", + "author": "drakkan", + "developer": "drakkan", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/sftpgo.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8080, + "path": "/web/admin" + }, + "website": "https://sftpgo.com/", + "documentation": "https://docs.sftpgo.com/latest/docker/", + "repository": "https://github.com/drakkan/sftpgo", + "tips": [ + "Open /web/admin and create the initial administrator. SFTP listens on TCP port 2022.", + "The data directory is /srv/sftpgo; application settings and its SQLite database are stored under /var/lib/sftpgo." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "drakkan", + "repository": "https://github.com/drakkan/sftpgo", + "revision": "9712163e617744ed244da6d4d2a84cdbd25d8888", + "image_repository_url": "https://github.com/drakkan/sftpgo/pkgs/container/sftpgo", + "readme_pushed_at": "", + "compose_sha256": "46b9585c1f2db3df9c0b4aa1a7453e08897b016f8871a04ff587aaced0bb9ac4", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/drakkan/sftpgo" + }, + "container_contract": { + "service_name": "sftpgo", + "container_name": "sftpgo", + "image": { + "reference": "ghcr.io/drakkan/sftpgo:latest", + "registry": "ghcr.io", + "repository": "drakkan/sftpgo", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [], + "volumes": [ + { + "id": "volume-0", + "container_path": "/srv/sftpgo", + "compose_source_example": "./sftpgo-files", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 4 + } + }, + { + "id": "volume-1", + "container_path": "/var/lib/sftpgo", + "compose_source_example": "./sftpgo-state", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "ports": [ + { + "container_port": 8080, + "published_example": 8080, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + }, + { + "container_port": 2022, + "published_example": 2022, + "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\": \"sftpgo\",\n \"services\": {\n \"sftpgo\": {\n \"image\": \"ghcr.io/drakkan/sftpgo:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080/tcp\",\n \"2022:2022/tcp\"\n ],\n \"volumes\": [\n \"./sftpgo-files:/srv/sftpgo\",\n \"./sftpgo-state:/var/lib/sftpgo\"\n ]\n }\n }\n}" + }, + "compose_stack": { + "project_name": "sftpgo", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "sftpgo", + "service_count": 1, + "services": [ + { + "name": "sftpgo", + "image": "ghcr.io/drakkan/sftpgo:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/drakkan/sftpgo:latest", + "restart": "unless-stopped", + "ports": [ + "8080:8080/tcp", + "2022:2022/tcp" + ], + "volumes": [ + "./sftpgo-files:/srv/sftpgo", + "./sftpgo-state:/var/lib/sftpgo" + ] + } + } + ], + "top_level": { + "name": "sftpgo" + }, + "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": "sftpgo-volume-0", + "service": "sftpgo", + "container_path": "/srv/sftpgo", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "sftpgo-volume-1", + "service": "sftpgo", + "container_path": "/var/lib/sftpgo", + "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": [ + "sftpgo" + ], + "stop_order": [ + "sftpgo" + ], + "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": "SFTPGo", + "scheme": "http", + "port": 8080, + "path": "/web/admin", + "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": "/web/admin", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + } + }, + "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/silverbullet.json b/oci/catalog/curated/silverbullet.json new file mode 100644 index 00000000..bbd14206 --- /dev/null +++ b/oci/catalog/curated/silverbullet.json @@ -0,0 +1,456 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-silverbullet", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "SilverBullet" + }, + "tagline": { + "en_US": "Markdown-based personal knowledge management." + }, + "description": { + "en_US": "Create and connect Markdown notes in a self-hosted, wiki-like workspace. The required SB_USER variable enables the initial single-user login, and /data holds the workspace." + }, + "category": "documents", + "category_label": "Documents & Notes", + "author": "silverbulletmd", + "developer": "silverbulletmd", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/silverbullet.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 3000, + "path": "/" + }, + "website": "https://silverbullet.md/", + "documentation": "https://docs.silverbullet.md/Install/Docker", + "repository": "https://github.com/silverbulletmd/silverbullet", + "tips": [ + "Enter the login as username:password when installing. This profile enables SilverBullet single-space authentication." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "silverbulletmd", + "repository": "https://github.com/silverbulletmd/silverbullet", + "revision": "3ee1f82deedf4d083bb1adde43f131448082abc6", + "image_repository_url": "https://github.com/silverbulletmd/silverbullet/pkgs/container/silverbullet", + "readme_pushed_at": "", + "compose_sha256": "3cdfaf590e6db6122eee078b0c5ce083e3a83322ca8e53d6a4a80d97bb653c7c", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/silverbulletmd/silverbullet" + }, + "container_contract": { + "service_name": "silverbullet", + "container_name": "silverbullet", + "image": { + "reference": "ghcr.io/silverbulletmd/silverbullet:latest", + "registry": "ghcr.io", + "repository": "silverbulletmd/silverbullet", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "SB_USER", + "example": "${SB_USER}", + "required": true, + "sensitive": true, + "source": "upstream-documentation", + "prompt": "SilverBullet login in username:password format" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "compose_source_example": "./silverbullet-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": 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": "{\n \"name\": \"silverbullet\",\n \"services\": {\n \"silverbullet\": {\n \"image\": \"ghcr.io/silverbulletmd/silverbullet:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"3000:3000/tcp\"\n ],\n \"volumes\": [\n \"./silverbullet-data:/data\"\n ],\n \"environment\": {\n \"SB_USER\": \"${SB_USER}\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "silverbullet", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "silverbullet", + "service_count": 1, + "services": [ + { + "name": "silverbullet", + "image": "ghcr.io/silverbulletmd/silverbullet:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "ghcr.io/silverbulletmd/silverbullet:latest", + "restart": "unless-stopped", + "ports": [ + "3000:3000/tcp" + ], + "volumes": [ + "./silverbullet-data:/data" + ], + "environment": { + "SB_USER": "${SB_USER}" + } + } + } + ], + "top_level": { + "name": "silverbullet" + }, + "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": "silverbullet-volume-0", + "service": "silverbullet", + "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": [ + "silverbullet" + ], + "stop_order": [ + "silverbullet" + ], + "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": "SilverBullet", + "scheme": "http", + "port": 3000, + "path": "/", + "source": "upstream-documentation" + } + ], + "credentials": [ + { + "label": "SilverBullet login", + "type": "configured-or-installer-generated", + "username": "Set in SB_USER", + "password": null, + "password_environment": "SB_USER", + "change_required": false, + "source": "https://docs.silverbullet.md/Authentication" + } + ] + }, + "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": 3000, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + }, + "minimum_secret_length": { + "SB_USER": 12 + } + }, + "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/wallos.json b/oci/catalog/curated/wallos.json new file mode 100644 index 00000000..c3a31f07 --- /dev/null +++ b/oci/catalog/curated/wallos.json @@ -0,0 +1,470 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-wallos", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Wallos" + }, + "tagline": { + "en_US": "Track subscriptions and recurring expenses." + }, + "description": { + "en_US": "Keep a private list of subscriptions, payment dates and recurring costs in a web interface. Create the first administrator account from the browser after installation." + }, + "category": "finance", + "category_label": "Finance & Budgeting", + "author": "ellite", + "developer": "ellite", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/wallos.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 80, + "path": "/" + }, + "website": "https://wallosapp.com/", + "documentation": "https://github.com/ellite/Wallos#readme", + "repository": "https://github.com/ellite/Wallos", + "tips": [ + "The first account is created from the web interface. Change the timezone if this server uses a different one." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "ellite", + "repository": "https://github.com/ellite/Wallos", + "revision": "d799b8faac0c81d0993f52a6a8b5cfed2902086c", + "image_repository_url": "https://hub.docker.com/r/bellamy/wallos", + "readme_pushed_at": "", + "compose_sha256": "66db6a6a29b28790185681316e88fde92cb5a39471cdd2c874ae445ebe1cf530", + "generated_at": "2026-10-25T18:34:06+00:00", + "readme_raw_url": "https://github.com/ellite/Wallos" + }, + "container_contract": { + "service_name": "wallos", + "container_name": "wallos", + "image": { + "reference": "bellamy/wallos:latest", + "registry": "docker.io", + "repository": "bellamy/wallos", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "TZ", + "example": "Europe/Madrid", + "required": true, + "sensitive": false, + "source": "upstream-documentation" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/var/www/html/db", + "compose_source_example": "./wallos-db", + "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": "/var/www/html/images/uploads/logos", + "compose_source_example": "./wallos-logos", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 2 + } + } + ], + "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": "{\n \"name\": \"wallos\",\n \"services\": {\n \"wallos\": {\n \"image\": \"bellamy/wallos:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"80:80/tcp\"\n ],\n \"volumes\": [\n \"./wallos-db:/var/www/html/db\",\n \"./wallos-logos:/var/www/html/images/uploads/logos\"\n ],\n \"environment\": {\n \"TZ\": \"Europe/Madrid\"\n }\n }\n }\n}" + }, + "compose_stack": { + "project_name": "wallos", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "wallos", + "service_count": 1, + "services": [ + { + "name": "wallos", + "image": "bellamy/wallos:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "bellamy/wallos:latest", + "restart": "unless-stopped", + "ports": [ + "80:80/tcp" + ], + "volumes": [ + "./wallos-db:/var/www/html/db", + "./wallos-logos:/var/www/html/images/uploads/logos" + ], + "environment": { + "TZ": "Europe/Madrid" + } + } + } + ], + "top_level": { + "name": "wallos" + }, + "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": "wallos-volume-0", + "service": "wallos", + "container_path": "/var/www/html/db", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "wallos-volume-1", + "service": "wallos", + "container_path": "/var/www/html/images/uploads/logos", + "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": [ + "wallos" + ], + "stop_order": [ + "wallos" + ], + "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": "Wallos", + "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": 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": 80, + "path": "/", + "timeout_seconds": 180, + "request_timeout_seconds": 10, + "stability_seconds": 4, + "verify_tls": true + } + }, + "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/index.json b/oci/catalog/index.json index 39961060..127d7efd 100644 --- a/oci/catalog/index.json +++ b/oci/catalog/index.json @@ -3742,6 +3742,37 @@ "hidden": false, "template_id": "image-gateway-go" }, + { + "id": "gatus", + "provider": "TwiN", + "template_family": "curated-profile", + "title": "Gatus", + "repository": "https://github.com/TwiN/gatus", + "description": "Monitor HTTP endpoints and display their status in a lightweight dashboard. The profile supplies a minimal configuration, stores history in SQLite and checks the local Gatus page.", + "website": "https://gatus.io/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/gatus.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "ghcr.io/twin/gatus:latest", + "category": "monitoring", + "category_label": "Monitoring & Analytics", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/gatus.json", + "template": "apps/gatus.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, + "requires_security_confirmation": false, + "hidden": false, + "template_id": "image-gatus" + }, { "id": "gimp", "provider": "linuxserver.io", @@ -3950,6 +3981,37 @@ "hidden": false, "template_id": "image-gopeed" }, + { + "id": "gotify", + "provider": "gotify", + "template_family": "curated-profile", + "title": "Gotify", + "repository": "https://github.com/gotify/server", + "description": "Receive notifications from scripts and services through Gotify clients and its web interface. ProxMenux generates a unique initial administrator password.", + "website": "https://gotify.net/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/gotify.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "gotify/server:latest", + "category": "messaging", + "category_label": "Messaging & Notifications", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/gotify.json", + "template": "apps/gotify.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, + "requires_security_confirmation": false, + "hidden": false, + "template_id": "image-gotify" + }, { "id": "grafana", "source_app_id": "grafana", @@ -5918,6 +5980,37 @@ "hidden": false, "template_id": "image-llamacpp" }, + { + "id": "lldap", + "provider": "lldap", + "template_family": "curated-profile", + "title": "LLDAP", + "repository": "https://github.com/lldap/lldap", + "description": "Provide LDAP authentication and manage users and groups through a compact web interface. ProxMenux generates the initial administrator password and the secrets required by LLDAP.", + "website": "https://github.com/lldap/lldap", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/lldap.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "lldap/lldap:stable", + "category": "security", + "category_label": "Authentication & Security", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/lldap.json", + "template": "apps/lldap.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, + "requires_security_confirmation": false, + "hidden": false, + "template_id": "image-lldap" + }, { "id": "lm-studio", "provider": "linuxserver.io", @@ -7537,6 +7630,37 @@ "hidden": false, "template_id": "image-node-red" }, + { + "id": "ntfy", + "provider": "binwiederhier", + "template_family": "curated-profile", + "title": "ntfy", + "repository": "https://github.com/binwiederhier/ntfy", + "description": "Send and receive push notifications through simple HTTP requests, mobile apps and a web interface. The profile stores the message cache and enables authentication with anonymous access denied by default.", + "website": "https://ntfy.sh/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/ntfy.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "binwiederhier/ntfy:latest", + "category": "messaging", + "category_label": "Messaging & Notifications", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/ntfy.json", + "template": "apps/ntfy.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, + "requires_security_confirmation": false, + "hidden": false, + "template_id": "image-ntfy" + }, { "id": "nut-server", "provider": "instantlinux", @@ -8336,6 +8460,37 @@ "hidden": false, "template_id": "image-overseerr" }, + { + "id": "owncast", + "provider": "owncast", + "template_family": "curated-profile", + "title": "Owncast", + "repository": "https://github.com/owncast/owncast", + "description": "Host a live stream with an RTMP ingest endpoint and a public viewing page. The profile generates an administrator password and stream key, then passes both to Owncast at startup.", + "website": "https://owncast.online/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/owncast.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "owncast/owncast:latest", + "category": "communication", + "category_label": "Communication & Community", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/owncast.json", + "template": "apps/owncast.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, + "requires_security_confirmation": false, + "hidden": false, + "template_id": "image-owncast" + }, { "id": "pairdrop", "provider": "linuxserver.io", @@ -8640,6 +8795,37 @@ "hidden": false, "template_id": "linuxserver-phpmyadmin" }, + { + "id": "picoshare", + "provider": "mtlynch", + "template_family": "curated-profile", + "title": "PicoShare", + "repository": "https://github.com/mtlynch/picoshare", + "description": "Upload files and share direct links through a small web interface. ProxMenux generates the required administrator passphrase and stores PicoShare data in a persistent volume.", + "website": "https://github.com/mtlynch/picoshare", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/picoshare.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "mtlynch/picoshare:latest", + "category": "sharing", + "category_label": "Files & Sharing", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/picoshare.json", + "template": "apps/picoshare.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, + "requires_security_confirmation": false, + "hidden": false, + "template_id": "image-picoshare" + }, { "id": "pidgin", "provider": "linuxserver.io", @@ -8975,6 +9161,37 @@ "hidden": false, "template_id": "image-plex-official" }, + { + "id": "pocket-id", + "provider": "pocket-id", + "template_family": "curated-profile", + "title": "Pocket ID", + "repository": "https://github.com/pocket-id/pocket-id", + "description": "Run a lightweight identity provider that uses passkeys for sign-in and can provide OpenID Connect to other applications. ProxMenux creates an encryption key and sets the application URL to this container address.", + "website": "https://pocket-id.org/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/pocket-id.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "ghcr.io/pocket-id/pocket-id:latest", + "category": "security", + "category_label": "Authentication & Security", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/pocket-id.json", + "template": "apps/pocket-id.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, + "requires_security_confirmation": false, + "hidden": false, + "template_id": "image-pocket-id" + }, { "id": "pocketbase", "source_app_id": "pocketbase", @@ -10213,6 +10430,37 @@ "hidden": false, "template_id": "image-seerr" }, + { + "id": "sftpgo", + "provider": "drakkan", + "template_family": "curated-profile", + "title": "SFTPGo", + "repository": "https://github.com/drakkan/sftpgo", + "description": "Manage files and users through a web interface, and transfer files over SFTP. The first administrator is created through the setup page; the profile persists SFTPGo configuration and file storage.", + "website": "https://sftpgo.com/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/sftpgo.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "ghcr.io/drakkan/sftpgo:latest", + "category": "sharing", + "category_label": "Files & Sharing", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/sftpgo.json", + "template": "apps/sftpgo.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, + "requires_security_confirmation": false, + "hidden": false, + "template_id": "image-sftpgo" + }, { "id": "shadps4", "provider": "linuxserver.io", @@ -10406,6 +10654,37 @@ "hidden": false, "template_id": "linuxserver-signal" }, + { + "id": "silverbullet", + "provider": "silverbulletmd", + "template_family": "curated-profile", + "title": "SilverBullet", + "repository": "https://github.com/silverbulletmd/silverbullet", + "description": "Create and connect Markdown notes in a self-hosted, wiki-like workspace. The required SB_USER variable enables the initial single-user login, and /data holds the workspace.", + "website": "https://silverbullet.md/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/silverbullet.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "ghcr.io/silverbulletmd/silverbullet:latest", + "category": "documents", + "category_label": "Documents & Notes", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/silverbullet.json", + "template": "apps/silverbullet.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, + "requires_security_confirmation": false, + "hidden": false, + "template_id": "image-silverbullet" + }, { "id": "siyuan-note", "source_app_id": "siyuan-note", @@ -12297,6 +12576,37 @@ "hidden": false, "template_id": "image-wallabag" }, + { + "id": "wallos", + "provider": "ellite", + "template_family": "curated-profile", + "title": "Wallos", + "repository": "https://github.com/ellite/Wallos", + "description": "Keep a private list of subscriptions, payment dates and recurring costs in a web interface. Create the first administrator account from the browser after installation.", + "website": "https://wallosapp.com/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/wallos.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "bellamy/wallos:latest", + "category": "finance", + "category_label": "Finance & Budgeting", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/wallos.json", + "template": "apps/wallos.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, + "requires_security_confirmation": false, + "hidden": false, + "template_id": "image-wallos" + }, { "id": "webcord", "provider": "linuxserver.io",