11 Commits

Author SHA1 Message Date
Yury Kossakovsky
a99676e3d5 fix(postiz): improve temporal integration
- increase elasticsearch memory to 512mb
- add temporal databases to initialization
- add postiz to final report
2026-01-17 19:56:29 -07:00
Yury Kossakovsky
bf7ce20f7b fix(caddy): add http block for welcome page to prevent redirect loop
when accessing welcome page through cloudflare tunnel, caddy was
redirecting http to https, causing an infinite redirect loop.
adding an explicit http block prevents automatic https redirect.
2026-01-17 19:42:50 -07:00
Yury Kossakovsky
36717a45c9 docs(readme): clarify vps requirement in prerequisites 2026-01-17 12:28:55 -07:00
Yury Kossakovsky
31b81b71a4 fix(postiz): add elasticsearch for temporal advanced visibility
temporal with sql visibility has a hard limit of 3 text search
attributes per namespace. postiz requires more, causing startup
failure. adding elasticsearch enables advanced visibility mode
which removes this limitation.
2026-01-17 12:26:40 -07:00
Yury Kossakovsky
a3e8f26925 fix(postiz): use correct temporal address env var 2026-01-16 20:27:06 -07:00
Yury Kossakovsky
917afe615c fix(temporal): use container ip for healthcheck connection 2026-01-16 20:15:03 -07:00
Yury Kossakovsky
641fd04290 fix(temporal): update healthcheck to use modern cli 2026-01-16 18:59:37 -07:00
Yury Kossakovsky
ca43e7ab12 docs(readme): add troubleshooting for update script issues 2026-01-16 18:48:31 -07:00
Yury Kossakovsky
e5db00098a refactor(docker-compose): extract logging config into yaml anchor 2026-01-16 18:45:30 -07:00
Yury Kossakovsky
4a6f1c0e01 feat(postiz): add temporal server for workflow orchestration
add temporal and temporal-ui services to the postiz profile for
workflow orchestration. includes caddy reverse proxy with basic
auth, secret generation, and welcome page integration.
2026-01-16 18:42:54 -07:00
Yury Kossakovsky
19cd6b6f91 docs(cloudflare): update tunnel instructions and add missing services
- update dashboard navigation to match current cloudflare ui
- add nocodb and welcome page to services table
- add notes explaining external compose files and caddy-served content
2026-01-13 08:40:36 -07:00
12 changed files with 182 additions and 52 deletions

View File

@@ -314,14 +314,16 @@ ${SERVICE_NAME_UPPER}_PASSWORD=
${SERVICE_NAME_UPPER}_PASSWORD_HASH=
```
### 3.3 GOST_NO_PROXY (if using proxy-env)
### 3.3 GOST_NO_PROXY (REQUIRED for ALL services)
Add service to comma-separated list:
**CRITICAL:** Add ALL new service container names to the comma-separated list to prevent internal Docker traffic from going through the proxy:
```dotenv
GOST_NO_PROXY=localhost,127.0.0.1,...existing...,$ARGUMENTS
```
This applies to ALL services, not just those using `<<: *proxy-env`. Internal service-to-service communication must bypass the proxy.
---
## STEP 4: scripts/03_generate_secrets.sh
@@ -706,6 +708,7 @@ bash -n scripts/07_final_report.sh
- [ ] `docker-compose.yml`: caddy environment vars (if external)
- [ ] `Caddyfile`: reverse proxy block (if external)
- [ ] `.env.example`: hostname added
- [ ] `.env.example`: service added to `GOST_NO_PROXY` (ALL internal services must be listed)
- [ ] `scripts/03_generate_secrets.sh`: password in `VARS_TO_GENERATE`
- [ ] `scripts/04_wizard.sh`: service in `base_services_data`
- [ ] `scripts/generate_welcome_page.sh`: `SERVICES_ARRAY` entry
@@ -722,7 +725,6 @@ bash -n scripts/07_final_report.sh
### If Outbound Proxy (AI API calls)
- [ ] `docker-compose.yml`: `<<: *proxy-env` in environment
- [ ] `.env.example`: service added to `GOST_NO_PROXY`
- [ ] `docker-compose.yml`: healthcheck bypasses proxy
### If Database Required

View File

@@ -164,6 +164,7 @@ NOCODB_HOSTNAME=nocodb.yourdomain.com
PADDLEOCR_HOSTNAME=paddleocr.yourdomain.com
PORTAINER_HOSTNAME=portainer.yourdomain.com
POSTIZ_HOSTNAME=postiz.yourdomain.com
TEMPORAL_UI_HOSTNAME=temporal.yourdomain.com
PROMETHEUS_HOSTNAME=prometheus.yourdomain.com
QDRANT_HOSTNAME=qdrant.yourdomain.com
RAGAPP_HOSTNAME=ragapp.yourdomain.com
@@ -433,7 +434,7 @@ GOST_UPSTREAM_PROXY=
# Internal services bypass list (prevents internal Docker traffic from going through proxy)
# Includes: Docker internal networks (172.16-31.*, 10.*), Docker DNS (127.0.0.11), and all service hostnames
GOST_NO_PROXY=localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.local,postgres,postgres:5432,redis,redis:6379,caddy,ollama,neo4j,qdrant,weaviate,clickhouse,minio,searxng,crawl4ai,gotenberg,langfuse-web,langfuse-worker,flowise,n8n,n8n-import,n8n-worker-1,n8n-worker-2,n8n-worker-3,n8n-worker-4,n8n-worker-5,n8n-worker-6,n8n-worker-7,n8n-worker-8,n8n-worker-9,n8n-worker-10,n8n-runner-1,n8n-runner-2,n8n-runner-3,n8n-runner-4,n8n-runner-5,n8n-runner-6,n8n-runner-7,n8n-runner-8,n8n-runner-9,n8n-runner-10,letta,lightrag,docling,postiz,ragflow,ragflow-mysql,ragflow-minio,ragflow-redis,ragflow-elasticsearch,ragapp,open-webui,comfyui,waha,libretranslate,paddleocr,nocodb,db,studio,kong,auth,rest,realtime,storage,imgproxy,meta,functions,analytics,vector,supavisor,gost
GOST_NO_PROXY=localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.local,postgres,postgres:5432,redis,redis:6379,caddy,ollama,neo4j,qdrant,weaviate,clickhouse,minio,searxng,crawl4ai,gotenberg,langfuse-web,langfuse-worker,flowise,n8n,n8n-import,n8n-worker-1,n8n-worker-2,n8n-worker-3,n8n-worker-4,n8n-worker-5,n8n-worker-6,n8n-worker-7,n8n-worker-8,n8n-worker-9,n8n-worker-10,n8n-runner-1,n8n-runner-2,n8n-runner-3,n8n-runner-4,n8n-runner-5,n8n-runner-6,n8n-runner-7,n8n-runner-8,n8n-runner-9,n8n-runner-10,letta,lightrag,docling,postiz,temporal,temporal-ui,ragflow,ragflow-mysql,ragflow-minio,ragflow-redis,ragflow-elasticsearch,ragapp,open-webui,comfyui,waha,libretranslate,paddleocr,nocodb,db,studio,kong,auth,rest,realtime,storage,imgproxy,meta,functions,analytics,vector,supavisor,gost
############
# Functions - Configuration for Functions
@@ -489,6 +490,13 @@ RAGAPP_PASSWORD_HASH=
POSTIZ_DISABLE_REGISTRATION=false
############
# Temporal UI credentials (for Caddy basic auth)
############
TEMPORAL_UI_USERNAME=
TEMPORAL_UI_PASSWORD=
TEMPORAL_UI_PASSWORD_HASH=
############
# Postiz Social Media Integrations
# Leave blank if not used. Provide credentials from each platform.

View File

@@ -1,5 +1,12 @@
# Changelog
## [Unreleased]
## [1.2.1] - 2026-01-16
### Added
- **Temporal** - Temporal server and UI for Postiz workflow orchestration (#33)
## [1.2.0] - 2026-01-12
### Added

View File

@@ -81,6 +81,14 @@
reverse_proxy postiz:5000
}
# Temporal UI (workflow orchestration for Postiz)
{$TEMPORAL_UI_HOSTNAME} {
basic_auth {
{$TEMPORAL_UI_USERNAME} {$TEMPORAL_UI_PASSWORD_HASH}
}
reverse_proxy temporal-ui:8080
}
# Databasus
{$DATABASUS_HOSTNAME} {
reverse_proxy databasus:4005
@@ -154,6 +162,17 @@ https://{$NEO4J_HOSTNAME}:7687 {
}
# Welcome Page (Post-install dashboard)
# HTTP block for Cloudflare Tunnel access (prevents redirect loop)
http://{$WELCOME_HOSTNAME} {
basic_auth {
{$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
}
root * /srv/welcome
file_server
try_files {path} /index.html
}
# HTTPS block for direct access
{$WELCOME_HOSTNAME} {
basic_auth {
{$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}

View File

@@ -137,9 +137,10 @@ Get started quickly with a vast library of pre-built automations (optional impor
1. **Domain Name:** You need a registered domain name (e.g., `yourdomain.com`).
2. **DNS Configuration:** Before running the installation script, you **must** configure DNS A-record for your domain, pointing to the public IP address of the server where you'll install this system. Replace `yourdomain.com` with your actual domain:
- **Wildcard Record:** `A *.yourdomain.com` -> `YOUR_SERVER_IP`
3. **Server:** Minimum server system requirements: Ubuntu 24.04 LTS, 64-bit.
- For running **all available services**: at least **20 GB Memory / 4 CPU Cores / 60 GB Disk Space**.
- For a minimal setup with **n8n, Monitoring, Databasus and Portainer**: **4 GB Memory / 2 CPU Cores / 40 GB Disk Space**.
3. **VPS (Virtual Private Server):** A dedicated VPS with a public IP address is required. Home servers, shared hosting, or localhost setups are not supported.
- **Operating System:** Ubuntu 24.04 LTS, 64-bit
- For a minimal setup with **n8n, Monitoring, Databasus and Portainer**: **4 GB Memory / 2 CPU Cores / 40 GB Disk Space**
- For running **all available services**: at least **20 GB Memory / 4 CPU Cores / 60 GB Disk Space**
### Running the Install
@@ -367,6 +368,18 @@ Here are solutions to common issues you might encounter:
- **VPN Conflicts:** Using a VPN might interfere with downloading Docker images. If you encounter issues pulling images, try temporarily disabling your VPN.
- **Server Requirements:** If you experience unexpected issues, ensure your server meets the minimum hardware and operating system requirements (including version) as specified in the "Prerequisites before Installation" section.
### Update Script Not Working
- **Symptom:** The `make update` command fails, shows errors, or doesn't apply the latest changes.
- **Cause:** This can happen if your local repository has diverged from the upstream, has uncommitted changes, or is in an inconsistent state.
- **Solution:** Run the following command to force-sync your local installation with the latest version:
```bash
git config pull.rebase true && git fetch origin && git checkout main && git reset --hard "origin/main" && make update
```
**Warning:** This will discard any local changes you've made to the installer files. If you've customized any scripts or configurations, back them up first.
## Recommended Reading
n8n offers excellent resources for getting started with its AI capabilities:

View File

@@ -22,8 +22,8 @@ Cloudflare Tunnel **bypasses Caddy** and connects directly to your services. Thi
1. Go to [Cloudflare One Dashboard](https://one.dash.cloudflare.com/)
2. Navigate to **Networks****Connectors****Cloudflare Tunnels**
3. Click **Create new cloudflared Tunnel**
4. Choose **Cloudflared** connector and click **Next**
3. Click **Create a tunnel**
4. Select **Cloudflared** as the connector type and click **Next**
5. Name your tunnel (e.g., "n8n-install") and click **Save tunnel**
6. Copy the installation command shown - it contains your tunnel token
@@ -106,7 +106,7 @@ dig NS yourdomain.com +short
#### 3. Configure Public Hostnames
After DNS is configured, go to **Cloudflare Zero Trust** → **Networks** → **Tunnels** → your tunnel → **Public Hostname** tab. For each service you want to expose, click **Add a public hostname** and configure:
After DNS is configured, go to **Cloudflare One Dashboard** → **Networks** → **Connectors** → **Cloudflare Tunnels** → your tunnel → **Public Hostname** tab. For each service you want to expose, click **Add a public hostname** and configure:
| Service | Public Hostname | Service URL | Auth Notes |
| ------------------ | ----------------------------- | ---------------------------- | ------------------- |
@@ -122,6 +122,7 @@ After DNS is configured, go to **Cloudflare Zero Trust** → **Networks** → **
| **LibreTranslate** | libretranslate.yourdomain.com | `http://libretranslate:5000` | ⚠️ Loses Caddy auth |
| **LightRAG** | lightrag.yourdomain.com | `http://lightrag:9621` | No auth |
| **Neo4j** | neo4j.yourdomain.com | `http://neo4j:7474` | Built-in login |
| **NocoDB** | nocodb.yourdomain.com | `http://nocodb:8080` | Built-in login |
| **Open WebUI** | webui.yourdomain.com | `http://open-webui:8080` | Built-in login |
| **PaddleOCR** | paddleocr.yourdomain.com | `http://paddleocr:8080` | ⚠️ Loses Caddy auth |
| **Portainer** | portainer.yourdomain.com | `http://portainer:9000` | Built-in login |
@@ -134,6 +135,11 @@ After DNS is configured, go to **Cloudflare Zero Trust** → **Networks** → **
| **Supabase** ¹ | supabase.yourdomain.com | `http://kong:8000` | Built-in login |
| **WAHA** | waha.yourdomain.com | `http://waha:3000` | API key recommended |
| **Weaviate** | weaviate.yourdomain.com | `http://weaviate:8080` | API key recommended |
| **Welcome Page** ² | welcome.yourdomain.com | `http://caddy:80` | ⚠️ Loses Caddy auth |
**Notes:**
- ¹ Dify and Supabase use external compose files from adjacent directories
- ² Welcome Page is served by Caddy as static content; tunnel proxies through Caddy
**⚠️ Security Warning:**
- Services marked **"Loses Caddy auth"** have basic authentication via Caddy that is bypassed by the tunnel. Use [Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/applications/) or keep them internal.
@@ -181,7 +187,7 @@ You have two options for accessing your services:
For services that lose Caddy's basic auth protection, you can add Cloudflare Access:
1. In **Cloudflare One Dashboard** → **Access controls** → **Applications**
1. In **Cloudflare One Dashboard** → **Access** → **Applications** (or **Access controls** → **Applications** depending on your dashboard version)
2. Click **Add an application** → **Self-hosted**
3. Configure:
- **Application name**: e.g., "Prometheus"

View File

@@ -33,9 +33,17 @@ volumes:
ragflow_minio_data:
ragflow_mysql_data:
ragflow_redis_data:
temporal_elasticsearch_data:
valkey-data:
weaviate_data:
# Shared logging configuration for services
x-logging: &default-logging
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
# Shared proxy configuration for services that need outbound proxy support
x-proxy-env: &proxy-env
HTTP_PROXY: ${GOST_PROXY_URL:-}
@@ -274,11 +282,7 @@ services:
container_name: nocodb
profiles: ["nocodb"]
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
environment:
NC_AUTH_JWT_SECRET: ${NOCODB_JWT_SECRET}
NC_DB: pg://postgres:5432?u=postgres&p=${POSTGRES_PASSWORD}&d=nocodb
@@ -339,6 +343,9 @@ services:
PORTAINER_HOSTNAME: ${PORTAINER_HOSTNAME}
DATABASUS_HOSTNAME: ${DATABASUS_HOSTNAME}
POSTIZ_HOSTNAME: ${POSTIZ_HOSTNAME}
TEMPORAL_UI_HOSTNAME: ${TEMPORAL_UI_HOSTNAME}
TEMPORAL_UI_USERNAME: ${TEMPORAL_UI_USERNAME}
TEMPORAL_UI_PASSWORD_HASH: ${TEMPORAL_UI_PASSWORD_HASH}
PROMETHEUS_HOSTNAME: ${PROMETHEUS_HOSTNAME}
PROMETHEUS_PASSWORD_HASH: ${PROMETHEUS_PASSWORD_HASH}
PROMETHEUS_USERNAME: ${PROMETHEUS_USERNAME}
@@ -361,11 +368,7 @@ services:
- ALL
cap_add:
- NET_BIND_SERVICE
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
cloudflared:
image: cloudflare/cloudflared:latest
@@ -375,11 +378,7 @@ services:
command: tunnel --no-autoupdate run
environment:
TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN}
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
gost:
image: gogost/gost:latest
@@ -397,11 +396,7 @@ services:
timeout: 10s
retries: 3
start_period: 10s
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
langfuse-worker:
image: langfuse/langfuse-worker:3
@@ -553,11 +548,7 @@ services:
- SETGID
- SETUID
- DAC_OVERRIDE
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 3s
@@ -580,11 +571,7 @@ services:
- CHOWN
- SETGID
- SETUID
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
ollama-cpu:
profiles: ["cpu"]
@@ -778,6 +765,70 @@ services:
- portainer_data:/data
- ${DOCKER_SOCKET_LOCATION:-/var/run/docker.sock}:/var/run/docker.sock
temporal-elasticsearch:
image: elasticsearch:7.17.27
container_name: temporal-elasticsearch
profiles: ["postiz"]
restart: unless-stopped
logging: *default-logging
environment:
cluster.routing.allocation.disk.threshold_enabled: "true"
cluster.routing.allocation.disk.watermark.low: 512mb
cluster.routing.allocation.disk.watermark.high: 256mb
cluster.routing.allocation.disk.watermark.flood_stage: 128mb
discovery.type: single-node
ES_JAVA_OPTS: -Xms512m -Xmx512m
xpack.security.enabled: "false"
volumes:
- temporal_elasticsearch_data:/usr/share/elasticsearch/data
healthcheck:
test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -qE '\"status\":\"(green|yellow)\"'"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
temporal:
image: temporalio/auto-setup:latest
container_name: temporal
profiles: ["postiz"]
restart: unless-stopped
logging: *default-logging
environment:
DB: postgres12
POSTGRES_USER: postgres
POSTGRES_PWD: ${POSTGRES_PASSWORD}
POSTGRES_SEEDS: postgres
DB_PORT: 5432
TEMPORAL_NAMESPACE: default
ENABLE_ES: "true"
ES_SEEDS: temporal-elasticsearch
ES_VERSION: v7
depends_on:
postgres:
condition: service_healthy
temporal-elasticsearch:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "temporal operator cluster health --address $(hostname -i):7233 | grep -q SERVING || exit 1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
temporal-ui:
image: temporalio/ui:latest
container_name: temporal-ui
profiles: ["postiz"]
restart: unless-stopped
logging: *default-logging
environment:
TEMPORAL_ADDRESS: temporal:7233
TEMPORAL_CORS_ORIGINS: http://localhost:3000
depends_on:
temporal:
condition: service_healthy
postiz:
image: ghcr.io/gitroomhq/postiz-app:latest
container_name: postiz
@@ -796,6 +847,7 @@ services:
NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads"
REDIS_URL: "redis://redis:6379"
STORAGE_PROVIDER: "local"
TEMPORAL_ADDRESS: temporal:7233
UPLOAD_DIRECTORY: "/uploads"
# Social Media API Settings
X_API_KEY: ${X_API_KEY}
@@ -837,17 +889,15 @@ services:
condition: service_healthy
redis:
condition: service_healthy
temporal:
condition: service_healthy
databasus:
image: databasus/databasus:latest
container_name: databasus
profiles: ["databasus"]
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
volumes:
- databasus_data:/databasus-data
healthcheck:
@@ -1044,11 +1094,7 @@ services:
- SETGID
- SETUID
- DAC_OVERRIDE
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: "1"
logging: *default-logging
healthcheck:
test: ["CMD", "valkey-cli", "-a", "${RAGFLOW_REDIS_PASSWORD}", "ping"]
interval: 3s

View File

@@ -55,6 +55,7 @@ EMAIL_VARS=(
"PROMETHEUS_USERNAME"
"RAGAPP_USERNAME"
"SEARXNG_USERNAME"
"TEMPORAL_UI_USERNAME"
"WAHA_DASHBOARD_USERNAME"
"WEAVIATE_USERNAME"
"WELCOME_USERNAME"
@@ -114,6 +115,7 @@ declare -A VARS_TO_GENERATE=(
["RAGFLOW_REDIS_PASSWORD"]="password:32"
["SEARXNG_PASSWORD"]="password:32" # Added SearXNG admin password
["SECRET_KEY_BASE"]="base64:64" # 48 bytes -> 64 chars
["TEMPORAL_UI_PASSWORD"]="password:32" # Temporal UI basic auth password
["VAULT_ENC_KEY"]="alphanum:32"
["WAHA_DASHBOARD_PASSWORD"]="password:32"
["WEAVIATE_API_KEY"]="secret:48" # API Key for Weaviate service (36 bytes -> 48 chars base64)
@@ -564,7 +566,7 @@ if [[ -n "$template_no_proxy" ]]; then
fi
# Hash passwords using caddy with bcrypt (consolidated loop)
SERVICES_NEEDING_HASH=("PROMETHEUS" "SEARXNG" "COMFYUI" "PADDLEOCR" "RAGAPP" "LT" "DOCLING" "WELCOME")
SERVICES_NEEDING_HASH=("PROMETHEUS" "SEARXNG" "COMFYUI" "PADDLEOCR" "RAGAPP" "LT" "DOCLING" "TEMPORAL_UI" "WELCOME")
for service in "${SERVICES_NEEDING_HASH[@]}"; do
password_var="${service}_PASSWORD"

View File

@@ -97,6 +97,9 @@ fi
if is_profile_active "nocodb"; then
echo -e " ${GREEN}*${NC} ${WHITE}NocoDB${NC}: Create your account on first login"
fi
if is_profile_active "postiz"; then
echo -e " ${GREEN}*${NC} ${WHITE}Postiz${NC}: Create your account on first login"
fi
if is_profile_active "gost"; then
echo -e " ${GREEN}*${NC} ${WHITE}Gost Proxy${NC}: Routing AI traffic through external proxy"
fi

View File

@@ -30,6 +30,8 @@ INIT_DB_DATABASES=(
"lightrag"
"nocodb"
"postiz"
"temporal"
"temporal_visibility"
"waha"
)

View File

@@ -327,6 +327,20 @@ if is_profile_active "postiz"; then
}")
fi
# Temporal UI
if is_profile_active "postiz"; then
SERVICES_ARRAY+=(" \"temporal-ui\": {
\"hostname\": \"$(json_escape "$TEMPORAL_UI_HOSTNAME")\",
\"credentials\": {
\"username\": \"$(json_escape "$TEMPORAL_UI_USERNAME")\",
\"password\": \"$(json_escape "$TEMPORAL_UI_PASSWORD")\"
},
\"extra\": {
\"note\": \"Workflow orchestration admin for Postiz\"
}
}")
fi
# WAHA
if is_profile_active "waha"; then
SERVICES_ARRAY+=(" \"waha\": {

View File

@@ -340,6 +340,14 @@
category: 'tools',
docsUrl: 'https://docs.postiz.com'
},
'temporal-ui': {
name: 'Temporal UI',
description: 'Postiz Workflow Orchestration',
icon: 'TM',
color: 'bg-violet-500',
category: 'tools',
docsUrl: 'https://docs.temporal.io/'
},
'waha': {
name: 'WAHA',
description: 'WhatsApp HTTP API',