ソースを参照

sub template support

xcad 9 ヶ月 前
コミット
abb018d4b2
92 ファイル変更1321 行追加872 行削除
  1. 31 5
      AGENTS.md
  2. 151 33
      cli/core/module.py
  3. 4 3
      cli/core/prompt.py
  4. 21 4
      cli/core/template.py
  5. 30 9
      cli/core/variables.py
  6. 0 20
      library/compose/alloy/compose.yaml.j2
  7. 21 0
      library/compose/alloy/template.yaml
  8. 0 13
      library/compose/ansiblesemaphore/compose.yaml.j2
  9. 21 0
      library/compose/ansiblesemaphore/template.yaml
  10. 0 48
      library/compose/authentik/compose.yaml.j2
  11. 21 0
      library/compose/authentik/template.yaml
  12. 0 13
      library/compose/bind9/compose.yaml.j2
  13. 21 0
      library/compose/bind9/template.yaml
  14. 0 13
      library/compose/cadvisor/compose.yaml.j2
  15. 21 0
      library/compose/cadvisor/template.yaml
  16. 0 13
      library/compose/checkmk/compose.yaml.j2
  17. 21 0
      library/compose/checkmk/template.yaml
  18. 0 13
      library/compose/clamav/compose.yaml.j2
  19. 21 0
      library/compose/clamav/template.yaml
  20. 0 13
      library/compose/dockge/compose.yaml.j2
  21. 21 0
      library/compose/dockge/template.yaml
  22. 0 14
      library/compose/gitea/compose.yaml.j2
  23. 21 0
      library/compose/gitea/template.yaml
  24. 0 14
      library/compose/gitlab-runner/compose.yaml.j2
  25. 21 0
      library/compose/gitlab-runner/template.yaml
  26. 0 14
      library/compose/gitlab/compose.yaml.j2
  27. 21 0
      library/compose/gitlab/template.yaml
  28. 0 20
      library/compose/grafana/compose.yaml.j2
  29. 22 0
      library/compose/grafana/template.yaml
  30. 0 14
      library/compose/heimdall/compose.yaml.j2
  31. 21 0
      library/compose/heimdall/template.yaml
  32. 0 14
      library/compose/homeassistant/compose.yaml.j2
  33. 21 0
      library/compose/homeassistant/template.yaml
  34. 0 13
      library/compose/homepage/compose.yaml.j2
  35. 21 0
      library/compose/homepage/template.yaml
  36. 0 20
      library/compose/homer/compose.yaml.j2
  37. 21 0
      library/compose/homer/template.yaml
  38. 0 46
      library/compose/influxdb/compose.yaml.j2
  39. 21 0
      library/compose/influxdb/template.yaml
  40. 0 13
      library/compose/loki/compose.yaml.j2
  41. 21 0
      library/compose/loki/template.yaml
  42. 0 13
      library/compose/mariadb/compose.yaml.j2
  43. 21 0
      library/compose/mariadb/template.yaml
  44. 0 77
      library/compose/n8n/compose.yaml.backup
  45. 0 21
      library/compose/n8n/compose.yaml.j2
  46. 21 0
      library/compose/n8n/template.yaml
  47. 0 43
      library/compose/nextcloud/compose.yaml.j2
  48. 21 0
      library/compose/nextcloud/template.yaml
  49. 0 13
      library/compose/nginxproxymanager/compose.yaml.j2
  50. 21 0
      library/compose/nginxproxymanager/template.yaml
  51. 0 22
      library/compose/nodeexporter/compose.yaml
  52. 9 0
      library/compose/nodeexporter/compose.yaml.j2
  53. 21 0
      library/compose/nodeexporter/template.yaml
  54. 0 13
      library/compose/openwebui/compose.yaml.j2
  55. 21 0
      library/compose/openwebui/template.yaml
  56. 0 13
      library/compose/passbolt/compose.yaml.j2
  57. 21 0
      library/compose/passbolt/template.yaml
  58. 0 46
      library/compose/pihole/compose.yaml.j2
  59. 21 0
      library/compose/pihole/template.yaml
  60. 0 28
      library/compose/portainer/compose.yaml.j2
  61. 21 0
      library/compose/portainer/template.yaml
  62. 0 28
      library/compose/postgres/compose.yaml.j2
  63. 22 0
      library/compose/postgres/template.yaml
  64. 0 13
      library/compose/prometheus/compose.yaml.j2
  65. 21 0
      library/compose/prometheus/template.yaml
  66. 0 13
      library/compose/promtail/compose.yaml.j2
  67. 21 0
      library/compose/promtail/template.yaml
  68. 0 13
      library/compose/teleport/compose.yaml.j2
  69. 21 0
      library/compose/teleport/template.yaml
  70. 18 0
      library/compose/traefik.authentik-middleware/middleware.yaml.j2
  71. 26 0
      library/compose/traefik.authentik-middleware/template.yaml
  72. 21 0
      library/compose/traefik.external-service/external-service.yaml.j2
  73. 17 0
      library/compose/traefik.external-service/router.yaml.j2
  74. 41 0
      library/compose/traefik.external-service/template.yaml
  75. 26 0
      library/compose/traefik.grafana/router.yaml.j2
  76. 15 0
      library/compose/traefik.guacamole/router.yaml.j2
  77. 6 0
      library/compose/traefik.ldap-middleware/middleware.yaml.j2
  78. 31 0
      library/compose/traefik.nextcloud/router.yaml.j2
  79. 23 0
      library/compose/traefik.pihole/router.yaml.j2
  80. 26 0
      library/compose/traefik.proxmox/router.yaml.j2
  81. 15 0
      library/compose/traefik.vaultwarden/router.yaml.j2
  82. 0 70
      library/compose/traefik/compose.yaml
  83. 36 0
      library/compose/traefik/compose.yaml.j2
  84. 37 0
      library/compose/traefik/template.yaml
  85. 0 13
      library/compose/twingate_connector/compose.yaml.j2
  86. 21 0
      library/compose/twingate_connector/template.yaml
  87. 0 13
      library/compose/uptimekuma/compose.yaml.j2
  88. 21 0
      library/compose/uptimekuma/template.yaml
  89. 0 13
      library/compose/wazuh/compose.yaml.j2
  90. 21 0
      library/compose/wazuh/template.yaml
  91. 0 25
      library/compose/whoami/compose.yaml.j2
  92. 21 0
      library/compose/whoami/template.yaml

+ 31 - 5
AGENTS.md

@@ -102,6 +102,37 @@ library/compose/my-nginx-template/
     └── README.md
     └── README.md
 ```
 ```
 
 
+#### Sub-Templates
+
+Sub-templates are specialized templates that use dot notation in their directory names to create related template variations or components. They provide a way to organize templates hierarchically and create focused, reusable configurations.
+
+**Directory Naming Convention:**
+- Sub-templates use dot notation: `parent.sub-name`
+- Example: `traefik.authentik-middleware`, `traefik.external-service`
+- The parent name should match an existing template for logical grouping
+
+**Visibility:**
+- By default, sub-templates are hidden from the standard `list` command
+- Use `list --all` to show all templates including sub-templates
+- This keeps the default view clean while providing access to specialized templates
+
+**Usage Examples:**
+```bash
+# Show only main templates (default behavior)
+python3 -m cli compose list
+
+# Show all templates including sub-templates
+python3 -m cli compose list --all
+
+# Generate a sub-template
+python3 -m cli compose generate traefik.authentik-middleware
+```
+
+**Common Use Cases:**
+- Configuration variations (e.g., `service.production`, `service.development`)
+- Component templates (e.g., `traefik.middleware`, `traefik.router`)
+- Environment-specific templates (e.g., `app.docker`, `app.kubernetes`)
+
 #### Variables
 #### Variables
 
 
 Variables are a cornerstone of the CLI, allowing for dynamic and customizable template generation. They are defined and processed with a clear precedence and logic.
 Variables are a cornerstone of the CLI, allowing for dynamic and customizable template generation. They are defined and processed with a clear precedence and logic.
@@ -153,14 +184,9 @@ After creating the issue, update the TODO line in the `AGENTS.md` file with the
 
 
 ### Work in Progress
 ### Work in Progress
 
 
-* TODO[1242-secret-support] Consider creating a "secret" variable type that automatically handles sensitive data and masks input during prompts, which also should be set via .env file and not directly in the compose files or other templates.
-* TODO[1244-mask-secrets] Mask secrets in rendering output (e.g. when displaying the final docker-compose file, mask secret values)
-* TODO[1245-out-directory] Add support for --out to specify a directory
-* TODO[1246-validation-rules] Add support for more complex validation rules for environment variables, such as regex patterns or value ranges.
 * TODO[1247-user-overrides] Add configuration support to allow users to override module and template spec with their own (e.g. defaults -> compose -> spec -> general ...)
 * TODO[1247-user-overrides] Add configuration support to allow users to override module and template spec with their own (e.g. defaults -> compose -> spec -> general ...)
 * TODO[1248-installation-script] Add an installation script when cloning the repo and setup necessary commands
 * TODO[1248-installation-script] Add an installation script when cloning the repo and setup necessary commands
 * TODO[1249-update-script] Add an automatic update script to keep the tool up-to-date with the latest version from the repository.
 * TODO[1249-update-script] Add an automatic update script to keep the tool up-to-date with the latest version from the repository.
 * TODO[1250-compose-deploy] Add compose deploy command to deploy a generated compose project to a local or remote docker environment
 * TODO[1250-compose-deploy] Add compose deploy command to deploy a generated compose project to a local or remote docker environment
 * TODO[1251-centralize-display-logic] Create a DisplayManager class to handle all rich rendering.
 * TODO[1251-centralize-display-logic] Create a DisplayManager class to handle all rich rendering.
-* TODO[1252-simplify-variable-handling] Refactor Variable and VariableCollection classes to simplify validation and initialization.
 * TODO[1253-streamline-prompting] Refactor PromptHandler to streamline validation and default value logic.
 * TODO[1253-streamline-prompting] Refactor PromptHandler to streamline validation and default value logic.

+ 151 - 33
cli/core/module.py

@@ -3,11 +3,11 @@ from __future__ import annotations
 import logging
 import logging
 from abc import ABC
 from abc import ABC
 from pathlib import Path
 from pathlib import Path
-from typing import Any, Dict, List, Optional
+from typing import Any, Optional
 
 
 from rich.console import Console
 from rich.console import Console
 from rich.panel import Panel
 from rich.panel import Panel
-from rich.prompt import Prompt
+from rich.prompt import Confirm
 from rich.table import Table
 from rich.table import Table
 from rich.tree import Tree
 from rich.tree import Tree
 from typer import Argument, Context, Option, Typer
 from typer import Argument, Context, Option, Typer
@@ -62,9 +62,6 @@ def parse_var_inputs(var_options: list[str], extra_args: list[str]) -> dict[str,
 
 
 class Module(ABC):
 class Module(ABC):
   """Streamlined base module that auto-detects variables from templates."""
   """Streamlined base module that auto-detects variables from templates."""
-  
-  name: str | None = None
-  description: str | None = None
 
 
   def __init__(self) -> None:
   def __init__(self) -> None:
     if not all([self.name, self.description]):
     if not all([self.name, self.description]):
@@ -80,9 +77,13 @@ class Module(ABC):
   # SECTION: Public Commands
   # SECTION: Public Commands
   # --------------------------
   # --------------------------
 
 
-  def list(self) -> list[Template]:
-    """List all templates."""
-    logger.debug(f"Listing templates for module '{self.name}'")
+  def list(
+    self, 
+    filter_name: Optional[str] = Argument(None, help="Filter templates by name (e.g., 'traefik' shows traefik.*)"),
+    all_templates: bool = Option(False, "--all", "-a", help="Show all templates including sub-templates")
+  ) -> list[Template]:
+    """List templates with optional filtering."""
+    logger.debug(f"Listing templates for module '{self.name}' with filter='{filter_name}', all={all_templates}")
     templates = []
     templates = []
 
 
     entries = self.libraries.find(self.name, sort_results=True)
     entries = self.libraries.find(self.name, sort_results=True)
@@ -94,30 +95,39 @@ class Module(ABC):
         logger.error(f"Failed to load template from {template_dir}: {exc}")
         logger.error(f"Failed to load template from {template_dir}: {exc}")
         continue
         continue
     
     
-    if templates:
-      logger.info(f"Listing {len(templates)} templates for module '{self.name}'")
+    # Apply filtering logic
+    filtered_templates = self._filter_templates(templates, filter_name, all_templates)
+    
+    if filtered_templates:
+      # Group templates for hierarchical display
+      grouped_templates = self._group_templates(filtered_templates)
+      
+      logger.info(f"Listing {len(filtered_templates)} templates for module '{self.name}'")
       table = Table(title=f"{self.name.capitalize()} templates")
       table = Table(title=f"{self.name.capitalize()} templates")
       table.add_column("ID", style="bold", no_wrap=True)
       table.add_column("ID", style="bold", no_wrap=True)
       table.add_column("Name")
       table.add_column("Name")
       table.add_column("Description")
       table.add_column("Description")
       table.add_column("Version", no_wrap=True)
       table.add_column("Version", no_wrap=True)
-      table.add_column("Tags")
       table.add_column("Library", no_wrap=True)
       table.add_column("Library", no_wrap=True)
 
 
-      for template in templates:
+      for template_info in grouped_templates:
+        template = template_info['template']
+        indent = template_info['indent']
         name = template.metadata.name or 'Unnamed Template'
         name = template.metadata.name or 'Unnamed Template'
         desc = template.metadata.description or 'No description available'
         desc = template.metadata.description or 'No description available'
         version = template.metadata.version or ''
         version = template.metadata.version or ''
-        tags_list = template.metadata.tags or []
-        tags = ", ".join(tags_list) if isinstance(tags_list, list) else str(tags_list)
         library = template.metadata.library or ''
         library = template.metadata.library or ''
-        table.add_row(template.id, name, desc, version, tags, library)
+
+        # Add indentation for sub-templates
+        template_id = f"{indent}{template.id}"
+        table.add_row(template_id, name, desc, version, library)
 
 
       console.print(table)
       console.print(table)
     else:
     else:
-      logger.info(f"No templates found for module '{self.name}'")
+      filter_msg = f" matching '{filter_name}'" if filter_name else ""
+      logger.info(f"No templates found for module '{self.name}'{filter_msg}")
 
 
-    return templates
+    return filtered_templates
 
 
   def show(
   def show(
     self,
     self,
@@ -176,14 +186,23 @@ class Module(ABC):
       if template.variables:
       if template.variables:
         template.variables.validate_all()
         template.variables.validate_all()
       
       
-      rendered_files = template.render(variable_values)
+      rendered_files = template.render(template.variables)
       logger.info(f"Successfully rendered template '{id}'")
       logger.info(f"Successfully rendered template '{id}'")
+      output_dir = out or Path(".")
+
+      # Check if the directory is empty and confirm overwrite if necessary
+      if output_dir.exists() and any(output_dir.iterdir()):
+        if interactive:
+          if not Confirm.ask(f"Output directory '{output_dir}' is not empty. Overwrite files?", default=False):
+            console.print("[yellow]Generation cancelled.[/yellow]")
+            return
+        else:
+          logger.warning(f"Output directory '{output_dir}' is not empty. Existing files may be overwritten.")
       
       
-      output_dir = out
-      if not output_dir:
-        output_dir_str = Prompt.ask("Enter output directory", default=".")
-        output_dir = Path(output_dir_str)
-      
+      # Create the output directory if it doesn't exist
+      output_dir.mkdir(parents=True, exist_ok=True)
+
+      # Write rendered files to the output directory
       for file_path, content in rendered_files.items():
       for file_path, content in rendered_files.items():
         full_path = output_dir / file_path
         full_path = output_dir / file_path
         full_path.parent.mkdir(parents=True, exist_ok=True)
         full_path.parent.mkdir(parents=True, exist_ok=True)
@@ -193,6 +212,13 @@ class Module(ABC):
       
       
       logger.info(f"Template written to directory: {output_dir}")
       logger.info(f"Template written to directory: {output_dir}")
 
 
+      # If no output directory was specified, print the masked content to the console
+      if not out:
+        console.print("\n[bold]Rendered output (sensitive values masked):[/bold]")
+        masked_files = template.mask_sensitive_values(rendered_files, template.variables)
+        for file_path, content in masked_files.items():
+          console.print(Panel(content, title=file_path, border_style="green"))
+
     except Exception as e:
     except Exception as e:
       logger.error(f"Error rendering template '{id}': {e}")
       logger.error(f"Error rendering template '{id}': {e}")
       console.print(f"[red]Error generating template: {e}[/red]")
       console.print(f"[red]Error generating template: {e}[/red]")
@@ -226,6 +252,89 @@ class Module(ABC):
 
 
   # !SECTION
   # !SECTION
 
 
+  # --------------------------
+  # SECTION: Template Organization Methods
+  # --------------------------
+
+  def _filter_templates(self, templates: list[Template], filter_name: Optional[str], all_templates: bool) -> list[Template]:
+    """Filter templates based on name and sub-template visibility."""
+    filtered = []
+    
+    for template in templates:
+      template_id = template.id
+      is_sub_template = '.' in template_id
+      
+      # If we have a filter, apply it
+      if filter_name:
+        if is_sub_template:
+          # For sub-templates, check if they start with filter_name.
+          if template_id.startswith(f"{filter_name}."):
+            filtered.append(template)
+        else:
+          # For main templates, exact match
+          if template_id == filter_name:
+            filtered.append(template)
+      else:
+        # No filter - include based on all_templates flag
+        if not all_templates and is_sub_template:
+          continue
+        filtered.append(template)
+    
+    return filtered
+
+  def _group_templates(self, templates: list[Template]) -> list[dict]:
+    """Group templates hierarchically for display."""
+    grouped = []
+    main_templates = {}
+    sub_templates = []
+    
+    # Separate main templates and sub-templates
+    for template in templates:
+      if '.' in template.id:
+        sub_templates.append(template)
+      else:
+        main_templates[template.id] = template
+        grouped.append({
+          'template': template,
+          'indent': '',
+          'is_main': True
+        })
+    
+    # Sort sub-templates by parent
+    sub_templates.sort(key=lambda t: t.id)
+    
+    # Insert sub-templates after their parents
+    for sub_template in sub_templates:
+      parent_name = sub_template.id.split('.')[0]
+      
+      # Find where to insert this sub-template
+      insert_index = -1
+      for i, item in enumerate(grouped):
+        if item['template'].id == parent_name:
+          # Find the last sub-template for this parent
+          j = i + 1
+          while j < len(grouped) and not grouped[j]['is_main']:
+            j += 1
+          insert_index = j
+          break
+      
+      sub_name = sub_template.id.split('.', 1)[1]  # Get part after first dot
+      sub_template_info = {
+        'template': sub_template,
+        'indent': '├─ ' if insert_index < len(grouped) - 1 else '└─ ',
+        'is_main': False
+      }
+      
+      if insert_index >= 0:
+        grouped.insert(insert_index, sub_template_info)
+      else:
+        # Parent not found, add at end
+        grouped.append(sub_template_info)
+    
+    return grouped
+
+  # !SECTION
+
   # --------------------------
   # --------------------------
   # SECTION: Private Methods
   # SECTION: Private Methods
   # --------------------------
   # --------------------------
@@ -249,12 +358,16 @@ class Module(ABC):
   def _display_template_details(self, template: Template, template_id: str) -> None:
   def _display_template_details(self, template: Template, template_id: str) -> None:
     """Display template information panel and variables table."""
     """Display template information panel and variables table."""
     
     
-    # Print the main panel
-    console.print(Panel(
-      f"[bold]{template.metadata.name or 'Unnamed Template'}[/bold]\n\n{template.metadata.description or 'No description available'}", 
-      title=f"Template: {template_id}", 
-      subtitle=f"Module: {self.name}"
-    ))
+    # Build metadata info text
+    info_lines = []
+    info_lines.append(f"{template.metadata.description or 'No description available'}")
+    info_lines.append("")  # Empty line
+    
+    # Print template information with simple heading
+    template_name = template.metadata.name or 'Unnamed Template'
+    console.print(f"[bold blue]{template_name} ({template_id} - [cyan]{template.metadata.version or 'Not specified'}[/cyan])[/bold blue]")
+    for line in info_lines:
+      console.print(line)
     
     
     # Build the file structure tree
     # Build the file structure tree
     file_tree = Tree("[bold blue]Template File Structure:[/bold blue]")
     file_tree = Tree("[bold blue]Template File Structure:[/bold blue]")
@@ -289,11 +402,14 @@ class Module(ABC):
         console.print() # Add spacing
         console.print() # Add spacing
         console.print(file_tree) # Print the Tree object directly
         console.print(file_tree) # Print the Tree object directly
 
 
-    if template.variables and template.variables._set:
+    if template.variables and template.variables.has_sections():
       console.print()  # Add spacing
       console.print()  # Add spacing
       
       
+      # Print variables heading
+      console.print(f"[bold blue]Template Variables:[/bold blue]")
+      
       # Create variables table
       # Create variables table
-      variables_table = Table(title="Template Variables", show_header=True, header_style="bold blue")
+      variables_table = Table(show_header=True, header_style="bold blue")
       variables_table.add_column("Variable", style="cyan", no_wrap=True)
       variables_table.add_column("Variable", style="cyan", no_wrap=True)
       variables_table.add_column("Type", style="magenta")
       variables_table.add_column("Type", style="magenta")
       variables_table.add_column("Default", style="green")
       variables_table.add_column("Default", style="green")
@@ -302,7 +418,7 @@ class Module(ABC):
       
       
       # Add variables grouped by section
       # Add variables grouped by section
       first_section = True
       first_section = True
-      for section_key, section in template.variables._set.items():
+      for section_key, section in template.variables.get_sections().items():
         if section.variables:
         if section.variables:
           # Add spacing between sections (except before first section)
           # Add spacing between sections (except before first section)
           if not first_section:
           if not first_section:
@@ -348,7 +464,9 @@ class Module(ABC):
             
             
             # Format default value
             # Format default value
             default_val = str(variable.value) if variable.value is not None else ""
             default_val = str(variable.value) if variable.value is not None else ""
-            if len(default_val) > 30:
+            if variable.sensitive:
+              default_val = "********"
+            elif len(default_val) > 30:
               default_val = default_val[:27] + "..."
               default_val = default_val[:27] + "..."
             
             
             variables_table.add_row(
             variables_table.add_row(

+ 4 - 3
cli/core/prompt.py

@@ -41,7 +41,7 @@ class PromptHandler:
     collected: Dict[str, Any] = {}
     collected: Dict[str, Any] = {}
 
 
     # Process each section
     # Process each section
-    for section_key, section in variables._set.items():
+    for section_key, section in variables.get_sections().items():
       if not section.variables:
       if not section.variables:
         continue
         continue
 
 
@@ -127,17 +127,18 @@ class PromptHandler:
       "int": self._prompt_int,
       "int": self._prompt_int,
       "enum": lambda text, default: self._prompt_enum(text, variable.options or [], default),
       "enum": lambda text, default: self._prompt_enum(text, variable.options or [], default),
     }
     }
-    return handlers.get(variable.type, self._prompt_string)
+    return handlers.get(variable.type, lambda text, default: self._prompt_string(text, default, is_sensitive=variable.sensitive))
 
 
   def _show_validation_error(self, message: str) -> None:
   def _show_validation_error(self, message: str) -> None:
     """Display validation feedback consistently."""
     """Display validation feedback consistently."""
     self.console.print(f"[red]{message}[/red]")
     self.console.print(f"[red]{message}[/red]")
 
 
-  def _prompt_string(self, prompt_text: str, default: Any = None) -> str:
+  def _prompt_string(self, prompt_text: str, default: Any = None, is_sensitive: bool = False) -> str:
     value = Prompt.ask(
     value = Prompt.ask(
       prompt_text,
       prompt_text,
       default=str(default) if default is not None else "",
       default=str(default) if default is not None else "",
       show_default=True,
       show_default=True,
+      password=is_sensitive
     )
     )
     return value.strip() if value else ""
     return value.strip() if value else ""
 
 

+ 21 - 4
cli/core/template.py

@@ -211,7 +211,11 @@ class Template:
       if "vars" in section_data and isinstance(section_data["vars"], dict):
       if "vars" in section_data and isinstance(section_data["vars"], dict):
         filtered_vars = {}
         filtered_vars = {}
         for var_name, var_data in section_data["vars"].items():
         for var_name, var_data in section_data["vars"].items():
-          if var_name in used_variables:
+          is_used = var_name in used_variables
+          is_sensitive = var_data.get("sensitive", False)
+          
+          # Include variables that are either used in templates OR marked as sensitive
+          if is_used or is_sensitive:
             module_has_var = var_name in module_specs.get(section_key, {}).get("vars", {})
             module_has_var = var_name in module_specs.get(section_key, {}).get("vars", {})
             template_has_var = var_name in template_specs.get(section_key, {}).get("vars", {})
             template_has_var = var_name in template_specs.get(section_key, {}).get("vars", {})
             
             
@@ -286,15 +290,16 @@ class Template:
       keep_trailing_newline=False,
       keep_trailing_newline=False,
     )
     )
 
 
-  def render(self, variables: dict[str, Any]) -> Dict[str, str]:
+  def render(self, variables: VariableCollection) -> Dict[str, str]:
     """Render all .j2 files in the template directory."""
     """Render all .j2 files in the template directory."""
-    logger.debug(f"Rendering template '{self.id}' with variables: {variables}")
+    variable_values = variables.get_all_values()
+    logger.debug(f"Rendering template '{self.id}' with variables: {variable_values}")
     rendered_files = {}
     rendered_files = {}
     for template_file in self.template_files: # Iterate over TemplateFile objects
     for template_file in self.template_files: # Iterate over TemplateFile objects
       if template_file.file_type == 'j2':
       if template_file.file_type == 'j2':
         try:
         try:
           template = self.jinja_env.get_template(str(template_file.relative_path)) # Use lazy-loaded jinja_env
           template = self.jinja_env.get_template(str(template_file.relative_path)) # Use lazy-loaded jinja_env
-          rendered_content = template.render(**variables)
+          rendered_content = template.render(**variable_values)
           rendered_files[str(template_file.output_path)] = rendered_content
           rendered_files[str(template_file.output_path)] = rendered_content
         except Exception as e:
         except Exception as e:
           logger.error(f"Error rendering template file {template_file.relative_path}: {e}")
           logger.error(f"Error rendering template file {template_file.relative_path}: {e}")
@@ -312,6 +317,18 @@ class Template:
               raise
               raise
           
           
     return rendered_files
     return rendered_files
+
+  def mask_sensitive_values(self, rendered_files: Dict[str, str], variables: VariableCollection) -> Dict[str, str]:
+    """Mask sensitive values in rendered files."""
+    masked_files = {}
+    sensitive_vars = variables.get_sensitive_variables()
+    
+    for file_path, content in rendered_files.items():
+      for var_name, var_value in sensitive_vars.items():
+        content = content.replace(str(var_value), "********")
+      masked_files[file_path] = content
+      
+    return masked_files
   
   
   # !SECTION
   # !SECTION
 
 

+ 30 - 9
cli/core/variables.py

@@ -53,6 +53,7 @@ class Variable:
     self.value: Any = data.get("value") if data.get("value") is not None else data.get("default")
     self.value: Any = data.get("value") if data.get("value") is not None else data.get("default")
     self.section: Optional[str] = data.get("section")
     self.section: Optional[str] = data.get("section")
     self.origin: Optional[str] = data.get("origin")
     self.origin: Optional[str] = data.get("origin")
+    self.sensitive: bool = data.get("sensitive", False)
 
 
     # Validate and convert the default/initial value if present
     # Validate and convert the default/initial value if present
     if self.value is not None:
     if self.value is not None:
@@ -298,11 +299,20 @@ class VariableCollection:
       self._variable_map[var_name] = variable
       self._variable_map[var_name] = variable
 
 
   # -------------------------
   # -------------------------
-  # SECTION: Helper Methods
+  # SECTION: Public API Methods
   # -------------------------
   # -------------------------
 
 
-  # NOTE: These helper methods reduce code duplication across module.py and prompt.py
-  # by centralizing common variable collection operations
+  def get_sections(self) -> Dict[str, VariableSection]:
+    """Get all sections in the collection."""
+    return self._sections.copy()
+  
+  def get_section(self, key: str) -> Optional[VariableSection]:
+    """Get a specific section by its key."""
+    return self._sections.get(key)
+  
+  def has_sections(self) -> bool:
+    """Check if the collection has any sections."""
+    return bool(self._sections)
 
 
   def get_all_values(self) -> dict[str, Any]:
   def get_all_values(self) -> dict[str, Any]:
     """Get all variable values as a dictionary."""
     """Get all variable values as a dictionary."""
@@ -313,6 +323,19 @@ class VariableCollection:
       all_values[var_name] = variable.get_typed_value()
       all_values[var_name] = variable.get_typed_value()
     return all_values
     return all_values
 
 
+  def get_sensitive_variables(self) -> Dict[str, Any]:
+    """Get only the sensitive variables with their values."""
+    return {name: var.value for name, var in self._variable_map.items() if var.sensitive and var.value}
+
+  # !SECTION
+
+  # -------------------------
+  # SECTION: Helper Methods
+  # -------------------------
+
+  # NOTE: These helper methods reduce code duplication across module.py and prompt.py
+  # by centralizing common variable collection operations
+
   def apply_overrides(self, overrides: dict[str, Any], origin_suffix: str = " -> cli") -> list[str]:
   def apply_overrides(self, overrides: dict[str, Any], origin_suffix: str = " -> cli") -> list[str]:
     """Apply multiple variable overrides at once."""
     """Apply multiple variable overrides at once."""
     # NOTE: This method uses the _variable_map for a significant performance gain,
     # NOTE: This method uses the _variable_map for a significant performance gain,
@@ -347,7 +370,7 @@ class VariableCollection:
     
     
     if errors:
     if errors:
       logger.warning(f"Some CLI overrides failed: {'; '.join(errors)}")
       logger.warning(f"Some CLI overrides failed: {'; '.join(errors)}")
-    
+  
   def validate_all(self) -> None:
   def validate_all(self) -> None:
     """Validate all variables in the collection, skipping disabled sections."""
     """Validate all variables in the collection, skipping disabled sections."""
     for section in self._sections.values():
     for section in self._sections.values():
@@ -358,11 +381,9 @@ class VariableCollection:
           logger.debug(f"Skipping validation for disabled section: '{section.key}'")
           logger.debug(f"Skipping validation for disabled section: '{section.key}'")
           continue  # Skip this entire section
           continue  # Skip this entire section
 
 
-      for var_name, variable in section.variables.items():
-        try:
-          variable.validate(variable.value)
-        except ValueError as e:
-          raise ValueError(f"Validation failed for variable '{var_name}': {e}") from e
+      # NOTE: Skip individual variable validation since we removed the validate method
+      # All validation now happens during conversion in the Variable.convert() method
+      pass
 
 
   # !SECTION
   # !SECTION
 
 

+ 0 - 20
library/compose/alloy/compose.yaml → library/compose/alloy/compose.yaml.j2

@@ -1,23 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Grafana Alloy"
-  description: "A lightweight and flexible service mesh"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - "grafana"
-    - "alloy"
-    - "monitoring"
-    - "http"
-    - "traefik"
-variables:
-  container_hostname:
-    description: "Sets the container's internal hostname (this will show up in the collected logs)"
-    type: "string"
-    required: true
----
 services:
 services:
   {{ service_name | default("alloy") }}:
   {{ service_name | default("alloy") }}:
     image: grafana/alloy:v1.10.2
     image: grafana/alloy:v1.10.2

+ 21 - 0
library/compose/alloy/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Alloy
+  description: Docker compose setup for alloy
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - alloy
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      alloy_version:
+        type: string
+        description: Alloy version
+        default: latest
+
+---

+ 0 - 13
library/compose/ansiblesemaphore/compose.yaml → library/compose/ansiblesemaphore/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Ansible Semaphore"
-  description: "A powerful and flexible automation tool"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - "ansible"
-    - "automation"
-    - "semaphore"
----
 volumes:
 volumes:
   semaphore-mysql:
   semaphore-mysql:
     driver: local
     driver: local

+ 21 - 0
library/compose/ansiblesemaphore/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Volumes
+  description: Docker compose setup for volumes
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - volumes
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      volumes_version:
+        type: string
+        description: Volumes version
+        default: latest
+
+---

+ 0 - 48
library/compose/authentik/compose.yaml → library/compose/authentik/compose.yaml.j2

@@ -1,51 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Authentik"
-  description: "An open-source identity and access management solution"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - "authentik"
-    - "identity"
-    - "access"
-    - "management"
-spec:
-  ports:
-    vars:
-      ports_http:
-        description: "HTTP port for Authentik web interface"
-        type: int
-        default: 9000
-      ports_https:
-        description: "HTTPS port for Authentik web interface"
-        type: int
-        default: 9443
-  authentik:
-    vars:
-      authentik_secret_key:
-        description: "Authentik secret key (generate with: openssl rand -base64 32)"
-        type: str
-        default: ""
-      authentik_error_reporting:
-        description: "Enable Authentik error reporting"
-        type: bool
-        default: false
-  database:
-    required: true
-    vars:
-      database_external:
-        description: "Use an external database (if true, the internal postgres service will not be created)"
-        type: bool
-        default: false
-  email:
-    vars:
-      email_timeout:
-        description: "Email timeout in seconds"
-        type: int
-        default: 10
----
 services:
 services:
   {{ service_name | default('authentik-server') }}:
   {{ service_name | default('authentik-server') }}:
     image: ghcr.io/goauthentik/server:2025.6.3
     image: ghcr.io/goauthentik/server:2025.6.3

+ 21 - 0
library/compose/authentik/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Authentik-Server
+  description: Docker compose setup for authentik-server
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - authentik-server
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      authentik-server_version:
+        type: string
+        description: Authentik-Server version
+        default: latest
+
+---

+ 0 - 13
library/compose/bind9/compose.yaml → library/compose/bind9/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "BIND9"
-  description: "A powerful and flexible DNS server"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - "bind9"
-    - "dns"
-    - "server"
----
 services:
 services:
   bind9:
   bind9:
     image: docker.io/ubuntu/bind9:9.20-24.10_edge
     image: docker.io/ubuntu/bind9:9.20-24.10_edge

+ 21 - 0
library/compose/bind9/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Bind9
+  description: Docker compose setup for bind9
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - bind9
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      bind9_version:
+        type: string
+        description: Bind9 version
+        default: latest
+
+---

+ 0 - 13
library/compose/cadvisor/compose.yaml → library/compose/cadvisor/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "cAdvisor"
-  description: "A tool for monitoring container performance"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - "cadvisor"
-    - "monitoring"
-    - "containers"
----
 services:
 services:
   cadvisor:
   cadvisor:
     image: gcr.io/cadvisor/cadvisor:v0.52.1
     image: gcr.io/cadvisor/cadvisor:v0.52.1

+ 21 - 0
library/compose/cadvisor/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Cadvisor
+  description: Docker compose setup for cadvisor
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - cadvisor
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      cadvisor_version:
+        type: string
+        description: Cadvisor version
+        default: latest
+
+---

+ 0 - 13
library/compose/checkmk/compose.yaml → library/compose/checkmk/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Checkmk"
-  description: "A powerful monitoring solution"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - "checkmk"
-    - "monitoring"
-    - "observability"
----
 services:
 services:
   monitoring:
   monitoring:
     image: checkmk/check-mk-raw:2.4.0-latest
     image: checkmk/check-mk-raw:2.4.0-latest

+ 21 - 0
library/compose/checkmk/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Monitoring
+  description: Docker compose setup for monitoring
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - monitoring
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      monitoring_version:
+        type: string
+        description: Monitoring version
+        default: latest
+
+---

+ 0 - 13
library/compose/clamav/compose.yaml → library/compose/clamav/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "ClamAV"
-  description: "An open-source antivirus engine"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - "clamav"
-    - "antivirus"
-    - "security"
----
 services:
 services:
   clamav:
   clamav:
     image: docker.io/clamav/clamav:1.4.3
     image: docker.io/clamav/clamav:1.4.3

+ 21 - 0
library/compose/clamav/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Clamav
+  description: Docker compose setup for clamav
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - clamav
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      clamav_version:
+        type: string
+        description: Clamav version
+        default: latest
+
+---

+ 0 - 13
library/compose/dockge/compose.yaml → library/compose/dockge/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Dockge"
-  description: "A Docker GUI for managing your containers"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - "dockge"
-    - "docker"
-    - "management"
----
 services:
 services:
   dockge:
   dockge:
     container_name: dockge
     container_name: dockge

+ 21 - 0
library/compose/dockge/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Dockge
+  description: Docker compose setup for dockge
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - dockge
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      dockge_version:
+        type: string
+        description: Dockge version
+        default: latest
+
+---

+ 0 - 14
library/compose/gitea/compose.yaml → library/compose/gitea/compose.yaml.j2

@@ -1,17 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Gitea"
-  description: "A self-hosted Git service"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - gitea
-    - git
-    - code
-    - repository
----
 services:
 services:
   server:
   server:
     image: docker.io/gitea/gitea:1.24.5
     image: docker.io/gitea/gitea:1.24.5

+ 21 - 0
library/compose/gitea/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Server
+  description: Docker compose setup for server
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - server
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      server_version:
+        type: string
+        description: Server version
+        default: latest
+
+---

+ 0 - 14
library/compose/gitlab-runner/compose.yaml → library/compose/gitlab-runner/compose.yaml.j2

@@ -1,17 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "GitLab Runner"
-  description: "A self-hosted CI/CD automation tool"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - gitlab-runner
-    - ci
-    - cd
-    - automation
----
 services:
 services:
   gitlab-runner:
   gitlab-runner:
     image: docker.io/gitlab/gitlab-runner:alpine-v17.9.1
     image: docker.io/gitlab/gitlab-runner:alpine-v17.9.1

+ 21 - 0
library/compose/gitlab-runner/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Gitlab-Runner
+  description: Docker compose setup for gitlab-runner
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - gitlab-runner
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      gitlab-runner_version:
+        type: string
+        description: Gitlab-Runner version
+        default: latest
+
+---

+ 0 - 14
library/compose/gitlab/compose.yaml → library/compose/gitlab/compose.yaml.j2

@@ -1,17 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "GitLab"
-  description: "A self-hosted Git repository manager"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - gitlab
-    - git
-    - repository
-    - management
----
 services:
 services:
   gitlab:
   gitlab:
     image: docker.io/gitlab/gitlab-ce:18.3.1-ce.0
     image: docker.io/gitlab/gitlab-ce:18.3.1-ce.0

+ 21 - 0
library/compose/gitlab/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Gitlab
+  description: Docker compose setup for gitlab
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - gitlab
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      gitlab_version:
+        type: string
+        description: Gitlab version
+        default: latest
+
+---

+ 0 - 20
library/compose/grafana/compose.yaml → library/compose/grafana/compose.yaml.j2

@@ -1,23 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Grafana"
-  description: "An open-source platform for monitoring and observability"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - grafana
-    - monitoring
-    - observability
-spec:
-  ports:
-    vars:
-      ports_http:
-        description: "HTTP port for Grafana web interface"
-        type: int
-        default: 3000
----
 services:
 services:
   {{ service_name | default('grafana') }}:
   {{ service_name | default('grafana') }}:
     image: docker.io/grafana/grafana-oss:12.1.1
     image: docker.io/grafana/grafana-oss:12.1.1

+ 22 - 0
library/compose/grafana/template.yaml

@@ -0,0 +1,22 @@
+---
+kind: compose
+metadata:
+  name: Grafana
+  description: Open-source platform for monitoring and observability
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - grafana
+  - monitoring
+  - observability
+  - dashboard
+spec:
+  general:
+    vars:
+      grafana_version:
+        type: string
+        description: Grafana version
+        default: latest
+
+---

+ 0 - 14
library/compose/heimdall/compose.yaml → library/compose/heimdall/compose.yaml.j2

@@ -1,17 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Heimdall"
-  description: "An open-source dashboard for your web applications"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - heimdall
-    - dashboard
-    - monitoring
-    - observability
----
 services:
 services:
   heimdall:
   heimdall:
     image: lscr.io/linuxserver/heimdall:2.7.4
     image: lscr.io/linuxserver/heimdall:2.7.4

+ 21 - 0
library/compose/heimdall/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Heimdall
+  description: Docker compose setup for heimdall
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - heimdall
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      heimdall_version:
+        type: string
+        description: Heimdall version
+        default: latest
+
+---

+ 0 - 14
library/compose/homeassistant/compose.yaml → library/compose/homeassistant/compose.yaml.j2

@@ -1,17 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Home Assistant"
-  description: "A self-hosted home automation platform"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - homeassistant
-    - automation
-    - monitoring
-    - observability
----
 services:
 services:
   homeassistant:
   homeassistant:
     container_name: homeassistant
     container_name: homeassistant

+ 21 - 0
library/compose/homeassistant/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Homeassistant
+  description: Docker compose setup for homeassistant
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - homeassistant
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      homeassistant_version:
+        type: string
+        description: Homeassistant version
+        default: latest
+
+---

+ 0 - 13
library/compose/homepage/compose.yaml → library/compose/homepage/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Homepage"
-  description: "A self-hosted homepage for your web applications"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - homepage
-    - web
-    - dashboard
----
 services:
 services:
   homepage:
   homepage:
     image: ghcr.io/gethomepage/homepage:v1.4.6
     image: ghcr.io/gethomepage/homepage:v1.4.6

+ 21 - 0
library/compose/homepage/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Homepage
+  description: Docker compose setup for homepage
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - homepage
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      homepage_version:
+        type: string
+        description: Homepage version
+        default: latest
+
+---

+ 0 - 20
library/compose/homer/compose.yaml → library/compose/homer/compose.yaml.j2

@@ -1,23 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Homer"
-  description: "A simple homepage for your services"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - "homer"
-    - "http"
-    - "testing"
-spec:
-  ports:
-    vars:
-      ports_http:
-        description: "HTTP port for Homer web interface"
-        type: int
-        default: 8080
----
 services:
 services:
   {{ service_name | default('homer') }}:
   {{ service_name | default('homer') }}:
     image: docker.io/b4bz/homer:v25.08.1
     image: docker.io/b4bz/homer:v25.08.1

+ 21 - 0
library/compose/homer/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Homer
+  description: Docker compose setup for homer
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - homer
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      homer_version:
+        type: string
+        description: Homer version
+        default: latest
+
+---

+ 0 - 46
library/compose/influxdb/compose.yaml → library/compose/influxdb/compose.yaml.j2

@@ -1,49 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "InfluxDB"
-  description: "An open-source time series database"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - influxdb
-    - monitoring
-    - database
-spec:
-  ports:
-    vars:
-      ports_http:
-        description: "HTTP port for InfluxDB web interface and API"
-        type: int
-        default: 8086
-  influxdb:
-    vars:
-      influxdb_init_username:
-        description: "Initial InfluxDB admin username"
-        type: str
-        default: "admin"
-      influxdb_init_password:
-        description: "Initial InfluxDB admin password"
-        type: str
-        default: "password"
-      influxdb_init_org:
-        description: "Initial InfluxDB organization name"
-        type: str
-        default: "myorg"
-      influxdb_init_bucket:
-        description: "Initial InfluxDB bucket name"
-        type: str
-        default: "mybucket"
-      influxdb_init_retention:
-        description: "Data retention period (e.g., 1w, 30d, 1y)"
-        type: str
-        default: "0"
-      influxdb_init_token:
-        description: "Admin token for InfluxDB (leave empty for auto-generation)"
-        type: str
-        default: ""
----
 services:
 services:
   {{ service_name | default('influxdb') }}:
   {{ service_name | default('influxdb') }}:
     container_name: {{ container_name | default('influxdb') }}
     container_name: {{ container_name | default('influxdb') }}

+ 21 - 0
library/compose/influxdb/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Influxdb
+  description: Docker compose setup for influxdb
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - influxdb
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      influxdb_version:
+        type: string
+        description: Influxdb version
+        default: latest
+
+---

+ 0 - 13
library/compose/loki/compose.yaml → library/compose/loki/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Loki"
-  description: "An open-source log aggregation system"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - loki
-    - monitoring
-    - logging
----
 services:
 services:
   loki:
   loki:
     container_name: loki
     container_name: loki

+ 21 - 0
library/compose/loki/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Loki
+  description: Docker compose setup for loki
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - loki
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      loki_version:
+        type: string
+        description: Loki version
+        default: latest
+
+---

+ 0 - 13
library/compose/mariadb/compose.yaml → library/compose/mariadb/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "MariaDB"
-  description: "An open-source relational database management system"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - mariadb
-    - database
-    - sql
----
 # (Optional) when using custom network
 # (Optional) when using custom network
 # networks:
 # networks:
 #   yournetwork:
 #   yournetwork:

+ 21 - 0
library/compose/mariadb/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Volumes
+  description: Docker compose setup for volumes
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - volumes
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      volumes_version:
+        type: string
+        description: Volumes version
+        default: latest
+
+---

+ 0 - 77
library/compose/n8n/compose.yaml.backup

@@ -1,77 +0,0 @@
----
-name: "n8n"
-description: "Workflow automation and integration tool"
-version: "0.0.1"
-date: "2025-09-03"
-author: "Christian Lempa"
-tags:
-  - n8n
-  - automation
-  - workflows
-  - compose
-variables:
-  template.custom_config:
-    description: "Custom configuration for n8n"
-    hint: "Additional environment variables or settings"
-    type: "string"
-    default: ""
----
-services:
-  {{ service_name }}:
-    image: n8nio/n8n:1.110.1
-    environment:
-      - N8N_LOG_LEVEL={{ container_loglevel | default('info') }}
-      - GENERIC_TIMEZONE={{ container_timezone }}
-      - TZ={{ container_timezone }}
-      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
-      - N8N_RUNNERS_ENABLED=true
-      {% if traefik %}
-      {% if traefik.tls %}
-      - N8N_EDITOR_BASE_URL=https://{{ traefik.host }}
-      {% else %}
-      - N8N_EDITOR_BASE_URL=http://{{ traefik.host }}
-      {% endif %}
-      {% endif %}
-      {% if postgres %}
-      - DB_TYPE=postgresdb
-      - DB_POSTGRESDB_HOST={{ postgres.host }}
-      - DB_POSTGRESDB_PORT=${DB_POSTGRESDB_PORT:-5432}
-      - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
-      - DB_POSTGRESDB_USER=${POSTGRES_USER}
-      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
-      {% endif %}
-    volumes:
-      - /etc/localtime:/etc/localtime:ro
-      - data:/home/node/.n8n
-    {% if network %}
-    networks:
-      - {{ network.name | default('bridge') }}
-    {% endif %}
-    {% if traefik %}
-    labels:
-      - traefik.enable={{ traefik | default('true') }}
-      - traefik.http.routers.{{ service_name }}.rule=Host(`{{ traefik.host }}`)
-      {% if traefik.tls %}
-      - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik.tls.entrypoint | default('websecure') }}
-      - traefik.http.routers.{{ service_name }}.tls=true
-      - traefik.http.routers.{{ service_name }}.tls.certresolver={{ traefik.tls.certresolver }}
-      {% else %}
-      - traefik.http.routers.{{ service_name }}.entrypoints={{ traefik.entrypoint | default('web') }}
-      {% endif %}
-      - traefik.http.services.{{ service_name }}.loadbalancer.server.port=5678
-    {% endif %}
-    restart: {{ restart_policy | default('unless-stopped') }}
-    {% if ports %}
-
-volumes:
-  data:
-    driver: local
-
-{% if network %}
-networks:
-  {{ network.name | default('bridge') }}:
-  {% if network.external %}
-    external: true
-  {% endif %}
-{% endif %}
-{% endif %}

+ 0 - 21
library/compose/n8n/compose.yaml → library/compose/n8n/compose.yaml.j2

@@ -1,24 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "n8n"
-  description: "Workflow automation and integration tool"
-  version: "0.0.1"
-  date: "2025-09-03"
-  author: "Christian Lempa"
-  tags:
-    - n8n
-    - automation
-    - workflows
-    - compose
-spec:
-  ports:
-    vars:
-      ports_http:
-        description: "HTTP port for n8n web interface"
-        type: int
-        default: 5678
----
 services:
 services:
   {{ service_name | default('n8n') }}:
   {{ service_name | default('n8n') }}:
     image: n8nio/n8n:1.110.1
     image: n8nio/n8n:1.110.1

+ 21 - 0
library/compose/n8n/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: N8N
+  description: Docker compose setup for n8n
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - n8n
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      n8n_version:
+        type: string
+        description: N8N version
+        default: latest
+
+---

+ 0 - 43
library/compose/nextcloud/compose.yaml → library/compose/nextcloud/compose.yaml.j2

@@ -1,46 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Nextcloud"
-  description: "A self-hosted file sync and share platform"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - nextcloud
-    - web
-    - file-storage
-spec:
-  ports:
-    vars:
-      ports_http:
-        description: "HTTP port for Nextcloud web interface"
-        type: int
-        default: 80
-  database:
-    vars:
-      database_type:
-        description: "Database type (mysql or postgres)"
-        type: enum
-        options: ["mysql", "postgres"]
-        default: "mysql"
-      mysql_user:
-        description: "MySQL username"
-        type: str
-        default: "nextcloud"
-      mysql_password:
-        description: "MySQL password"
-        type: str
-        default: "nextcloud"
-      mysql_database:
-        description: "MySQL database name"
-        type: str
-        default: "nextcloud"
-      mysql_root_password_random:
-        description: "Use random MySQL root password"
-        type: bool
-        default: true
----
 services:
 services:
   {{ service_name | default('nextcloud-app') }}:
   {{ service_name | default('nextcloud-app') }}:
     image: docker.io/library/nextcloud:31.0.8-apache
     image: docker.io/library/nextcloud:31.0.8-apache

+ 21 - 0
library/compose/nextcloud/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Nextcloud-App
+  description: Docker compose setup for nextcloud-app
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - nextcloud-app
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      nextcloud-app_version:
+        type: string
+        description: Nextcloud-App version
+        default: latest
+
+---

+ 0 - 13
library/compose/nginxproxymanager/compose.yaml → library/compose/nginxproxymanager/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Nginx Proxy Manager"
-  description: "An open-source reverse proxy manager"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - nginx
-    - reverse-proxy
-    - web
----
 volumes:
 volumes:
   nginxproxymanager-data:
   nginxproxymanager-data:
   nginxproxymanager-ssl:
   nginxproxymanager-ssl:

+ 21 - 0
library/compose/nginxproxymanager/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Volumes
+  description: Docker compose setup for volumes
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - volumes
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      volumes_version:
+        type: string
+        description: Volumes version
+        default: latest
+
+---

+ 0 - 22
library/compose/nodeexporter/compose.yaml

@@ -1,22 +0,0 @@
----
-kind: "compose"
-metadata:
-  name: "Node Exporter"
-  description: "A Prometheus exporter for hardware and OS metrics"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - prometheus
-    - monitoring
-    - metrics
----
-services:
-  node_exporter:
-    image: quay.io/prometheus/node-exporter:v1.9.1
-    container_name: node_exporter
-    command: "--path.rootfs=/host"
-    pid: host
-    restart: unless-stopped
-    volumes:
-      - /:/host:ro,rslave

+ 9 - 0
library/compose/nodeexporter/compose.yaml.j2

@@ -0,0 +1,9 @@
+services:
+  node_exporter:
+    image: quay.io/prometheus/node-exporter:v1.9.1
+    container_name: node_exporter
+    command: "--path.rootfs=/host"
+    pid: host
+    restart: unless-stopped
+    volumes:
+      - /:/host:ro,rslave

+ 21 - 0
library/compose/nodeexporter/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Node_Exporter
+  description: Docker compose setup for node_exporter
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - node_exporter
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      node_exporter_version:
+        type: string
+        description: Node_Exporter version
+        default: latest
+
+---

+ 0 - 13
library/compose/openwebui/compose.yaml → library/compose/openwebui/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Open Web UI"
-  description: "A web-based user interface for managing various services"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - openwebui
-    - web
-    - user-interface
----
 services:
 services:
   openwebui:
   openwebui:
     image: ghcr.io/open-webui/open-webui:v0.6.26
     image: ghcr.io/open-webui/open-webui:v0.6.26

+ 21 - 0
library/compose/openwebui/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Openwebui
+  description: Docker compose setup for openwebui
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - openwebui
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      openwebui_version:
+        type: string
+        description: Openwebui version
+        default: latest
+
+---

+ 0 - 13
library/compose/passbolt/compose.yaml → library/compose/passbolt/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Passbolt"
-  description: "An open-source password manager"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - passbolt
-    - password-manager
-    - web
----
 volumes:
 volumes:
   passbolt-db:
   passbolt-db:
   passbolt-data-gpg:
   passbolt-data-gpg:

+ 21 - 0
library/compose/passbolt/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Volumes
+  description: Docker compose setup for volumes
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - volumes
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      volumes_version:
+        type: string
+        description: Volumes version
+        default: latest
+
+---

+ 0 - 46
library/compose/pihole/compose.yaml → library/compose/pihole/compose.yaml.j2

@@ -1,49 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Pi-hole"
-  description: "An open-source DNS sinkhole"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - pihole
-    - dns
-    - ad-blocker
-spec:
-  ports:
-    vars:
-      ports_dns_tcp:
-        description: "DNS TCP port"
-        type: int
-        default: 53
-      ports_dns_udp:
-        description: "DNS UDP port"
-        type: int
-        default: 53
-      ports_dhcp:
-        description: "DHCP port"
-        type: int
-        default: 67
-      ports_http:
-        description: "HTTP port for Pi-hole web interface"
-        type: int
-        default: 8081
-      ports_https:
-        description: "HTTPS port for Pi-hole web interface"
-        type: int
-        default: 8443
-  pihole:
-    vars:
-      pihole_webpassword:
-        description: "Pi-hole web admin password"
-        type: str
-        default: ""
-      pihole_dns_upstreams:
-        description: "Pi-hole upstream DNS servers"
-        type: str
-        default: "8.8.8.8;8.8.4.4"
----
 services:
 services:
   {{ service_name | default('pihole') }}:
   {{ service_name | default('pihole') }}:
     container_name: {{ container_name | default('pihole') }}
     container_name: {{ container_name | default('pihole') }}

+ 21 - 0
library/compose/pihole/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Pihole
+  description: Docker compose setup for pihole
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - pihole
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      pihole_version:
+        type: string
+        description: Pihole version
+        default: latest
+
+---

+ 0 - 28
library/compose/portainer/compose.yaml → library/compose/portainer/compose.yaml.j2

@@ -1,31 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Portainer"
-  description: "An open-source container management tool"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - portainer
-    - container-management
-    - web
-spec:
-  ports:
-    vars:
-      ports_http:
-        description: "HTTP port for Portainer web interface"
-        type: int
-        default: 9000
-      ports_https:
-        description: "HTTPS port for Portainer web interface"
-        type: int
-        default: 9443
-      ports_edge:
-        description: "Edge agent port for Portainer"
-        type: int
-        default: 8000
----
 services:
 services:
   {{ service_name | default('portainer') }}:
   {{ service_name | default('portainer') }}:
     container_name: {{ container_name | default('portainer') }}
     container_name: {{ container_name | default('portainer') }}

+ 21 - 0
library/compose/portainer/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Portainer
+  description: Docker compose setup for portainer
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - portainer
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      portainer_version:
+        type: string
+        description: Portainer version
+        default: latest
+
+---

+ 0 - 28
library/compose/postgres/compose.yaml → library/compose/postgres/compose.yaml.j2

@@ -1,31 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "PostgreSQL"
-  description: "An open-source relational database management system"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - postgres
-    - database
-    - sql
-spec:
-  postgres:
-    vars:
-      postgres_initdb_args:
-        description: "PostgreSQL initdb arguments"
-        type: str
-        default: "--data-checksums"
-      postgres_host_auth_method:
-        description: "PostgreSQL host authentication method"
-        type: str
-        default: ""
-      postgres_secrets_enabled:
-        description: "Use PostgreSQL secrets file for password"
-        type: bool
-        default: true
----
 services:
 services:
   {{ service_name | default('postgres') }}:
   {{ service_name | default('postgres') }}:
     image: docker.io/library/postgres:17.6
     image: docker.io/library/postgres:17.6

+ 22 - 0
library/compose/postgres/template.yaml

@@ -0,0 +1,22 @@
+---
+kind: compose
+metadata:
+  name: PostgreSQL
+  description: Advanced open-source relational database
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - postgresql
+  - database
+  - sql
+  - relational
+spec:
+  general:
+    vars:
+      postgres_version:
+        type: string
+        description: PostgreSQL version
+        default: latest
+
+---

+ 0 - 13
library/compose/prometheus/compose.yaml → library/compose/prometheus/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Prometheus"
-  description: "An open-source monitoring and alerting toolkit"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - prometheus
-    - monitoring
-    - alerting
----
 volumes:
 volumes:
   prometheus-data:
   prometheus-data:
     driver: local
     driver: local

+ 21 - 0
library/compose/prometheus/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Volumes
+  description: Docker compose setup for volumes
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - volumes
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      volumes_version:
+        type: string
+        description: Volumes version
+        default: latest
+
+---

+ 0 - 13
library/compose/promtail/compose.yaml → library/compose/promtail/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Promtail"
-  description: "An open-source log collection agent"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - promtail
-    - logging
-    - grafana
----
 services:
 services:
   promtail:
   promtail:
     image: docker.io/grafana/promtail:3.5.3
     image: docker.io/grafana/promtail:3.5.3

+ 21 - 0
library/compose/promtail/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Promtail
+  description: Docker compose setup for promtail
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - promtail
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      promtail_version:
+        type: string
+        description: Promtail version
+        default: latest
+
+---

+ 0 - 13
library/compose/teleport/compose.yaml → library/compose/teleport/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Teleport"
-  description: "An open-source access plane for managing SSH access"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - teleport
-    - ssh
-    - access-management
----
 # -- (Optional) When using Traefik, use this section
 # -- (Optional) When using Traefik, use this section
 # networks:
 # networks:
 #   your-traefik-network:
 #   your-traefik-network:

+ 21 - 0
library/compose/teleport/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Teleport
+  description: Docker compose setup for teleport
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - teleport
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      teleport_version:
+        type: string
+        description: Teleport version
+        default: latest
+
+---

+ 18 - 0
library/compose/traefik.authentik-middleware/middleware.yaml.j2

@@ -0,0 +1,18 @@
+http:
+  middlewares:
+    {{ middleware_name }}:
+      forwardAuth:
+        address: {{ authentik_outpost_url }}/outpost.goauthentik.io/auth/traefik
+        trustForwardHeader: true
+        authResponseHeaders:
+          - X-authentik-username
+          - X-authentik-groups
+          - X-authentik-email
+          - X-authentik-name
+          - X-authentik-uid
+          - X-authentik-jwt
+          - X-authentik-meta-jwks
+          - X-authentik-meta-outpost
+          - X-authentik-meta-provider
+          - X-authentik-meta-app
+          - X-authentik-meta-version

+ 26 - 0
library/compose/traefik.authentik-middleware/template.yaml

@@ -0,0 +1,26 @@
+---
+kind: "compose"
+metadata:
+  name: "Traefik Authentik Middleware"
+  description: "Authentication middleware for Traefik using Authentik forward auth"
+  version: "0.1.0"
+  author: "Christian Lempa"
+  date: "2025-09-28"
+  tags:
+    - traefik
+    - authentik
+    - middleware
+    - authentication
+    - forward-auth
+spec:
+  general:
+    vars:
+      authentik_outpost_url:
+        type: "url"
+        description: "Authentik outpost URL"
+        default: "http://your-authentik-outpost-fqdn:9000"
+      middleware_name:
+        type: "string"
+        description: "Name of the middleware"
+        default: "authentik-middleware"
+---

+ 21 - 0
library/compose/traefik.external-service/external-service.yaml.j2

@@ -0,0 +1,21 @@
+http:
+  # Router Configuration
+  routers:
+    {{ service_name }}-router:
+      rule: "Host(`{{ service_host }}`)"
+      service: {{ service_name }}
+      priority: {{ router_priority }}
+      entryPoints:
+        - web
+        {% if tls_enabled %}
+        - websecure
+      tls:
+        certResolver: {{ cert_resolver }}
+        {% endif %}
+
+  # Service Configuration
+  services:
+    {{ service_name }}:
+      loadBalancer:
+        servers:
+          - url: "{{ service_url }}"

+ 17 - 0
library/compose/traefik.external-service/router.yaml.j2

@@ -0,0 +1,17 @@
+http:
+  routers:
+    {{ service_name }}:
+      rule: "Host(`{{ hostname }}`)"
+      entryPoints:
+        - https
+      tls:
+        certResolver: {{ cert_resolver }}
+      service: {{ service_name }}
+      middlewares:
+        - {{ middleware_name }}@file
+
+  services:
+    {{ service_name }}:
+      loadBalancer:
+        servers:
+          - url: "http://{{ backend_ip }}:{{ backend_port }}"

+ 41 - 0
library/compose/traefik.external-service/template.yaml

@@ -0,0 +1,41 @@
+---
+kind: "compose"
+metadata:
+  name: "Traefik External Service Config"
+  description: "Configuration for routing external services through Traefik"
+  version: "0.1.0"
+  author: "Christian Lempa"
+  date: "2025-09-28"
+  tags:
+    - traefik
+    - external-service
+    - routing
+    - config
+spec:
+  general:
+    vars:
+      service_name:
+        type: "string"
+        description: "Name of the external service"
+        default: "your-local-service"
+      service_host:
+        type: "hostname"
+        description: "Domain for the service (e.g., service.yourdomain.com)"
+        default: "your-service.your-domain.com"
+      service_url:
+        type: "url"
+        description: "URL of the external service"
+        default: "http://your-local-service:port"
+      router_priority:
+        type: "int"
+        description: "Router priority (higher = more precedence)"
+        default: 1000
+      tls_enabled:
+        type: "bool"
+        description: "Enable TLS/SSL"
+        default: true
+      cert_resolver:
+        type: "string"
+        description: "Certificate resolver name"
+        default: "cloudflare"
+---

+ 26 - 0
library/compose/traefik.grafana/router.yaml.j2

@@ -0,0 +1,26 @@
+http:
+  routers:
+    {{ service_name }}:
+      rule: "Host(`{{ hostname }}`)"
+      entryPoints:
+        - https
+      tls:
+        certResolver: {{ cert_resolver }}
+      service: {{ service_name }}
+      middlewares:
+{% if enable_auth_middleware %}
+        - {{ auth_middleware_name }}@file
+{% endif %}
+        - {{ service_name }}-headers
+
+  services:
+    {{ service_name }}:
+      loadBalancer:
+        servers:
+          - url: "http://{{ grafana_backend_ip }}:{{ grafana_backend_port }}/"
+
+  middlewares:
+    {{ service_name }}-headers:
+      headers:
+        customRequestHeaders:
+          X-WEBAUTH-USER: "{{ grafana_username }}"

+ 15 - 0
library/compose/traefik.guacamole/router.yaml.j2

@@ -0,0 +1,15 @@
+http:
+  routers:
+    {{ service_name }}:
+      rule: "Host(`{{ hostname }}`)"
+      entryPoints:
+        - https
+      tls:
+        certResolver: {{ cert_resolver }}
+      service: {{ service_name }}
+
+  services:
+    {{ service_name }}:
+      loadBalancer:
+        servers:
+          - url: "http://{{ guacamole_backend_ip }}:{{ guacamole_backend_port }}/"

+ 6 - 0
library/compose/traefik.ldap-middleware/middleware.yaml.j2

@@ -0,0 +1,6 @@
+http:
+  middlewares:
+    {{ middleware_name }}:
+      forwardAuth:
+        address: "http://{{ ldap_auth_backend_ip }}:{{ ldap_auth_backend_port }}/auth"
+        trustForwardHeader: true

+ 31 - 0
library/compose/traefik.nextcloud/router.yaml.j2

@@ -0,0 +1,31 @@
+http:
+  routers:
+    {{ service_name }}:
+      rule: "Host(`{{ hostname }}`)"
+      entryPoints:
+        - https
+      tls:
+        certResolver: {{ cert_resolver }}
+      service: {{ service_name }}
+      middlewares:
+        - {{ service_name }}-dav
+        - {{ service_name }}-secure-headers
+
+  services:
+    {{ service_name }}:
+      loadBalancer:
+        servers:
+          - url: "http://{{ nextcloud_backend_ip }}:{{ nextcloud_backend_port }}/"
+
+  middlewares:
+    {{ service_name }}-dav:
+      redirectRegex:
+        regex: "https://(.*)/.well-known/(?:card|cal)dav"
+        replacement: "https://${1}/remote.php/dav/"
+
+    {{ service_name }}-secure-headers:
+      headers:
+        accessControlMaxAge: 100
+        hostsProxyHeaders:
+          - "X-Forwarded-Host"
+        referrerPolicy: "same-origin"

+ 23 - 0
library/compose/traefik.pihole/router.yaml.j2

@@ -0,0 +1,23 @@
+http:
+  routers:
+    {{ service_name }}:
+      rule: "Host(`{{ hostname }}`)"
+      entryPoints:
+        - https
+      tls:
+        certResolver: {{ cert_resolver }}
+      service: {{ service_name }}
+      middlewares:
+        - {{ service_name }}-headers
+
+  services:
+    {{ service_name }}:
+      loadBalancer:
+        servers:
+          - url: "http://{{ pihole_backend_ip }}:{{ pihole_backend_port }}/"
+
+  middlewares:
+    {{ service_name }}-headers:
+      headers:
+        customRequestHeaders:
+          Host: "{{ hostname }}"

+ 26 - 0
library/compose/traefik.proxmox/router.yaml.j2

@@ -0,0 +1,26 @@
+http:
+  routers:
+    {{ service_name }}:
+      rule: "Host(`{{ hostname }}`)"
+      entryPoints:
+        - https
+      tls:
+        certResolver: {{ cert_resolver }}
+      service: {{ service_name }}
+      middlewares:
+{% if enable_auth_middleware %}
+        - {{ auth_middleware_name }}@file
+{% endif %}
+        - {{ service_name }}-headers
+
+  services:
+    {{ service_name }}:
+      loadBalancer:
+        servers:
+          - url: "https://{{ proxmox_backend_ip }}:{{ proxmox_backend_port }}/"
+
+  middlewares:
+    {{ service_name }}-headers:
+      headers:
+        customRequestHeaders:
+          Host: "{{ hostname }}"

+ 15 - 0
library/compose/traefik.vaultwarden/router.yaml.j2

@@ -0,0 +1,15 @@
+http:
+  routers:
+    {{ service_name }}:
+      rule: "Host(`{{ hostname }}`)"
+      entryPoints:
+        - https
+      tls:
+        certResolver: {{ cert_resolver }}
+      service: {{ service_name }}
+
+  services:
+    {{ service_name }}:
+      loadBalancer:
+        servers:
+          - url: "http://{{ vaultwarden_backend_ip }}:{{ vaultwarden_backend_port }}/"

+ 0 - 70
library/compose/traefik/compose.yaml

@@ -1,70 +0,0 @@
----
-kind: "compose"
-metadata:
-  name: "Traefik"
-  description: "An open-source edge router for microservices"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - traefik
-    - reverse-proxy
-    - load-balancer
-files:
-  - config/traefik.yaml
-spec:
-  traefik:
-    vars:
-      acme_email:
-        description: "Email address for ACME (Let's Encrypt) registration"
-        type: str
-        default: ""
-  database:
-    vars:
-      database_name:
-        description: "Name of the database"
-        type: str
-        default: ""
----
-services:
-  {{ service_name | default('traefik') }}:
-    image: docker.io/library/traefik:v3.5.1
-    container_name: {{ container_name | default('traefik') }}
-    {% if ports_enabled %}
-    ports:
-      - 80:80
-      - 443:443
-      # --> (Optional) Enable Dashboard, don't do in production
-      # - 8080:8080
-      # <--
-    {% endif %}
-    volumes:
-      - /run/docker.sock:/run/docker.sock:ro
-      - ./config/:/etc/traefik/:ro
-      - ./certs/:/var/traefik/certs/:rw
-    environment:
-      - TZ={{ container_timezone | default('UTC') }}
-      {% if acme_email -%}
-      - CF_DNS_API_TOKEN={{ acme_email }}
-      {% endif %}
-      {% if traefik_host -%}
-      - TRAEFIK_HOST={{ traefik_host }}
-      {% endif %}
-      {% if database_name -%}
-      - DB_NAME={{ database_name }}
-      {% endif %}
-    {% if network_enabled %}
-    networks:
-      - {{ network_name | default('frontend') }}
-    {% endif %}
-    restart: {{ restart_policy | default('unless-stopped') }}
-
-{% if network_enabled %}
-networks:
-  {{ network_name | default('frontend') }}:
-    {% if network_external %}
-    external: true
-    {% else %}
-    driver: bridge
-    {% endif %}
-{% endif %}

+ 36 - 0
library/compose/traefik/compose.yaml.j2

@@ -0,0 +1,36 @@
+services:
+  {{ service_name }}:
+    image: docker.io/library/traefik:{{ traefik_version }}
+    container_name: {{ container_name }}
+    {% if ports_enabled %}
+    ports:
+      - "80:80"
+      - "443:443"
+      {% if dashboard_enabled %}
+      - "8080:8080"  # Dashboard (don't use in production)
+      {% endif %}
+    {% endif %}
+    volumes:
+      - /var/run/docker.sock:/var/run/docker.sock:ro
+      - ./config/:/etc/traefik/:ro
+      - ./certs/:/var/traefik/certs/:rw
+    environment:
+      - TZ={{ container_timezone }}
+      {% if acme_email %}
+      - ACME_EMAIL={{ acme_email }}
+      {% endif %}
+    {% if network_enabled %}
+    networks:
+      - {{ network_name }}
+    {% endif %}
+    restart: {{ restart_policy }}
+
+{% if network_enabled %}
+networks:
+  {{ network_name }}:
+    {% if network_external %}
+    external: true
+    {% else %}
+    driver: bridge
+    {% endif %}
+{% endif %}

+ 37 - 0
library/compose/traefik/template.yaml

@@ -0,0 +1,37 @@
+---
+kind: "compose"
+metadata:
+  name: "Traefik"
+  description: "Modern reverse proxy and load balancer for microservices"
+  version: "0.1.0"
+  author: "Christian Lempa"
+  date: "2025-09-28"
+  tags:
+    - traefik
+    - reverse-proxy
+    - load-balancer
+    - edge-router
+spec:
+  general:
+    vars:
+      traefik_version:
+        type: "string"
+        description: "Traefik version"
+        default: "v3.5.1"
+      acme_email:
+        type: "email"
+        description: "Email address for ACME (Let's Encrypt) registration"
+        default: ""
+  ports:
+    prompt: "Expose ports via 'ports' mapping?"
+    toggle: "ports_enabled"
+    vars:
+      ports_enabled:
+        type: "bool"
+        description: "Expose ports via 'ports' mapping"
+        default: true
+      dashboard_enabled:
+        type: "bool"
+        description: "Enable Traefik dashboard (don't use in production)"
+        default: false
+---

+ 0 - 13
library/compose/twingate_connector/compose.yaml → library/compose/twingate_connector/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Twingate Connector"
-  description: "A connector for Twingate"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - twingate
-    - connector
-    - networking
----
 services:
 services:
   twingate_connector:
   twingate_connector:
     container_name: twingate_connector
     container_name: twingate_connector

+ 21 - 0
library/compose/twingate_connector/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Twingate_Connector
+  description: Docker compose setup for twingate_connector
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - twingate_connector
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      twingate_connector_version:
+        type: string
+        description: Twingate_Connector version
+        default: latest
+
+---

+ 0 - 13
library/compose/uptimekuma/compose.yaml → library/compose/uptimekuma/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Uptime Kuma"
-  description: "A self-hosted status monitoring solution"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - uptime-kuma
-    - monitoring
-    - self-hosted
----
 volumes:
 volumes:
   uptimekuma-data:
   uptimekuma-data:
     driver: local
     driver: local

+ 21 - 0
library/compose/uptimekuma/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Volumes
+  description: Docker compose setup for volumes
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - volumes
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      volumes_version:
+        type: string
+        description: Volumes version
+        default: latest
+
+---

+ 0 - 13
library/compose/wazuh/compose.yaml → library/compose/wazuh/compose.yaml.j2

@@ -1,16 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Wazuh"
-  description: "A security monitoring platform"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - wazuh
-    - security
-    - monitoring
----
 services:
 services:
   wazuh.manager:
   wazuh.manager:
     image: docker.io/wazuh/wazuh-manager:4.12.0
     image: docker.io/wazuh/wazuh-manager:4.12.0

+ 21 - 0
library/compose/wazuh/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Wazuh.Manager
+  description: Docker compose setup for wazuh.manager
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - wazuh.manager
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      wazuh.manager_version:
+        type: string
+        description: Wazuh.Manager version
+        default: latest
+
+---

+ 0 - 25
library/compose/whoami/compose.yaml → library/compose/whoami/compose.yaml.j2

@@ -1,28 +1,3 @@
----
-kind: "compose"
-metadata:
-  name: "Whoami"
-  description: "Simple HTTP service that returns information about the request"
-  version: "0.0.1"
-  date: "2023-10-01"
-  author: "Christian Lempa"
-  tags:
-    - "traefik"
-    - "whoami"
-    - "http"
-    - "testing"
-spec:
-  ports:
-    vars:
-      ports_http:
-        description: "HTTP port for whoami service"
-        type: int
-        default: 8080
-      ports_https:
-        description: "HTTPS port for whoami service"
-        type: int
-        default: 8443
----
 services:
 services:
   {{ service_name | default('whoami') }}:
   {{ service_name | default('whoami') }}:
     image: traefik/whoami
     image: traefik/whoami

+ 21 - 0
library/compose/whoami/template.yaml

@@ -0,0 +1,21 @@
+---
+kind: compose
+metadata:
+  name: Whoami
+  description: Docker compose setup for whoami
+  version: 0.1.0
+  author: Christian Lempa
+  date: '2025-09-28'
+  tags:
+  - whoami
+  - docker
+  - compose
+spec:
+  general:
+    vars:
+      whoami_version:
+        type: string
+        description: Whoami version
+        default: latest
+
+---