services...

This commit is contained in:
plasmagoat 2025-07-30 00:22:33 +02:00
parent 73d2f44d74
commit 8552656731
15 changed files with 918 additions and 490 deletions

View file

@ -20,7 +20,7 @@ This documentation is automatically generated from your colmena flake configurat
## 🚀 Quick Actions ## 🚀 Quick Actions
### View Current Status ### View Current Status
\`\`\`bash ```bash
# Service status across fleet (if homelab CLI is available) # Service status across fleet (if homelab CLI is available)
homelab services --global homelab services --global
@ -29,22 +29,22 @@ homelab backups --global
# Overall status # Overall status
homelab status homelab status
\`\`\` ```
### Update Documentation ### Update Documentation
\`\`\`bash ```bash
# Regenerate all documentation # Regenerate all documentation
homelab-generate-docs ./docs homelab-generate-docs ./docs
# Generate in different directory # Generate in different directory
homelab-generate-docs /path/to/output homelab-generate-docs /path/to/output
\`\`\` ```
## 📋 Quick Stats ## 📋 Quick Stats
- **Total Nodes**: 2 - **Total Nodes**: 2
- **Homelab-Enabled Nodes**: 2 - **Homelab-Enabled Nodes**: 2
- **Generated**: tir 29 jul 16:57:16 CEST 2025 - **Generated**: ons 30 jul 00:20:46 CEST 2025
## 🛠️ Management Tools ## 🛠️ Management Tools

View file

@ -11,15 +11,15 @@
|--------|-------| |--------|-------|
| Total Nodes | 2 | | Total Nodes | 2 |
| Homelab-Enabled Nodes | 2 | | Homelab-Enabled Nodes | 2 |
| Unique Services | 1 | | Unique Services | 4 |
| Service Instances | 1 | | Service Instances | 4 |
## Node Status ## Node Status
| Node | Homelab | Environment | Services | Monitoring | Backups | Proxy | | Node | Homelab | Environment | Services | Monitoring | Backups | Proxy |
|------|---------|-------------|----------|------------|---------|-------| |------|---------|-------------|----------|------------|---------|-------|
| `photos` | ✅ | production | 1 | ✅ | ❌ | ❌ | | `photos` | ✅ | production | 1 | ✅ | ❌ | ❌ |
| `sandbox` | ✅ | production | 0 | ✅ | ✅ | ❌ | | `sandbox` | ✅ | production | 3 | ✅ | ✅ | ❌ |
--- ---

View file

@ -26,7 +26,7 @@
| Node | Service Count | Services | | Node | Service Count | Services |
|------|---------------|----------| |------|---------------|----------|
| `photos` | 1 | minio | | `photos` | 1 | minio |
| `sandbox` | 0 | | | `sandbox` | 3 | gatus, grafana, prometheus |
--- ---

View file

@ -61,10 +61,10 @@
| Service | Enabled | Port | Description | Tags | | Service | Enabled | Port | Description | Tags |
|---------|---------|------|-------------|------| |---------|---------|------|-------------|------|
| `gatus` | | 8080 | Gatus Status Page | | | `gatus` | | 8080 | Gatus Status Page | |
| `grafana` | | 3000 | Grafana Metrics Dashboard | | | `grafana` | | 3000 | Grafana Metrics Dashboard | |
| `minio` | ❌ | 9000 | minio | | | `minio` | ❌ | 9000 | minio | |
| `prometheus` | | 9090 | Prometheus Monitoring Server | | | `prometheus` | | 9090 | Prometheus Monitoring Server | |
--- ---

View file

@ -1,10 +1,13 @@
# Service Catalog # Service Catalog
> Available services and their configuration options > Complete service documentation with core options, feature integrations, and smart defaults
> >
> Generated on: $(date) > Generated on: $(date)
This document catalogs all available homelab services, their configuration options, and integration capabilities. This document provides comprehensive documentation for homelab services, organized by:
- **Core Service Options**: The main service configuration
- **Feature Integrations**: Available monitoring, logging, and proxy features
- **Service Defaults**: What this service configures by default for each feature
## Overview ## Overview
@ -12,142 +15,252 @@ This document catalogs all available homelab services, their configuration optio
## Service Integration Matrix ## Service Integration Matrix
| Service | Monitoring | Logging | Proxy | Auth Default | | Service | Core Options | Monitoring | Logging | Proxy | Deployments |
|---------|------------|---------|-------|--------------| |---------|--------------|------------|---------|-------|-------------|
| `gatus` | ❌ | ❌ | ❌ | 🌐 | | `gatus` | 11 | 📊 | 📝 | 🔀 | 1 |
| `grafana` | ❌ | ❌ | ❌ | 🌐 | | `grafana` | 3 | 📊 | 📝 | 🔀 | 1 |
| `minio` | ❌ | ❌ | ❌ | 🌐 | | `minio` | 4 | ❌ | ❌ | ❌ | 1 |
| `prometheus` | ❌ | ❌ | ❌ | 🌐 | | `prometheus` | 12 | 📊 | 📝 | 🔀 | 1 |
**Legend:** ✅ = Enabled by default, ❌ = Available but disabled, 🔒 = Auth required, 🌐 = Public access **Legend:** 📊📝🔀 = Feature available, ❌ = Feature not available
## Service Reference ## Service Documentation
### gatus ### gatus
**Description:** Gatus Status Page **Deployment Status:** 1/2 nodes have this service enabled
**Default Port:** `8080` #### Core Service Options
**Current Deployments:** 0 instance(s) on: The main configuration options for gatus:
#### Default Integration Status
| Integration | Status | Default Configuration |
|-------------|--------|----------------------|
| 📊 Monitoring | ❌ Disabled | Available but requires `monitoring.enable = true` |
| 📝 Logging | ❌ Disabled | Available but requires `logging.enable = true` |
| 🔀 Proxy | ❌ Disabled | Available but requires `proxy.enable = true` |
#### Core Configuration
```nix ```nix
homelab.services.gatus = { homelab.services.gatus = {
enable = true; alerting = {}; # Gatus alerting configuration
port = 8080; description = Gatus Status Page; # No description
description = "Gatus Status Page"; enable = false; # Whether to enable Gatus Status Page.
extraConfig = {}; # Additional Gatus configuration options
# Default integrations (adjust as needed) port = 8080; # No description
# monitoring.enable = true; # ❌ Disabled by default storage = {
# logging.enable = true; # ❌ Disabled by default "type": "memory"
# proxy.enable = true; # ❌ Disabled by default }; # Gatus storage configuration
ui.buttons = [
{
"link": "https://grafana.procopius.dk",
"name": "Grafana"
},
{
"link": "https://prometheus.procopius.dk",
"name": "Prometheus"
}
]; # Navigation buttons in the Gatus interface
ui.header = Homelab Services Status; # Header text for the Gatus interface
ui.link = https://status.procopius.dk; # Link in the Gatus header
ui.title = Homelab Status; # Title for the Gatus web interface
web.address = 0.0.0.0; # Web interface bind address
}; };
``` ```
#### Service-Specific Options #### Feature Integrations
Available configuration options for gatus: ##### 📊 Monitoring Integration
Available monitoring options:
```nix ```nix
homelab.services.gatus = { homelab.services.gatus = {
# ... core options above ... # ... core options above ...
# Service-specific configuration monitoring.enable = true; # Enable monitoring for gatus
alerting = {}; monitoring.extraLabels = {}; # No description
extraConfig = {}; monitoring.healthCheck.conditions = [
storage = {"type":"memory"}; "[STATUS] == 200"
ui = {"buttons":[{"link":"https://grafana.procopius.dk","name":"Grafana"},{"link":"https://prometheus.procopius.dk","name":"Prometheus"}],"header":"Homelab Services Status","link":"https://status.procopius.dk","title":"Homelab Status"}; ]; # Health check conditions. Setting conditions enables health checks.
web = {"address":"0.0.0.0"}; monitoring.healthCheck.enable = true; # No description
monitoring.healthCheck.extraChecks = []; # Additional health checks. Adding checks enables health monitoring.
# monitoring.healthCheck.path = <null or string>; # Health check endpoint path. Setting this enables health checks.
monitoring.metrics.enable = false; # No description
monitoring.metrics.extraEndpoints = []; # Additional metrics endpoints. Adding endpoints enables metrics collection.
# monitoring.metrics.path = <null or string>; # Metrics endpoint path. Setting this enables metrics collection.
}; };
``` ```
**gatus sets these monitoring defaults:**
```nix
enable = true;
extraLabels = {};
healthCheck = {"conditions":["[STATUS] == 200"],"enable":true,"extraChecks":[],"path":null};
metrics = {"enable":false,"extraEndpoints":[],"path":null};
```
##### 📝 Logging Integration
Available logging options:
```nix
homelab.services.gatus = {
# ... core options above ...
logging.enable = false; # Enable logging for gatus
logging.extraLabels = {}; # No description
logging.extraSources = []; # No description
logging.files = []; # No description
# logging.multiline = <null or (submodule)>; # No description
logging.parsing.extractFields = []; # No description
# logging.parsing.regex = <null or string>; # No description
};
```
**gatus sets these logging defaults:**
```nix
enable = false;
extraLabels = {};
extraSources = [];
files = [];
multiline = null;
parsing = {"extractFields":[],"regex":null};
```
##### 🔀 Proxy Integration
Available proxy options:
```nix
homelab.services.gatus = {
# ... core options above ...
proxy.additionalSubdomains = []; # No description
proxy.enable = true; # Enable reverse proxy for gatus
proxy.enableAuth = false; # No description
proxy.subdomain = gatus; # No description
};
```
**gatus sets these proxy defaults:**
```nix
additionalSubdomains = [];
enable = true;
enableAuth = false;
subdomain = gatus;
```
--- ---
### grafana ### grafana
**Description:** Grafana Metrics Dashboard **Deployment Status:** 1/2 nodes have this service enabled
**Default Port:** `3000` #### Core Service Options
**Current Deployments:** 0 instance(s) on: The main configuration options for grafana:
#### Default Integration Status
| Integration | Status | Default Configuration |
|-------------|--------|----------------------|
| 📊 Monitoring | ❌ Disabled | Available but requires `monitoring.enable = true` |
| 📝 Logging | ❌ Disabled | Available but requires `logging.enable = true` |
| 🔀 Proxy | ❌ Disabled | Available but requires `proxy.enable = true` |
#### Core Configuration
```nix ```nix
homelab.services.grafana = { homelab.services.grafana = {
enable = true; description = Grafana Metrics Dashboard; # No description
port = 3000; enable = false; # Whether to enable Grafana Dashboard.
description = "Grafana Metrics Dashboard"; port = 3000; # No description
# Default integrations (adjust as needed)
# monitoring.enable = true; # ❌ Disabled by default
# logging.enable = true; # ❌ Disabled by default
# proxy.enable = true; # ❌ Disabled by default
}; };
``` ```
#### Feature Integrations
##### 📊 Monitoring Integration
Available monitoring options:
```nix
homelab.services.grafana = {
# ... core options above ...
monitoring.enable = true; # Enable monitoring for grafana
monitoring.extraLabels = {}; # No description
monitoring.healthCheck.conditions = [
"[STATUS] == 200"
]; # Health check conditions. Setting conditions enables health checks.
monitoring.healthCheck.enable = true; # No description
monitoring.healthCheck.extraChecks = []; # Additional health checks. Adding checks enables health monitoring.
# monitoring.healthCheck.path = <null or string>; # Health check endpoint path. Setting this enables health checks.
monitoring.metrics.enable = false; # No description
monitoring.metrics.extraEndpoints = []; # Additional metrics endpoints. Adding endpoints enables metrics collection.
# monitoring.metrics.path = <null or string>; # Metrics endpoint path. Setting this enables metrics collection.
};
```
**grafana sets these monitoring defaults:**
```nix
enable = true;
extraLabels = {};
healthCheck = {"conditions":["[STATUS] == 200"],"enable":true,"extraChecks":[],"path":null};
metrics = {"enable":false,"extraEndpoints":[],"path":null};
```
##### 📝 Logging Integration
Available logging options:
```nix
homelab.services.grafana = {
# ... core options above ...
logging.enable = false; # Enable logging for grafana
logging.extraLabels = {}; # No description
logging.extraSources = []; # No description
logging.files = []; # No description
# logging.multiline = <null or (submodule)>; # No description
logging.parsing.extractFields = []; # No description
# logging.parsing.regex = <null or string>; # No description
};
```
**grafana sets these logging defaults:**
```nix
enable = false;
extraLabels = {};
extraSources = [];
files = [];
multiline = null;
parsing = {"extractFields":[],"regex":null};
```
##### 🔀 Proxy Integration
Available proxy options:
```nix
homelab.services.grafana = {
# ... core options above ...
proxy.additionalSubdomains = []; # No description
proxy.enable = true; # Enable reverse proxy for grafana
proxy.enableAuth = false; # No description
proxy.subdomain = grafana; # No description
};
```
**grafana sets these proxy defaults:**
```nix
additionalSubdomains = [];
enable = true;
enableAuth = false;
subdomain = grafana;
```
--- ---
### minio ### minio
**Description:** minio **Deployment Status:** 1/2 nodes have this service enabled
**Default Port:** `9000` #### Core Service Options
**Current Deployments:** 1 instance(s) on: photos The main configuration options for minio:
#### Default Integration Status
| Integration | Status | Default Configuration |
|-------------|--------|----------------------|
| 📊 Monitoring | ❌ Disabled | Available but requires `monitoring.enable = true` |
| 📝 Logging | ❌ Disabled | Available but requires `logging.enable = true` |
| 🔀 Proxy | ❌ Disabled | Available but requires `proxy.enable = true` |
#### Core Configuration
```nix ```nix
homelab.services.minio = { homelab.services.minio = {
enable = true; enable = false; # Whether to enable Minio Object Storage.
port = 9000; openFirewall = true; # Whether to open the ports specified in `port` and `webPort` in the firewall.
description = "minio"; port = 9000; # Port of the server.
webPort = 9001; # Port of the web UI (console).
# Default integrations (adjust as needed)
# monitoring.enable = true; # ❌ Disabled by default
# logging.enable = true; # ❌ Disabled by default
# proxy.enable = true; # ❌ Disabled by default
};
```
#### Service-Specific Options
Available configuration options for minio:
```nix
homelab.services.minio = {
# ... core options above ...
# Service-specific configuration
openFirewall = true;
webPort = 9001;
}; };
``` ```
@ -155,75 +268,132 @@ homelab.services.minio = {
### prometheus ### prometheus
**Description:** Prometheus Monitoring Server **Deployment Status:** 1/2 nodes have this service enabled
**Default Port:** `9090` #### Core Service Options
**Current Deployments:** 0 instance(s) on: The main configuration options for prometheus:
#### Default Integration Status
| Integration | Status | Default Configuration |
|-------------|--------|----------------------|
| 📊 Monitoring | ❌ Disabled | Available but requires `monitoring.enable = true` |
| 📝 Logging | ❌ Disabled | Available but requires `logging.enable = true` |
| 🔀 Proxy | ❌ Disabled | Available but requires `proxy.enable = true` |
#### Core Configuration
```nix ```nix
homelab.services.prometheus = { homelab.services.prometheus = {
enable = true; alertmanager.enable = true; # Enable integration with Alertmanager
port = 9090; alertmanager.url = alertmanager.lab:9093; # Alertmanager URL
description = "Prometheus Monitoring Server"; description = Prometheus Monitoring Server; # No description
enable = false; # Whether to enable Prometheus Monitoring Server.
# Default integrations (adjust as needed) extraAlertingRules = []; # Additional alerting rules
# monitoring.enable = true; # ❌ Disabled by default extraFlags = []; # Extra command line flags
# logging.enable = true; # ❌ Disabled by default extraScrapeConfigs = []; # Additional scrape configurations
# proxy.enable = true; # ❌ Disabled by default globalConfig = {
"evaluation_interval": "15s",
"scrape_interval": "15s"
}; # Global Prometheus configuration
port = 9090; # No description
retention = 15d; # How long to retain metrics data
ruleFiles = []; # Additional rule files to load
systemdServices = [
"prometheus.service",
"prometheus"
]; # Systemd services to monitor
}; };
``` ```
#### Service-Specific Options #### Feature Integrations
Available configuration options for prometheus: ##### 📊 Monitoring Integration
Available monitoring options:
```nix ```nix
homelab.services.prometheus = { homelab.services.prometheus = {
# ... core options above ... # ... core options above ...
# Service-specific configuration monitoring.enable = true; # Enable monitoring for prometheus
alertmanager = {"enable":true,"url":"alertmanager.lab:9093"}; monitoring.extraLabels = {}; # No description
extraAlertingRules = []; monitoring.healthCheck.conditions = [
extraFlags = []; "[STATUS] == 200"
extraScrapeConfigs = []; ]; # Health check conditions. Setting conditions enables health checks.
globalConfig = {"evaluation_interval":"15s","scrape_interval":"15s"}; monitoring.healthCheck.enable = true; # No description
retention = 15d; monitoring.healthCheck.extraChecks = []; # Additional health checks. Adding checks enables health monitoring.
ruleFiles = []; # monitoring.healthCheck.path = <null or string>; # Health check endpoint path. Setting this enables health checks.
systemdServices = ["prometheus.service","prometheus"]; monitoring.metrics.enable = false; # No description
monitoring.metrics.extraEndpoints = []; # Additional metrics endpoints. Adding endpoints enables metrics collection.
# monitoring.metrics.path = <null or string>; # Metrics endpoint path. Setting this enables metrics collection.
}; };
``` ```
**prometheus sets these monitoring defaults:**
```nix
enable = true;
extraLabels = {};
healthCheck = {"conditions":["[STATUS] == 200"],"enable":true,"extraChecks":[],"path":null};
metrics = {"enable":false,"extraEndpoints":[],"path":null};
```
##### 📝 Logging Integration
Available logging options:
```nix
homelab.services.prometheus = {
# ... core options above ...
logging.enable = false; # Enable logging for prometheus
logging.extraLabels = {}; # No description
logging.extraSources = []; # No description
logging.files = []; # No description
# logging.multiline = <null or (submodule)>; # No description
logging.parsing.extractFields = []; # No description
# logging.parsing.regex = <null or string>; # No description
};
```
**prometheus sets these logging defaults:**
```nix
enable = false;
extraLabels = {};
extraSources = [];
files = [];
multiline = null;
parsing = {"extractFields":[],"regex":null};
```
##### 🔀 Proxy Integration
Available proxy options:
```nix
homelab.services.prometheus = {
# ... core options above ...
proxy.additionalSubdomains = []; # No description
proxy.enable = true; # Enable reverse proxy for prometheus
proxy.enableAuth = false; # No description
proxy.subdomain = prometheus; # No description
};
```
**prometheus sets these proxy defaults:**
```nix
additionalSubdomains = [];
enable = true;
enableAuth = false;
subdomain = prometheus;
```
--- ---
## Integration Summary ## Feature Reference
### Available Integration Types ### Integration Features
| Integration | Purpose | Default Behavior | Configuration | Homelab services can integrate with three main features:
|-------------|---------|------------------|---------------|
| **📊 Monitoring** | Prometheus metrics + health checks | Service-dependent | `monitoring.enable = true` |
| **📝 Logging** | Centralized log collection | Service-dependent | `logging.enable = true` |
| **🔀 Proxy** | Reverse proxy with SSL + auth | Service-dependent | `proxy.enable = true` |
### Integration Benefits - **📊 Monitoring**: Prometheus metrics and health checks
- **📝 Logging**: Centralized log collection with Promtail/Loki
- **🔀 Proxy**: Reverse proxy with SSL and authentication
- **🔄 Automatic Discovery:** Enabled integrations are automatically discovered by fleet-wide services Each service can import these features and set service-specific defaults.
- **📊 Unified Monitoring:** All metrics and health checks appear in Prometheus/Grafana
- **📝 Centralized Logging:** All logs are collected and indexed in Loki
- **🌐 Consistent Access:** All services get consistent subdomain access with SSL
- **🎯 Smart Defaults:** Each service comes with sensible default configurations
--- ---
*This service catalog is generated from actual service configurations across your homelab fleet.* *This documentation is generated from actual NixOS module evaluations.*

View file

@ -42,9 +42,9 @@
}; };
# services.loki.enable = true; # services.loki.enable = true;
# services.prometheus.enable = true; services.prometheus.enable = true;
# services.grafana.enable = true; services.grafana.enable = true;
# services.gatus.enable = true; services.gatus.enable = true;
}; };
system.stateVersion = "25.05"; system.stateVersion = "25.05";

View file

@ -6,9 +6,18 @@ serviceName: {
with lib; let with lib; let
cfg = config.homelab.services.${serviceName}; cfg = config.homelab.services.${serviceName};
homelabCfg = config.homelab; homelabCfg = config.homelab;
shouldEnableLogging =
cfg.logging.files
!= []
|| cfg.logging.extraSources != [];
in { in {
options.homelab.services.${serviceName}.logging = { options.homelab.services.${serviceName}.logging = {
enable = mkEnableOption "logging for ${serviceName}"; enable = mkOption {
type = types.bool;
description = "Enable logging for ${serviceName}";
default = shouldEnableLogging;
};
files = mkOption { files = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
@ -51,37 +60,33 @@ in {
}; };
}; };
config = mkIf (cfg.enable && cfg.logging.enable) { config = mkIf cfg.enable {
homelab.logging.sources = homelab.logging.sources = mkIf cfg.logging.enable (
[ # Only create file source if files are specified
{ (optional (cfg.logging.files != []) {
name = "${serviceName}-logs"; name = "${serviceName}-logs";
type = "file"; type = "file";
files = { files = {
paths = cfg.logging.files; paths = cfg.logging.files;
multiline = cfg.logging.multiline; multiline = cfg.logging.multiline;
};
labels =
cfg.logging.extraLabels
// {
service = serviceName;
node = homelabCfg.hostname;
environment = homelabCfg.environment;
}; };
labels = pipelineStages =
cfg.logging.extraLabels (optional (cfg.logging.parsing.regex != null) {
// { regex.expression = cfg.logging.parsing.regex;
service = serviceName; })
node = homelabCfg.hostname; ++ (optional (cfg.logging.parsing.extractFields != []) {
environment = homelabCfg.environment; labels = listToAttrs (map (field: nameValuePair field null) cfg.logging.parsing.extractFields);
}; });
pipelineStages = enabled = true;
mkIf (cfg.logging.parsing.regex != null) [ })
{ ++ cfg.logging.extraSources
regex.expression = cfg.logging.parsing.regex; );
}
]
++ [
{
labels = listToAttrs (map (field: nameValuePair field null) cfg.logging.parsing.extractFields);
}
];
enabled = true;
}
]
++ cfg.logging.extraSources;
}; };
} }

View file

@ -6,47 +6,69 @@ serviceName: {
with lib; let with lib; let
cfg = config.homelab.services.${serviceName}; cfg = config.homelab.services.${serviceName};
homelabCfg = config.homelab; homelabCfg = config.homelab;
hasMetricsConfig =
cfg.monitoring.metrics.path
!= null
|| cfg.monitoring.metrics.extraEndpoints != [];
hasHealthCheckConfig =
cfg.monitoring.healthCheck.path
!= null
|| cfg.monitoring.healthCheck.conditions != []
|| cfg.monitoring.healthCheck.extraChecks != [];
in { in {
# Define the service-specific monitoring options # Define the service-specific monitoring options
options.homelab.services.${serviceName}.monitoring = { options.homelab.services.${serviceName}.monitoring = {
enable = mkEnableOption "monitoring for ${serviceName}"; enable = mkOption {
type = types.bool;
description = "Enable monitoring for ${serviceName}";
default = hasMetricsConfig || hasHealthCheckConfig;
};
metrics = { metrics = {
enable = mkOption { enable = mkOption {
type = types.bool; type = types.bool;
default = true; default = hasMetricsConfig;
}; };
path = mkOption { path = mkOption {
type = types.str; type = types.nullOr types.str;
default = "/metrics"; default = null;
description = "Metrics endpoint path. Setting this enables metrics collection.";
}; };
extraEndpoints = mkOption { extraEndpoints = mkOption {
type = types.listOf types.attrs; type = types.listOf types.attrs;
default = []; default = [];
description = "Additional metrics endpoints. Adding endpoints enables metrics collection.";
}; };
}; };
healthCheck = { healthCheck = {
enable = mkOption { enable = mkOption {
type = types.bool; type = types.bool;
default = true; default = hasHealthCheckConfig;
}; };
path = mkOption { path = mkOption {
type = types.str; type = types.nullOr types.str;
default = "/health"; default = null;
description = "Health check endpoint path. Setting this enables health checks.";
example = "/health";
}; };
conditions = mkOption { conditions = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = ["[STATUS] == 200"]; default = ["[STATUS] == 200"];
description = "Health check conditions. Setting conditions enables health checks.";
example = ["[STATUS] == 200"];
}; };
extraChecks = mkOption { extraChecks = mkOption {
type = types.listOf types.attrs; type = types.listOf types.attrs;
default = []; default = [];
description = "Additional health checks. Adding checks enables health monitoring.";
}; };
}; };
@ -57,52 +79,50 @@ in {
}; };
# Generate the homelab config automatically when service is enabled # Generate the homelab config automatically when service is enabled
config = mkIf (cfg.enable && cfg.monitoring.enable) { config = mkIf cfg.enable {
homelab.monitoring = { homelab.monitoring = mkIf cfg.monitoring.enable {
metrics = metrics = mkIf hasMetricsConfig (
[ (optional (cfg.monitoring.metrics.path != null) {
{ name = "${serviceName}-main";
name = "${serviceName}-main"; host = homelabCfg.hostname;
host = homelabCfg.hostname; port = cfg.port;
port = cfg.port; path = cfg.monitoring.metrics.path;
path = cfg.monitoring.metrics.path; jobName = serviceName;
jobName = serviceName; scrapeInterval = "30s";
scrapeInterval = "30s"; labels =
labels = cfg.monitoring.extraLabels
cfg.monitoring.extraLabels // {
// { service = serviceName;
service = serviceName; node = homelabCfg.hostname;
node = homelabCfg.hostname; environment = homelabCfg.environment;
environment = homelabCfg.environment; };
}; })
} ++ cfg.monitoring.metrics.extraEndpoints
] );
++ cfg.monitoring.metrics.extraEndpoints;
healthChecks = healthChecks = mkIf hasHealthCheckConfig (
[ (optional (cfg.monitoring.healthCheck.path != null) {
{ name = "${serviceName}-health";
name = "${serviceName}-health"; host = homelabCfg.hostname;
host = homelabCfg.hostname; port = cfg.port;
port = cfg.port; path = cfg.monitoring.healthCheck.path;
path = cfg.monitoring.healthCheck.path; protocol = "http";
protocol = "http"; method = "GET";
method = "GET"; interval = "30s";
interval = "30s"; timeout = "10s";
timeout = "10s"; conditions = cfg.monitoring.healthCheck.conditions;
conditions = cfg.monitoring.healthCheck.conditions; group = "services";
group = "services"; labels =
labels = cfg.monitoring.extraLabels
cfg.monitoring.extraLabels // {
// { service = serviceName;
service = serviceName; node = homelabCfg.hostname;
node = homelabCfg.hostname; environment = homelabCfg.environment;
environment = homelabCfg.environment; };
}; enabled = true;
enabled = true; })
} ++ cfg.monitoring.healthCheck.extraChecks
] );
++ cfg.monitoring.healthCheck.extraChecks;
}; };
}; };
} }

View file

@ -8,7 +8,11 @@ with lib; let
homelabCfg = config.homelab; homelabCfg = config.homelab;
in { in {
options.homelab.services.${serviceName}.proxy = { options.homelab.services.${serviceName}.proxy = {
enable = mkEnableOption "reverse proxy for ${serviceName}"; enable = mkOption {
type = types.bool;
description = "Enable reverse proxy for ${serviceName}";
default = true;
};
subdomain = mkOption { subdomain = mkOption {
type = types.str; type = types.str;
@ -39,8 +43,8 @@ in {
}; };
}; };
config = mkIf (cfg.enable && cfg.proxy.enable) { config = mkIf cfg.enable {
homelab.reverseProxy.entries = homelab.reverseProxy.entries = mkIf cfg.proxy.enable (
[ [
{ {
subdomain = cfg.proxy.subdomain; subdomain = cfg.proxy.subdomain;
@ -59,6 +63,7 @@ in {
enableAuth = sub.enableAuth; enableAuth = sub.enableAuth;
enableSSL = true; enableSSL = true;
}) })
cfg.proxy.additionalSubdomains; cfg.proxy.additionalSubdomains
);
}; };
} }

View file

@ -219,8 +219,7 @@ in {
homelab.services.${serviceName}.monitoring.enable = mkDefault true; homelab.services.${serviceName}.monitoring.enable = mkDefault true;
} }
# Smart defaults for Gatus {
(mkIf cfg.monitoring.enable {
homelab.services.${serviceName}.monitoring = mkDefault { homelab.services.${serviceName}.monitoring = mkDefault {
metrics = { metrics = {
path = "/metrics"; path = "/metrics";
@ -240,9 +239,9 @@ in {
tier = "monitoring"; tier = "monitoring";
}; };
}; };
}) }
(mkIf cfg.logging.enable { {
homelab.services.${serviceName}.logging = mkDefault { homelab.services.${serviceName}.logging = mkDefault {
files = ["/var/log/gatus/gatus.log"]; files = ["/var/log/gatus/gatus.log"];
parsing = { parsing = {
@ -255,13 +254,13 @@ in {
application = "gatus"; application = "gatus";
}; };
}; };
}) }
(mkIf cfg.proxy.enable { {
homelab.services.${serviceName}.proxy = mkDefault { homelab.services.${serviceName}.proxy = mkDefault {
subdomain = "status"; subdomain = "status";
enableAuth = false; # Status page should be public enableAuth = false; # Status page should be public
}; };
}) }
]); ]);
} }

View file

@ -45,7 +45,7 @@ in {
} }
# Smart defaults for Grafana # Smart defaults for Grafana
(mkIf cfg.logging.enable { {
# Grafana-specific log setup # Grafana-specific log setup
homelab.services.${serviceName}.logging = mkDefault { homelab.services.${serviceName}.logging = mkDefault {
files = ["/var/log/grafana/grafana.log"]; files = ["/var/log/grafana/grafana.log"];
@ -59,9 +59,8 @@ in {
component = "dashboard"; component = "dashboard";
}; };
}; };
}) }
{
(mkIf cfg.monitoring.enable {
homelab.services.${serviceName}.monitoring = mkDefault { homelab.services.${serviceName}.monitoring = mkDefault {
metrics.path = "/metrics"; metrics.path = "/metrics";
healthCheck = { healthCheck = {
@ -73,14 +72,13 @@ in {
tier = "monitoring"; tier = "monitoring";
}; };
}; };
}) }
{
(mkIf cfg.proxy.enable {
# Grafana needs auth by default (admin interface) # Grafana needs auth by default (admin interface)
homelab.services.${serviceName}.proxy = mkDefault { homelab.services.${serviceName}.proxy = mkDefault {
subdomain = "grafana"; subdomain = "grafana";
# enableAuth = true; # enableAuth = true;
}; };
}) }
]); ]);
} }

View file

@ -168,7 +168,6 @@ in {
# Service configuration with smart defaults # Service configuration with smart defaults
config = mkIf cfg.enable (mkMerge [ config = mkIf cfg.enable (mkMerge [
# Core Prometheus service
{ {
services.prometheus = { services.prometheus = {
enable = true; enable = true;
@ -203,39 +202,21 @@ in {
}; };
networking.firewall.allowedTCPPorts = [cfg.port]; networking.firewall.allowedTCPPorts = [cfg.port];
homelab.services.${serviceName}.monitoring.enable = mkDefault true;
} }
{
homelab.services.${serviceName}.monitoring = {
metrics.path = "/metrics";
healthCheck.path = "/-/healthy"; # ✅ Enables health checks
healthCheck.conditions = ["[STATUS] == 200" "[RESPONSE_TIME] < 1000"];
# Smart defaults for Prometheus
(mkIf cfg.monitoring.enable {
homelab.services.${serviceName}.monitoring = mkDefault {
metrics = {
path = "/metrics";
extraEndpoints = [];
};
healthCheck = {
path = "/-/healthy";
conditions = ["[STATUS] == 200" "[RESPONSE_TIME] < 1000"];
extraChecks = [
{
name = "prometheus-ready";
port = cfg.port;
path = "/-/ready";
conditions = ["[STATUS] == 200"];
group = "monitoring";
}
];
};
extraLabels = { extraLabels = {
component = "monitoring-server"; component = "monitoring-server";
tier = "monitoring"; tier = "monitoring";
}; };
}; };
}) }
{
(mkIf cfg.logging.enable { homelab.services.${serviceName}.logging = {
homelab.services.${serviceName}.logging = mkDefault {
files = ["/var/log/prometheus/prometheus.log"]; files = ["/var/log/prometheus/prometheus.log"];
parsing = { parsing = {
# Prometheus log format: ts=2024-01-01T12:00:00.000Z caller=main.go:123 level=info msg="message" # Prometheus log format: ts=2024-01-01T12:00:00.000Z caller=main.go:123 level=info msg="message"
@ -247,13 +228,11 @@ in {
application = "prometheus"; application = "prometheus";
}; };
}; };
}) }
{
(mkIf cfg.proxy.enable { homelab.services.${serviceName}.proxy = {
homelab.services.${serviceName}.proxy = mkDefault { enableAuth = true;
subdomain = "prometheus";
enableAuth = true; # Admin interface needs protection
}; };
}) }
]); ]);
} }

View file

@ -30,7 +30,7 @@ writeShellScriptBin "homelab-docs-readme" ''
## 🚀 Quick Actions ## 🚀 Quick Actions
### View Current Status ### View Current Status
\`\`\`bash ```bash
# Service status across fleet (if homelab CLI is available) # Service status across fleet (if homelab CLI is available)
homelab services --global homelab services --global
@ -39,16 +39,16 @@ writeShellScriptBin "homelab-docs-readme" ''
# Overall status # Overall status
homelab status homelab status
\`\`\` ```
### Update Documentation ### Update Documentation
\`\`\`bash ```bash
# Regenerate all documentation # Regenerate all documentation
homelab-generate-docs ./docs homelab-generate-docs ./docs
# Generate in different directory # Generate in different directory
homelab-generate-docs /path/to/output homelab-generate-docs /path/to/output
\`\`\` ```
## 📋 Quick Stats ## 📋 Quick Stats

View file

@ -0,0 +1,138 @@
# service-evaluator.nix - Pure Nix evaluation logic for service documentation
{
nodes,
pkgs,
lib,
...
}: let
# Helper to recursively extract option information
extractOptions = path: options: let
pathStr = lib.concatStringsSep "." path;
in
lib.flatten (lib.mapAttrsToList (
name: value: let
currentPath = path ++ [name];
currentPathStr = lib.concatStringsSep "." currentPath;
in
if (value._type or null) == "option"
then
# This is an actual NixOS option
[
{
name = currentPathStr;
type = value.type.description or (builtins.typeOf (value.default or null));
default = value.default or null;
defaultText =
if value ? defaultText
then value.defaultText.text or null
else null;
description = value.description or "No description available";
example = value.example or null;
readOnly = value.readOnly or false;
}
]
else if lib.isAttrs value && !(lib.hasAttr "_type" value)
then
# This is a nested attribute set, recurse
extractOptions currentPath value
else
# Skip other types
[]
)
options);
# Get service options from the first node (they should be the same across nodes)
firstNode = lib.head (lib.attrValues nodes);
homelabServices = firstNode.options.homelab.services or {};
# Extract all services and their options
serviceDefinitions =
lib.mapAttrs (serviceName: serviceOptions: {
inherit serviceName;
options = extractOptions [] serviceOptions;
features = let
optionNames = map (opt: opt.name) (extractOptions [] serviceOptions);
in {
hasMonitoring = lib.any (name: lib.hasPrefix "monitoring" name) optionNames;
hasLogging = lib.any (name: lib.hasPrefix "logging" name) optionNames;
hasProxy = lib.any (name: lib.hasPrefix "proxy" name) optionNames;
};
})
homelabServices;
# Also get all services that exist in actual configurations (for deployment info)
allConfiguredServices = lib.unique (lib.flatten (lib.mapAttrsToList (
nodeName: node:
if (node.config.homelab.enable or false)
then lib.attrNames (node.config.homelab.services or {})
else []
)
nodes));
# For each service, get deployment info
serviceDeployments = lib.listToAttrs (map (serviceName: {
name = serviceName;
value =
lib.foldl (
acc: nodeName: let
node = nodes.${nodeName};
serviceConfig = node.config.homelab.services.${serviceName} or null;
isEnabled =
if serviceConfig != null
then serviceConfig.enable or false
else false;
in
if serviceConfig != null
then
acc
// {
totalNodes = acc.totalNodes + 1;
enabledNodes =
acc.enabledNodes
+ (
if isEnabled
then 1
else 0
);
nodeNames = acc.nodeNames ++ [nodeName];
enabledNodeNames =
acc.enabledNodeNames
++ (
if isEnabled
then [nodeName]
else []
);
}
else acc
) {
totalNodes = 0;
enabledNodes = 0;
nodeNames = [];
enabledNodeNames = [];
} (lib.attrNames nodes);
})
allConfiguredServices);
# Combine service definitions with deployment info
servicesWithDeployment =
lib.mapAttrs (
serviceName: serviceData:
serviceData
// {
deployment =
serviceDeployments.${
serviceName
} or {
totalNodes = 0;
enabledNodes = 0;
nodeNames = [];
enabledNodeNames = [];
};
}
)
serviceDefinitions;
in {
services = servicesWithDeployment;
totalServices = lib.length (lib.attrNames servicesWithDeployment);
allConfiguredServices = allConfiguredServices;
}

View file

@ -1,4 +1,3 @@
# homelab-docs-services.nix - Service documentation generator CLI
{ {
writeShellScriptBin, writeShellScriptBin,
jq, jq,
@ -10,261 +9,376 @@ writeShellScriptBin "homelab-docs-services" ''
cat << 'EOF' cat << 'EOF'
# Service Catalog # Service Catalog
> Available services and their configuration options > Complete service documentation with core options, feature integrations, and smart defaults
> >
> Generated on: $(date) > Generated on: $(date)
This document catalogs all available homelab services, their configuration options, and integration capabilities. This document provides comprehensive documentation for homelab services, organized by:
- **Core Service Options**: The main service configuration
- **Feature Integrations**: Available monitoring, logging, and proxy features
- **Service Defaults**: What this service configures by default for each feature
EOF EOF
# Get all services and their configurations # Extract comprehensive service information
services_catalog=$(colmena eval -E '{ nodes, pkgs, lib, ... }: echo "Extracting service information..." >&2
services_catalog=$(colmena eval -E '
{ nodes, pkgs, lib, ... }:
let let
# Collect all services from all nodes to build a complete catalog # Helper to extract option information
allServiceConfigs = lib.flatten (lib.mapAttrsToList (nodeName: node: extractOptions = path: options:
if (node.config.homelab.enable or false) then lib.flatten (lib.mapAttrsToList (name: value:
lib.mapAttrsToList (serviceName: service: { let
inherit serviceName; currentPath = path ++ [name];
config = { pathStr = lib.concatStringsSep "." currentPath;
# Core service options in
enable = service.enable or false; if (value._type or null) == "option" then
port = service.port or null; [{
description = service.description or serviceName; name = pathStr;
tags = service.tags or []; type = value.type.description or "unknown";
default = value.default or null;
defaultText = if value ? defaultText then value.defaultText.text or null else null;
description = value.description or "No description";
readOnly = value.readOnly or false;
}]
else if lib.isAttrs value && !(lib.hasAttr "_type" value) then
extractOptions currentPath value
else []
) options);
# Integration options # Get first node for option definitions
monitoring = { firstNode = lib.head (lib.attrValues nodes);
enabled = service.monitoring.enable or false; homelabServices = firstNode.options.homelab.services or {};
metricsPath = service.monitoring.metrics.path or "/metrics";
healthPath = service.monitoring.healthCheck.path or "/health";
extraLabels = service.monitoring.extraLabels or {};
};
logging = { # Process each service
enabled = service.logging.enable or false; serviceInfo = lib.mapAttrs (serviceName: serviceOptions:
files = service.logging.files or [];
extraLabels = service.logging.extraLabels or {};
};
proxy = {
enabled = service.proxy.enable or false;
subdomain = service.proxy.subdomain or serviceName;
enableAuth = service.proxy.enableAuth or false;
};
# Service-specific options (everything else)
serviceSpecific = removeAttrs service [
"enable" "port" "description" "tags"
"monitoring" "logging" "proxy"
];
};
deployedOn = nodeName;
}) (node.config.homelab.services or {})
else []
) nodes);
# Group by service name and merge configurations
serviceGroups = lib.groupBy (svc: svc.serviceName) allServiceConfigs;
# Get unique services with merged configuration examples
uniqueServices = lib.mapAttrs (serviceName: instances:
let let
# Take the first enabled instance as the canonical example allOptions = extractOptions [] serviceOptions;
enabledInstances = lib.filter (inst: inst.config.enable) instances;
canonicalConfig = if enabledInstances != [] then (lib.head enabledInstances).config else (lib.head instances).config; # Separate core options from feature options
coreOptions = lib.filter (opt:
!(lib.hasPrefix "monitoring." opt.name) &&
!(lib.hasPrefix "logging." opt.name) &&
!(lib.hasPrefix "proxy." opt.name)
) allOptions;
monitoringOptions = lib.filter (opt: lib.hasPrefix "monitoring." opt.name) allOptions;
loggingOptions = lib.filter (opt: lib.hasPrefix "logging." opt.name) allOptions;
proxyOptions = lib.filter (opt: lib.hasPrefix "proxy." opt.name) allOptions;
# Get actual service configuration to see what defaults are set
serviceConfigs = lib.mapAttrs (nodeName: node:
let
serviceConfig = node.config.homelab.services.''${serviceName} or null;
in
if serviceConfig != null then {
exists = true;
enabled = serviceConfig.enable or false;
# Extract the computed configuration values
monitoring = serviceConfig.monitoring or {};
logging = serviceConfig.logging or {};
proxy = serviceConfig.proxy or {};
# Get other core options
coreConfig = removeAttrs serviceConfig ["monitoring" "logging" "proxy"];
} else {
exists = false;
}
) nodes;
# Find a node where this service exists to get default values
nodeWithService = lib.findFirst (nodeName: serviceConfigs.''${nodeName}.exists) null (lib.attrNames nodes);
exampleConfig = if nodeWithService != null then serviceConfigs.''${nodeWithService} else null;
in { in {
inherit serviceName; inherit serviceName;
config = canonicalConfig; coreOptions = coreOptions;
deploymentCount = lib.length (lib.filter (inst: inst.config.enable) instances); features = {
deployedOn = lib.unique (map (inst: inst.deployedOn or "unknown") enabledInstances); monitoring = {
available = monitoringOptions != [];
options = monitoringOptions;
defaults = if exampleConfig != null then exampleConfig.monitoring else {};
};
logging = {
available = loggingOptions != [];
options = loggingOptions;
defaults = if exampleConfig != null then exampleConfig.logging else {};
};
proxy = {
available = proxyOptions != [];
options = proxyOptions;
defaults = if exampleConfig != null then exampleConfig.proxy else {};
};
};
deployment = {
totalNodes = lib.length (lib.filter (cfg: cfg.exists) (lib.attrValues serviceConfigs));
enabledNodes = lib.length (lib.filter (cfg: cfg.exists && cfg.enabled) (lib.attrValues serviceConfigs));
};
} }
) serviceGroups; ) homelabServices;
in { in {
services = uniqueServices; services = serviceInfo;
totalUniqueServices = lib.length (lib.attrNames uniqueServices); totalServices = lib.length (lib.attrNames serviceInfo);
}') }
')
total_services=$(echo "$services_catalog" | ${jq}/bin/jq -r '.totalUniqueServices') total_services=$(echo "$services_catalog" | ${jq}/bin/jq -r '.totalServices')
echo "## Overview" echo "## Overview"
echo echo
echo "**Total Available Services:** $total_services" echo "**Total Available Services:** $total_services"
echo echo
# Create a summary table of services and their default integrations # Service matrix
echo "## Service Integration Matrix" echo "## Service Integration Matrix"
echo echo
echo "| Service | Monitoring | Logging | Proxy | Auth Default |" echo "| Service | Core Options | Monitoring | Logging | Proxy | Deployments |"
echo "|---------|------------|---------|-------|--------------|" echo "|---------|--------------|------------|---------|-------|-------------|"
echo "$services_catalog" | ${jq}/bin/jq -r '.services | to_entries[] | .key' | sort | while read -r service; do echo "$services_catalog" | ${jq}/bin/jq -r '.services | keys[]' | sort | while read -r service; do
service_data=$(echo "$services_catalog" | ${jq}/bin/jq -r ".services[\"$service\"]") service_data=$(echo "$services_catalog" | ${jq}/bin/jq -r ".services[\"$service\"]")
monitoring_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.enabled') core_count=$(echo "$service_data" | ${jq}/bin/jq -r '.coreOptions | length')
logging_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.logging.enabled') has_monitoring=$(echo "$service_data" | ${jq}/bin/jq -r '.features.monitoring.available')
proxy_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enabled') has_logging=$(echo "$service_data" | ${jq}/bin/jq -r '.features.logging.available')
auth_default=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enableAuth') has_proxy=$(echo "$service_data" | ${jq}/bin/jq -r '.features.proxy.available')
enabled_deployments=$(echo "$service_data" | ${jq}/bin/jq -r '.deployment.enabledNodes')
monitoring_icon=$(if [[ "$monitoring_enabled" == "true" ]]; then echo ""; else echo ""; fi) monitoring_icon=$(if [[ "$has_monitoring" == "true" ]]; then echo "📊"; else echo ""; fi)
logging_icon=$(if [[ "$logging_enabled" == "true" ]]; then echo ""; else echo ""; fi) logging_icon=$(if [[ "$has_logging" == "true" ]]; then echo "📝"; else echo ""; fi)
proxy_icon=$(if [[ "$proxy_enabled" == "true" ]]; then echo ""; else echo ""; fi) proxy_icon=$(if [[ "$has_proxy" == "true" ]]; then echo "🔀"; else echo ""; fi)
auth_icon=$(if [[ "$auth_default" == "true" ]]; then echo "🔒"; else echo "🌐"; fi)
echo "| \`$service\` | $monitoring_icon | $logging_icon | $proxy_icon | $auth_icon |" echo "| \`$service\` | $core_count | $monitoring_icon | $logging_icon | $proxy_icon | $enabled_deployments |"
done done
echo echo
echo "**Legend:** = Enabled by default, = Available but disabled, 🔒 = Auth required, 🌐 = Public access" echo "**Legend:** 📊📝🔀 = Feature available, = Feature not available"
echo echo
echo "## Service Reference" echo "## Service Documentation"
echo echo
# Process each service # Process each service
echo "$services_catalog" | ${jq}/bin/jq -r '.services | to_entries[] | .key' | sort | while read -r service; do echo "$services_catalog" | ${jq}/bin/jq -r '.services | keys[]' | sort | while read -r service; do
echo "### $service" echo "### $service"
echo echo
# Get service details
service_data=$(echo "$services_catalog" | ${jq}/bin/jq -r ".services[\"$service\"]") service_data=$(echo "$services_catalog" | ${jq}/bin/jq -r ".services[\"$service\"]")
enabled_deployments=$(echo "$service_data" | ${jq}/bin/jq -r '.deployment.enabledNodes')
total_deployments=$(echo "$service_data" | ${jq}/bin/jq -r '.deployment.totalNodes')
description=$(echo "$service_data" | ${jq}/bin/jq -r '.config.description // "No description available"') if [[ "$total_deployments" -gt 0 ]]; then
port=$(echo "$service_data" | ${jq}/bin/jq -r '.config.port // "N/A"') echo "**Deployment Status:** $enabled_deployments/$total_deployments nodes have this service enabled"
tags=$(echo "$service_data" | ${jq}/bin/jq -r '.config.tags | join(", ")')
deployment_count=$(echo "$service_data" | ${jq}/bin/jq -r '.deploymentCount')
deployed_on=$(echo "$service_data" | ${jq}/bin/jq -r '.deployedOn | join(", ")')
echo "**Description:** $description"
echo
echo "**Default Port:** \`$port\`"
echo
if [[ -n "$tags" && "$tags" != "" ]]; then
echo "**Tags:** $tags"
echo
fi
echo "**Current Deployments:** $deployment_count instance(s) on: $deployed_on"
echo
# Integration Status Overview
monitoring_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.enabled')
logging_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.logging.enabled')
proxy_enabled=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enabled')
echo "#### Default Integration Status"
echo
echo "| Integration | Status | Default Configuration |"
echo "|-------------|--------|----------------------|"
# Monitoring status
if [[ "$monitoring_enabled" == "true" ]]; then
metrics_path=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.metricsPath')
health_path=$(echo "$service_data" | ${jq}/bin/jq -r '.config.monitoring.healthPath')
echo "| 📊 Monitoring | **Enabled** | Metrics: \`$metrics_path\`, Health: \`$health_path\` |"
else else
echo "| 📊 Monitoring | Disabled | Available but requires \`monitoring.enable = true\` |" echo "**Deployment Status:** Available but not configured"
fi fi
# Logging status
if [[ "$logging_enabled" == "true" ]]; then
log_files=$(echo "$service_data" | ${jq}/bin/jq -r '.config.logging.files | length')
if [[ "$log_files" -gt 0 ]]; then
echo "| 📝 Logging | **Enabled** | Collecting $log_files log file(s) |"
else
echo "| 📝 Logging | **Enabled** | Auto-configured log collection |"
fi
else
echo "| 📝 Logging | Disabled | Available but requires \`logging.enable = true\` |"
fi
# Proxy status
if [[ "$proxy_enabled" == "true" ]]; then
subdomain=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.subdomain')
enable_auth=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enableAuth')
auth_status=$(if [[ "$enable_auth" == "true" ]]; then echo "🔒 Auth required"; else echo "🌐 Public access"; fi)
echo "| 🔀 Proxy | **Enabled** | Subdomain: \`$subdomain\`, $auth_status |"
else
echo "| 🔀 Proxy | Disabled | Available but requires \`proxy.enable = true\` |"
fi
echo echo
# Core Configuration # Core Service Configuration
echo "#### Core Configuration" echo "#### Core Service Options"
echo echo
echo "\`\`\`nix" echo "The main configuration options for $service:"
echo
echo '```nix'
echo "homelab.services.$service = {" echo "homelab.services.$service = {"
echo " enable = true;"
if [[ "$port" != "N/A" ]]; then echo "$service_data" | ${jq}/bin/jq -r '.coreOptions[] | @base64' | while IFS= read -r option_b64; do
echo " port = $port;" option=$(echo "$option_b64" | base64 -d)
fi
echo " description = \"$description\";" name=$(echo "$option" | ${jq}/bin/jq -r '.name')
if [[ -n "$tags" && "$tags" != "" ]]; then type=$(echo "$option" | ${jq}/bin/jq -r '.type')
echo " tags = [ $(echo "$tags" | sed 's/, /" "/g' | sed 's/^/"/; s/$/"/') ];" default_val=$(echo "$option" | ${jq}/bin/jq -r '.default')
fi description=$(echo "$option" | ${jq}/bin/jq -r '.description')
echo read_only=$(echo "$option" | ${jq}/bin/jq -r '.readOnly')
echo " # Default integrations (adjust as needed)"
if [[ "$monitoring_enabled" == "true" ]]; then if [[ "$read_only" == "true" ]]; then
echo " monitoring.enable = true; # Enabled by default" continue
else fi
echo " # monitoring.enable = true; # Disabled by default"
fi clean_description=$(echo "$description" | sed 's/"/\\"/g' | tr -d $'\n\r')
if [[ "$logging_enabled" == "true" ]]; then
echo " logging.enable = true; # Enabled by default" if [[ "$default_val" == "null" ]]; then
else echo " # $name = <$type>; # $clean_description"
echo " # logging.enable = true; # Disabled by default" else
fi echo " $name = $default_val; # $clean_description"
if [[ "$proxy_enabled" == "true" ]]; then fi
echo " proxy.enable = true; # Enabled by default" done
else
echo " # proxy.enable = true; # Disabled by default"
fi
echo "};" echo "};"
echo "\`\`\`" echo '```'
echo echo
# Service-specific options # Feature Integrations
service_specific=$(echo "$service_data" | ${jq}/bin/jq -r '.config.serviceSpecific') has_monitoring=$(echo "$service_data" | ${jq}/bin/jq -r '.features.monitoring.available')
if [[ "$service_specific" != "{}" && "$service_specific" != "null" ]]; then has_logging=$(echo "$service_data" | ${jq}/bin/jq -r '.features.logging.available')
echo "#### Service-Specific Options" has_proxy=$(echo "$service_data" | ${jq}/bin/jq -r '.features.proxy.available')
echo
echo "Available configuration options for $service:" if [[ "$has_monitoring" == "true" || "$has_logging" == "true" || "$has_proxy" == "true" ]]; then
echo echo "#### Feature Integrations"
echo "\`\`\`nix"
echo "homelab.services.$service = {"
echo " # ... core options above ..."
echo
echo " # Service-specific configuration"
echo "$service_specific" | ${jq}/bin/jq -r 'to_entries[] | " \(.key) = \(.value | tostring);"'
echo "};"
echo "\`\`\`"
echo echo
# Monitoring Feature
if [[ "$has_monitoring" == "true" ]]; then
echo "##### 📊 Monitoring Integration"
echo
echo "Available monitoring options:"
echo
echo '```nix'
echo "homelab.services.$service = {"
echo " # ... core options above ..."
echo
echo "$service_data" | ${jq}/bin/jq -r '.features.monitoring.options[] | @base64' | while IFS= read -r option_b64; do
option=$(echo "$option_b64" | base64 -d)
name=$(echo "$option" | ${jq}/bin/jq -r '.name')
type=$(echo "$option" | ${jq}/bin/jq -r '.type')
default_val=$(echo "$option" | ${jq}/bin/jq -r '.default')
description=$(echo "$option" | ${jq}/bin/jq -r '.description')
read_only=$(echo "$option" | ${jq}/bin/jq -r '.readOnly')
if [[ "$read_only" == "true" ]]; then
continue
fi
clean_description=$(echo "$description" | sed 's/"/\\"/g' | tr -d $'\n\r')
if [[ "$default_val" == "null" ]]; then
echo " # $name = <$type>; # $clean_description"
else
echo " $name = $default_val; # $clean_description"
fi
done
echo "};"
echo '```'
# Show service-specific monitoring defaults
monitoring_defaults=$(echo "$service_data" | ${jq}/bin/jq -r '.features.monitoring.defaults')
if [[ "$monitoring_defaults" != "{}" && "$monitoring_defaults" != "null" ]]; then
echo
echo "**$service sets these monitoring defaults:**"
echo '```nix'
echo "$monitoring_defaults" | ${jq}/bin/jq -r 'to_entries[] | " \(.key) = \(.value);"'
echo '```'
fi
echo
fi
# Logging Feature
if [[ "$has_logging" == "true" ]]; then
echo "##### 📝 Logging Integration"
echo
echo "Available logging options:"
echo
echo '```nix'
echo "homelab.services.$service = {"
echo " # ... core options above ..."
echo
echo "$service_data" | ${jq}/bin/jq -r '.features.logging.options[] | @base64' | while IFS= read -r option_b64; do
option=$(echo "$option_b64" | base64 -d)
name=$(echo "$option" | ${jq}/bin/jq -r '.name')
type=$(echo "$option" | ${jq}/bin/jq -r '.type')
default_val=$(echo "$option" | ${jq}/bin/jq -r '.default')
description=$(echo "$option" | ${jq}/bin/jq -r '.description')
read_only=$(echo "$option" | ${jq}/bin/jq -r '.readOnly')
if [[ "$read_only" == "true" ]]; then
continue
fi
clean_description=$(echo "$description" | sed 's/"/\\"/g' | tr -d $'\n\r')
if [[ "$default_val" == "null" ]]; then
echo " # $name = <$type>; # $clean_description"
else
echo " $name = $default_val; # $clean_description"
fi
done
echo "};"
echo '```'
# Show service-specific logging defaults
logging_defaults=$(echo "$service_data" | ${jq}/bin/jq -r '.features.logging.defaults')
if [[ "$logging_defaults" != "{}" && "$logging_defaults" != "null" ]]; then
echo
echo "**$service sets these logging defaults:**"
echo '```nix'
echo "$logging_defaults" | ${jq}/bin/jq -r 'to_entries[] | " \(.key) = \(.value);"'
echo '```'
fi
echo
fi
# Proxy Feature
if [[ "$has_proxy" == "true" ]]; then
echo "##### 🔀 Proxy Integration"
echo
echo "Available proxy options:"
echo
echo '```nix'
echo "homelab.services.$service = {"
echo " # ... core options above ..."
echo
echo "$service_data" | ${jq}/bin/jq -r '.features.proxy.options[] | @base64' | while IFS= read -r option_b64; do
option=$(echo "$option_b64" | base64 -d)
name=$(echo "$option" | ${jq}/bin/jq -r '.name')
type=$(echo "$option" | ${jq}/bin/jq -r '.type')
default_val=$(echo "$option" | ${jq}/bin/jq -r '.default')
description=$(echo "$option" | ${jq}/bin/jq -r '.description')
read_only=$(echo "$option" | ${jq}/bin/jq -r '.readOnly')
if [[ "$read_only" == "true" ]]; then
continue
fi
clean_description=$(echo "$description" | sed 's/"/\\"/g' | tr -d $'\n\r')
if [[ "$default_val" == "null" ]]; then
echo " # $name = <$type>; # $clean_description"
else
echo " $name = $default_val; # $clean_description"
fi
done
echo "};"
echo '```'
# Show service-specific proxy defaults
proxy_defaults=$(echo "$service_data" | ${jq}/bin/jq -r '.features.proxy.defaults')
if [[ "$proxy_defaults" != "{}" && "$proxy_defaults" != "null" ]]; then
echo
echo "**$service sets these proxy defaults:**"
echo '```nix'
echo "$proxy_defaults" | ${jq}/bin/jq -r 'to_entries[] | " \(.key) = \(.value);"'
echo '```'
fi
echo
fi
fi fi
echo "---" echo "---"
echo echo
done done
echo "## Integration Summary" echo "## Feature Reference"
echo echo
echo "### Available Integration Types" echo "### Integration Features"
echo echo
echo "| Integration | Purpose | Default Behavior | Configuration |" echo "Homelab services can integrate with three main features:"
echo "|-------------|---------|------------------|---------------|"
echo "| **📊 Monitoring** | Prometheus metrics + health checks | Service-dependent | \`monitoring.enable = true\` |"
echo "| **📝 Logging** | Centralized log collection | Service-dependent | \`logging.enable = true\` |"
echo "| **🔀 Proxy** | Reverse proxy with SSL + auth | Service-dependent | \`proxy.enable = true\` |"
echo echo
echo "### Integration Benefits" echo "- **📊 Monitoring**: Prometheus metrics and health checks"
echo "- **📝 Logging**: Centralized log collection with Promtail/Loki"
echo "- **🔀 Proxy**: Reverse proxy with SSL and authentication"
echo echo
echo "- **🔄 Automatic Discovery:** Enabled integrations are automatically discovered by fleet-wide services" echo "Each service can import these features and set service-specific defaults."
echo "- **📊 Unified Monitoring:** All metrics and health checks appear in Prometheus/Grafana"
echo "- **📝 Centralized Logging:** All logs are collected and indexed in Loki"
echo "- **🌐 Consistent Access:** All services get consistent subdomain access with SSL"
echo "- **🎯 Smart Defaults:** Each service comes with sensible default configurations"
echo echo
echo "---" echo "---"
echo echo
echo "*This service catalog is generated from actual service configurations across your homelab fleet.*" echo "*This documentation is generated from actual NixOS module evaluations.*"
'' ''