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
### View Current Status
\`\`\`bash
```bash
# Service status across fleet (if homelab CLI is available)
homelab services --global
@ -29,22 +29,22 @@ homelab backups --global
# Overall status
homelab status
\`\`\`
```
### Update Documentation
\`\`\`bash
```bash
# Regenerate all documentation
homelab-generate-docs ./docs
# Generate in different directory
homelab-generate-docs /path/to/output
\`\`\`
```
## 📋 Quick Stats
- **Total 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

View file

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

View file

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

View file

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

View file

@ -1,10 +1,13 @@
# Service Catalog
> Available services and their configuration options
> Complete service documentation with core options, feature integrations, and smart defaults
>
> 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
@ -12,142 +15,252 @@ This document catalogs all available homelab services, their configuration optio
## Service Integration Matrix
| Service | Monitoring | Logging | Proxy | Auth Default |
|---------|------------|---------|-------|--------------|
| `gatus` | ❌ | ❌ | ❌ | 🌐 |
| `grafana` | ❌ | ❌ | ❌ | 🌐 |
| `minio` | ❌ | ❌ | ❌ | 🌐 |
| `prometheus` | ❌ | ❌ | ❌ | 🌐 |
| Service | Core Options | Monitoring | Logging | Proxy | Deployments |
|---------|--------------|------------|---------|-------|-------------|
| `gatus` | 11 | 📊 | 📝 | 🔀 | 1 |
| `grafana` | 3 | 📊 | 📝 | 🔀 | 1 |
| `minio` | 4 | ❌ | ❌ | ❌ | 1 |
| `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
**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:
#### 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
The main configuration options for gatus:
```nix
homelab.services.gatus = {
enable = true;
port = 8080;
description = "Gatus Status Page";
# Default integrations (adjust as needed)
# monitoring.enable = true; # ❌ Disabled by default
# logging.enable = true; # ❌ Disabled by default
# proxy.enable = true; # ❌ Disabled by default
alerting = {}; # Gatus alerting configuration
description = Gatus Status Page; # No description
enable = false; # Whether to enable Gatus Status Page.
extraConfig = {}; # Additional Gatus configuration options
port = 8080; # No description
storage = {
"type": "memory"
}; # 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
homelab.services.gatus = {
# ... core options above ...
# Service-specific configuration
alerting = {};
extraConfig = {};
storage = {"type":"memory"};
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"};
web = {"address":"0.0.0.0"};
monitoring.enable = true; # Enable monitoring for gatus
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.
};
```
**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
**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:
#### 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
The main configuration options for grafana:
```nix
homelab.services.grafana = {
enable = true;
port = 3000;
description = "Grafana Metrics Dashboard";
# Default integrations (adjust as needed)
# monitoring.enable = true; # ❌ Disabled by default
# logging.enable = true; # ❌ Disabled by default
# proxy.enable = true; # ❌ Disabled by default
description = Grafana Metrics Dashboard; # No description
enable = false; # Whether to enable Grafana Dashboard.
port = 3000; # No description
};
```
#### 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
**Description:** minio
**Deployment Status:** 1/2 nodes have this service enabled
**Default Port:** `9000`
#### Core Service Options
**Current Deployments:** 1 instance(s) on: photos
#### 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
The main configuration options for minio:
```nix
homelab.services.minio = {
enable = true;
port = 9000;
description = "minio";
# 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;
enable = false; # Whether to enable Minio Object Storage.
openFirewall = true; # Whether to open the ports specified in `port` and `webPort` in the firewall.
port = 9000; # Port of the server.
webPort = 9001; # Port of the web UI (console).
};
```
@ -155,75 +268,132 @@ homelab.services.minio = {
### 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:
#### 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
The main configuration options for prometheus:
```nix
homelab.services.prometheus = {
enable = true;
port = 9090;
description = "Prometheus Monitoring Server";
# Default integrations (adjust as needed)
# monitoring.enable = true; # ❌ Disabled by default
# logging.enable = true; # ❌ Disabled by default
# proxy.enable = true; # ❌ Disabled by default
alertmanager.enable = true; # Enable integration with Alertmanager
alertmanager.url = alertmanager.lab:9093; # Alertmanager URL
description = Prometheus Monitoring Server; # No description
enable = false; # Whether to enable Prometheus Monitoring Server.
extraAlertingRules = []; # Additional alerting rules
extraFlags = []; # Extra command line flags
extraScrapeConfigs = []; # Additional scrape configurations
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
homelab.services.prometheus = {
# ... core options above ...
# Service-specific configuration
alertmanager = {"enable":true,"url":"alertmanager.lab:9093"};
extraAlertingRules = [];
extraFlags = [];
extraScrapeConfigs = [];
globalConfig = {"evaluation_interval":"15s","scrape_interval":"15s"};
retention = 15d;
ruleFiles = [];
systemdServices = ["prometheus.service","prometheus"];
monitoring.enable = true; # Enable monitoring for prometheus
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.
};
```
**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 |
|-------------|---------|------------------|---------------|
| **📊 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` |
Homelab services can integrate with three main features:
### 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
- **📊 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
Each service can import these features and set service-specific defaults.
---
*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.prometheus.enable = true;
# services.grafana.enable = true;
# services.gatus.enable = true;
services.prometheus.enable = true;
services.grafana.enable = true;
services.gatus.enable = true;
};
system.stateVersion = "25.05";

View file

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

View file

@ -6,47 +6,69 @@ serviceName: {
with lib; let
cfg = config.homelab.services.${serviceName};
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 {
# Define the service-specific monitoring options
options.homelab.services.${serviceName}.monitoring = {
enable = mkEnableOption "monitoring for ${serviceName}";
enable = mkOption {
type = types.bool;
description = "Enable monitoring for ${serviceName}";
default = hasMetricsConfig || hasHealthCheckConfig;
};
metrics = {
enable = mkOption {
type = types.bool;
default = true;
default = hasMetricsConfig;
};
path = mkOption {
type = types.str;
default = "/metrics";
type = types.nullOr types.str;
default = null;
description = "Metrics endpoint path. Setting this enables metrics collection.";
};
extraEndpoints = mkOption {
type = types.listOf types.attrs;
default = [];
description = "Additional metrics endpoints. Adding endpoints enables metrics collection.";
};
};
healthCheck = {
enable = mkOption {
type = types.bool;
default = true;
default = hasHealthCheckConfig;
};
path = mkOption {
type = types.str;
default = "/health";
type = types.nullOr types.str;
default = null;
description = "Health check endpoint path. Setting this enables health checks.";
example = "/health";
};
conditions = mkOption {
type = types.listOf types.str;
default = ["[STATUS] == 200"];
description = "Health check conditions. Setting conditions enables health checks.";
example = ["[STATUS] == 200"];
};
extraChecks = mkOption {
type = types.listOf types.attrs;
default = [];
description = "Additional health checks. Adding checks enables health monitoring.";
};
};
@ -57,11 +79,10 @@ in {
};
# Generate the homelab config automatically when service is enabled
config = mkIf (cfg.enable && cfg.monitoring.enable) {
homelab.monitoring = {
metrics =
[
{
config = mkIf cfg.enable {
homelab.monitoring = mkIf cfg.monitoring.enable {
metrics = mkIf hasMetricsConfig (
(optional (cfg.monitoring.metrics.path != null) {
name = "${serviceName}-main";
host = homelabCfg.hostname;
port = cfg.port;
@ -75,13 +96,12 @@ in {
node = homelabCfg.hostname;
environment = homelabCfg.environment;
};
}
]
++ cfg.monitoring.metrics.extraEndpoints;
})
++ cfg.monitoring.metrics.extraEndpoints
);
healthChecks =
[
{
healthChecks = mkIf hasHealthCheckConfig (
(optional (cfg.monitoring.healthCheck.path != null) {
name = "${serviceName}-health";
host = homelabCfg.hostname;
port = cfg.port;
@ -100,9 +120,9 @@ in {
environment = homelabCfg.environment;
};
enabled = true;
}
]
++ cfg.monitoring.healthCheck.extraChecks;
})
++ cfg.monitoring.healthCheck.extraChecks
);
};
};
}

View file

@ -8,7 +8,11 @@ with lib; let
homelabCfg = config.homelab;
in {
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 {
type = types.str;
@ -39,8 +43,8 @@ in {
};
};
config = mkIf (cfg.enable && cfg.proxy.enable) {
homelab.reverseProxy.entries =
config = mkIf cfg.enable {
homelab.reverseProxy.entries = mkIf cfg.proxy.enable (
[
{
subdomain = cfg.proxy.subdomain;
@ -59,6 +63,7 @@ in {
enableAuth = sub.enableAuth;
enableSSL = true;
})
cfg.proxy.additionalSubdomains;
cfg.proxy.additionalSubdomains
);
};
}

View file

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

View file

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

View file

@ -168,7 +168,6 @@ in {
# Service configuration with smart defaults
config = mkIf cfg.enable (mkMerge [
# Core Prometheus service
{
services.prometheus = {
enable = true;
@ -203,39 +202,21 @@ in {
};
networking.firewall.allowedTCPPorts = [cfg.port];
homelab.services.${serviceName}.monitoring.enable = mkDefault true;
}
# 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";
}
];
};
homelab.services.${serviceName}.monitoring = {
metrics.path = "/metrics";
healthCheck.path = "/-/healthy"; # ✅ Enables health checks
healthCheck.conditions = ["[STATUS] == 200" "[RESPONSE_TIME] < 1000"];
extraLabels = {
component = "monitoring-server";
tier = "monitoring";
};
};
})
(mkIf cfg.logging.enable {
homelab.services.${serviceName}.logging = mkDefault {
}
{
homelab.services.${serviceName}.logging = {
files = ["/var/log/prometheus/prometheus.log"];
parsing = {
# 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";
};
};
})
(mkIf cfg.proxy.enable {
homelab.services.${serviceName}.proxy = mkDefault {
subdomain = "prometheus";
enableAuth = true; # Admin interface needs protection
}
{
homelab.services.${serviceName}.proxy = {
enableAuth = true;
};
})
}
]);
}

View file

@ -30,7 +30,7 @@ writeShellScriptBin "homelab-docs-readme" ''
## 🚀 Quick Actions
### View Current Status
\`\`\`bash
```bash
# Service status across fleet (if homelab CLI is available)
homelab services --global
@ -39,16 +39,16 @@ writeShellScriptBin "homelab-docs-readme" ''
# Overall status
homelab status
\`\`\`
```
### Update Documentation
\`\`\`bash
```bash
# Regenerate all documentation
homelab-generate-docs ./docs
# Generate in different directory
homelab-generate-docs /path/to/output
\`\`\`
```
## 📋 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,
jq,
@ -10,261 +9,376 @@ writeShellScriptBin "homelab-docs-services" ''
cat << 'EOF'
# Service Catalog
> Available services and their configuration options
> Complete service documentation with core options, feature integrations, and smart defaults
>
> 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
# Get all services and their configurations
services_catalog=$(colmena eval -E '{ nodes, pkgs, lib, ... }:
# Extract comprehensive service information
echo "Extracting service information..." >&2
services_catalog=$(colmena eval -E '
{ nodes, pkgs, lib, ... }:
let
# Collect all services from all nodes to build a complete catalog
allServiceConfigs = lib.flatten (lib.mapAttrsToList (nodeName: node:
if (node.config.homelab.enable or false) then
lib.mapAttrsToList (serviceName: service: {
inherit serviceName;
config = {
# Core service options
enable = service.enable or false;
port = service.port or null;
description = service.description or serviceName;
tags = service.tags or [];
# Integration options
monitoring = {
enabled = service.monitoring.enable or false;
metricsPath = service.monitoring.metrics.path or "/metrics";
healthPath = service.monitoring.healthCheck.path or "/health";
extraLabels = service.monitoring.extraLabels or {};
};
logging = {
enabled = service.logging.enable or false;
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 {})
# Helper to extract option information
extractOptions = path: options:
lib.flatten (lib.mapAttrsToList (name: value:
let
currentPath = path ++ [name];
pathStr = lib.concatStringsSep "." currentPath;
in
if (value._type or null) == "option" then
[{
name = pathStr;
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 []
) nodes);
) options);
# Group by service name and merge configurations
serviceGroups = lib.groupBy (svc: svc.serviceName) allServiceConfigs;
# Get first node for option definitions
firstNode = lib.head (lib.attrValues nodes);
homelabServices = firstNode.options.homelab.services or {};
# Get unique services with merged configuration examples
uniqueServices = lib.mapAttrs (serviceName: instances:
# Process each service
serviceInfo = lib.mapAttrs (serviceName: serviceOptions:
let
# Take the first enabled instance as the canonical example
enabledInstances = lib.filter (inst: inst.config.enable) instances;
canonicalConfig = if enabledInstances != [] then (lib.head enabledInstances).config else (lib.head instances).config;
allOptions = extractOptions [] serviceOptions;
# 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 {
inherit serviceName;
config = canonicalConfig;
deploymentCount = lib.length (lib.filter (inst: inst.config.enable) instances);
deployedOn = lib.unique (map (inst: inst.deployedOn or "unknown") enabledInstances);
coreOptions = coreOptions;
features = {
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 {
services = uniqueServices;
totalUniqueServices = lib.length (lib.attrNames uniqueServices);
}')
services = serviceInfo;
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
echo "**Total Available Services:** $total_services"
echo
# Create a summary table of services and their default integrations
# Service matrix
echo "## Service Integration Matrix"
echo
echo "| Service | Monitoring | Logging | Proxy | Auth Default |"
echo "|---------|------------|---------|-------|--------------|"
echo "| Service | Core Options | Monitoring | Logging | Proxy | Deployments |"
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\"]")
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')
auth_default=$(echo "$service_data" | ${jq}/bin/jq -r '.config.proxy.enableAuth')
core_count=$(echo "$service_data" | ${jq}/bin/jq -r '.coreOptions | length')
has_monitoring=$(echo "$service_data" | ${jq}/bin/jq -r '.features.monitoring.available')
has_logging=$(echo "$service_data" | ${jq}/bin/jq -r '.features.logging.available')
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)
logging_icon=$(if [[ "$logging_enabled" == "true" ]]; then echo ""; else echo ""; fi)
proxy_icon=$(if [[ "$proxy_enabled" == "true" ]]; then echo ""; else echo ""; fi)
auth_icon=$(if [[ "$auth_default" == "true" ]]; then echo "🔒"; else echo "🌐"; fi)
monitoring_icon=$(if [[ "$has_monitoring" == "true" ]]; then echo "📊"; else echo ""; fi)
logging_icon=$(if [[ "$has_logging" == "true" ]]; then echo "📝"; else echo ""; fi)
proxy_icon=$(if [[ "$has_proxy" == "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
echo
echo "**Legend:** = Enabled by default, = Available but disabled, 🔒 = Auth required, 🌐 = Public access"
echo "**Legend:** 📊📝🔀 = Feature available, = Feature not available"
echo
echo "## Service Reference"
echo "## Service Documentation"
echo
# 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
# Get service details
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"')
port=$(echo "$service_data" | ${jq}/bin/jq -r '.config.port // "N/A"')
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\` |"
if [[ "$total_deployments" -gt 0 ]]; then
echo "**Deployment Status:** $enabled_deployments/$total_deployments nodes have this service enabled"
else
echo "| 📊 Monitoring | Disabled | Available but requires \`monitoring.enable = true\` |"
echo "**Deployment Status:** Available but not configured"
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
# Core Configuration
echo "#### Core Configuration"
# Core Service Configuration
echo "#### Core Service Options"
echo
echo "\`\`\`nix"
echo "The main configuration options for $service:"
echo
echo '```nix'
echo "homelab.services.$service = {"
echo " enable = true;"
if [[ "$port" != "N/A" ]]; then
echo " port = $port;"
echo "$service_data" | ${jq}/bin/jq -r '.coreOptions[] | @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
echo " description = \"$description\";"
if [[ -n "$tags" && "$tags" != "" ]]; then
echo " tags = [ $(echo "$tags" | sed 's/, /" "/g' | sed 's/^/"/; s/$/"/') ];"
fi
echo
echo " # Default integrations (adjust as needed)"
if [[ "$monitoring_enabled" == "true" ]]; then
echo " monitoring.enable = true; # Enabled by default"
clean_description=$(echo "$description" | sed 's/"/\\"/g' | tr -d $'\n\r')
if [[ "$default_val" == "null" ]]; then
echo " # $name = <$type>; # $clean_description"
else
echo " # monitoring.enable = true; # Disabled by default"
fi
if [[ "$logging_enabled" == "true" ]]; then
echo " logging.enable = true; # Enabled by default"
else
echo " # logging.enable = true; # Disabled by default"
fi
if [[ "$proxy_enabled" == "true" ]]; then
echo " proxy.enable = true; # Enabled by default"
else
echo " # proxy.enable = true; # Disabled by default"
echo " $name = $default_val; # $clean_description"
fi
done
echo "};"
echo "\`\`\`"
echo '```'
echo
# Service-specific options
service_specific=$(echo "$service_data" | ${jq}/bin/jq -r '.config.serviceSpecific')
if [[ "$service_specific" != "{}" && "$service_specific" != "null" ]]; then
echo "#### Service-Specific Options"
# Feature Integrations
has_monitoring=$(echo "$service_data" | ${jq}/bin/jq -r '.features.monitoring.available')
has_logging=$(echo "$service_data" | ${jq}/bin/jq -r '.features.logging.available')
has_proxy=$(echo "$service_data" | ${jq}/bin/jq -r '.features.proxy.available')
if [[ "$has_monitoring" == "true" || "$has_logging" == "true" || "$has_proxy" == "true" ]]; then
echo "#### Feature Integrations"
echo
echo "Available configuration options for $service:"
# Monitoring Feature
if [[ "$has_monitoring" == "true" ]]; then
echo "##### 📊 Monitoring Integration"
echo
echo "\`\`\`nix"
echo "Available monitoring options:"
echo
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 "$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 "\`\`\`"
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
echo "---"
echo
done
echo "## Integration Summary"
echo "## Feature Reference"
echo
echo "### Available Integration Types"
echo "### Integration Features"
echo
echo "| Integration | Purpose | Default Behavior | Configuration |"
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 "Homelab services can integrate with three main features:"
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 "- **🔄 Automatic Discovery:** Enabled integrations are automatically discovered by fleet-wide services"
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 "Each service can import these features and set service-specific defaults."
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.*"
''