stuffy stuff

This commit is contained in:
counterweight 2026-08-08 12:00:27 +02:00
parent 0736f29f79
commit ba4ba504d0
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
4 changed files with 912 additions and 0 deletions

View file

@ -31,3 +31,10 @@ mariadb_mempool_password: "CHANGE_ME_TO_SECURE_PASSWORD"
# Used by: services/forgejo-runner/deploy_forgejo_runner_playbook.yml # Used by: services/forgejo-runner/deploy_forgejo_runner_playbook.yml
# See: services/forgejo-runner/SETUP.md for how to obtain this token # See: services/forgejo-runner/SETUP.md for how to obtain this token
forgejo_runner_registration_token: "YOUR_RUNNER_TOKEN_HERE" forgejo_runner_registration_token: "YOUR_RUNNER_TOKEN_HERE"
# DATUM Gateway secrets
# Used by: services/datum-gateway/deploy_datum_gateway_playbook.yml
datum_mining_address: "YOUR_BITCOIN_ADDRESS_FOR_BLOCK_REWARDS"
datum_gateway_admin_password: "CHANGE_ME_TO_SECURE_PASSWORD"
datum_dashboard_username: "admin"
datum_dashboard_password_hash: "$2a$14$..." # Generate with: caddy hash-password

View file

@ -0,0 +1,43 @@
# DATUM Gateway Configuration Variables
# https://github.com/OCEAN-xyz/datum_gateway
# Version - pin to a specific tag
datum_gateway_version: "v0.4.1beta"
# Directories
datum_gateway_dir: /opt/datum-gateway
datum_gateway_source_dir: "{{ datum_gateway_dir }}/source"
datum_gateway_config_dir: /etc/datum-gateway
datum_gateway_log_dir: /var/log/datum-gateway
# Binary
datum_gateway_bin_path: /usr/local/bin/datum_gateway
# Ports
datum_gateway_stratum_port: 23334 # Miners connect here via Stratum v1
datum_gateway_api_port: 7152 # Web dashboard / API
# Stratum settings
datum_vardiff_min: 524288 # Minimum share difficulty (must be power of 2; OCEAN floor overrides if higher)
# Service user
datum_gateway_user: datum
datum_gateway_group: datum
# Build options
datum_gateway_build_jobs: 4
# Bitcoin node connection
# The gateway runs on the same host as Bitcoin Knots so localhost RPC works.
# datum_bitcoin_rpc_url should include http:// and port.
datum_bitcoin_rpc_url: "http://127.0.0.1:8332"
# Note: bitcoin_rpc_user and bitcoin_rpc_password come from infra_secrets.yml
# Mining config
datum_coinbase_tag_primary: "DATUM"
datum_coinbase_tag_secondary: "BY ORDER OF BIP110"
datum_pool_pass_workers: true
datum_pool_pass_full_users: true
datum_pooled_mining_only: true

View file

@ -0,0 +1,859 @@
---
# DATUM Gateway Deployment Playbook
#
# Deploys DATUM Gateway (https://github.com/OCEAN-xyz/datum_gateway) on the
# Bitcoin Knots host so it has direct localhost RPC access to bitcoind.
#
# What this does:
# 1. Installs build deps and compiles datum_gateway from source
# 2. Creates a dedicated system user and config/log directories
# 3. Writes /etc/datum-gateway/config.json from vars/secrets
# 4. Patches bitcoin.conf with the required blockmaxsize/blocknotify lines
# 5. Creates and enables a systemd service
# 6. Creates a push-monitor health check script + systemd timer
# 7. Registers a push monitor in Uptime Kuma
#
# Separate play: adds a Caddy reverse proxy on vipy for the dashboard.
#
# Stratum port (23334) is bound on knots_box_local. Expose it to miners via
# a firewall rule, Tailscale, or a socket proxy on vipy — not handled here.
#
# Required secrets in infra_secrets.yml:
# datum_mining_address - Bitcoin address for block rewards
# datum_gateway_admin_password - Password for the /api admin endpoint
# bitcoin_rpc_user - Shared with the bitcoin-knots deployment
# bitcoin_rpc_password - Shared with the bitcoin-knots deployment
- name: Deploy DATUM Gateway on knots_box_local
hosts: knots_box_local
become: yes
vars_files:
- ../../infra_vars.yml
- ../../services_config.yml
- ../../infra_secrets.yml
- ./datum_gateway_vars.yml
vars:
datum_gateway_subdomain: "{{ subdomains.datum_gateway }}"
datum_gateway_domain: "{{ datum_gateway_subdomain }}.{{ root_domain }}"
uptime_kuma_api_url: "https://{{ subdomains.uptime_kuma }}.{{ root_domain }}"
tasks:
# ===========================================
# Build Dependencies
# ===========================================
- name: Install DATUM Gateway build dependencies
apt:
name:
- cmake
- build-essential
- git
- libjansson-dev
- libmicrohttpd-dev
- libsodium-dev
- libcurl4-openssl-dev
# Runtime-only (netcat for health check)
- netcat-openbsd
state: present
update_cache: yes
# ===========================================
# System User and Directories
# ===========================================
- name: Create datum system user
user:
name: "{{ datum_gateway_user }}"
system: yes
shell: /usr/sbin/nologin
home: "{{ datum_gateway_dir }}"
create_home: no
comment: "DATUM Gateway"
- name: Create DATUM Gateway directories
file:
path: "{{ item.path }}"
state: directory
owner: "{{ item.owner }}"
group: "{{ datum_gateway_group }}"
mode: "{{ item.mode }}"
loop:
- { path: "{{ datum_gateway_dir }}", owner: root, mode: "0755" }
- { path: "{{ datum_gateway_source_dir }}", owner: root, mode: "0755" }
- { path: "{{ datum_gateway_config_dir }}", owner: "{{ datum_gateway_user }}", mode: "0750" }
- { path: "{{ datum_gateway_log_dir }}", owner: "{{ datum_gateway_user }}", mode: "0750" }
# ===========================================
# Build from Source
# ===========================================
- name: Clone DATUM Gateway repository at {{ datum_gateway_version }}
git:
repo: https://github.com/OCEAN-xyz/datum_gateway.git
dest: "{{ datum_gateway_source_dir }}"
version: "{{ datum_gateway_version }}"
force: yes
register: git_clone
- name: Configure cmake build
command: cmake . -DCMAKE_BUILD_TYPE=Release
args:
chdir: "{{ datum_gateway_source_dir }}"
- name: Compile datum_gateway
command: make -j{{ datum_gateway_build_jobs }}
args:
chdir: "{{ datum_gateway_source_dir }}"
- name: Install datum_gateway binary
copy:
src: "{{ datum_gateway_source_dir }}/datum_gateway"
dest: "{{ datum_gateway_bin_path }}"
remote_src: yes
owner: root
group: root
mode: "0755"
notify: Restart datum-gateway
# ===========================================
# Configuration
# ===========================================
- name: Write DATUM Gateway config.json
copy:
dest: "{{ datum_gateway_config_dir }}/config.json"
content: |
{
"bitcoind": {
"rpcuser": "{{ bitcoin_rpc_user }}",
"rpcpassword": "{{ bitcoin_rpc_password }}",
"rpcurl": "{{ datum_bitcoin_rpc_url }}",
"notify_fallback": true
},
"stratum": {
"listen_port": {{ datum_gateway_stratum_port }},
"vardiff_min": {{ datum_vardiff_min }}
},
"mining": {
"pool_address": "{{ datum_mining_address }}",
"coinbase_tag_primary": "{{ datum_coinbase_tag_primary }}",
"coinbase_tag_secondary": "{{ datum_coinbase_tag_secondary }}"
},
"api": {
"admin_password": "{{ datum_gateway_admin_password }}",
"listen_port": {{ datum_gateway_api_port }},
"modify_conf": false
},
"logger": {
"log_to_console": true,
"log_to_file": true,
"log_file": "{{ datum_gateway_log_dir }}/datum_gateway.log",
"log_rotate_daily": true,
"log_level_console": 2,
"log_level_file": 1
},
"datum": {
"pool_pass_workers": {{ datum_pool_pass_workers | lower }},
"pool_pass_full_users": {{ datum_pool_pass_full_users | lower }},
"pooled_mining_only": {{ datum_pooled_mining_only | lower }}
}
}
owner: "{{ datum_gateway_user }}"
group: "{{ datum_gateway_group }}"
mode: "0640"
notify: Restart datum-gateway
# ===========================================
# Systemd Service
# ===========================================
- name: Create datum-gateway systemd service
copy:
dest: /etc/systemd/system/datum-gateway.service
content: |
[Unit]
Description=DATUM Gateway - Bitcoin Mining Gateway
Documentation=https://github.com/OCEAN-xyz/datum_gateway
After=network.target bitcoind.service
Wants=bitcoind.service
[Service]
User={{ datum_gateway_user }}
Group={{ datum_gateway_group }}
Type=simple
ExecStart={{ datum_gateway_bin_path }} --config {{ datum_gateway_config_dir }}/config.json
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
# Prevent config from being read by other users
ReadWritePaths={{ datum_gateway_log_dir }}
ReadOnlyPaths={{ datum_gateway_config_dir }}
[Install]
WantedBy=multi-user.target
owner: root
group: root
mode: "0644"
notify: Restart datum-gateway
- name: Reload systemd daemon
systemd:
daemon_reload: yes
- name: Enable and start datum-gateway
systemd:
name: datum-gateway
enabled: yes
state: started
# ===========================================
# Health Check Script + Systemd Timer
# ===========================================
- name: Create DATUM Gateway health check script
copy:
dest: /usr/local/bin/datum-gateway-healthcheck-push.sh
content: |
#!/bin/bash
UPTIME_KUMA_PUSH_URL="${UPTIME_KUMA_PUSH_URL}"
STRATUM_PORT={{ datum_gateway_stratum_port }}
check_datum() {
# Service must be active and stratum port must be listening
systemctl is-active --quiet datum-gateway && \
nc -z 127.0.0.1 "${STRATUM_PORT}"
}
push_to_uptime_kuma() {
local status=$1
local msg=$2
if [ -z "$UPTIME_KUMA_PUSH_URL" ]; then
echo "ERROR: UPTIME_KUMA_PUSH_URL not set"
return 1
fi
curl -s --max-time 10 --retry 2 -o /dev/null \
"${UPTIME_KUMA_PUSH_URL}?status=${status}&msg=${msg// /%20}&ping=" || true
}
if check_datum; then
push_to_uptime_kuma "up" "OK"
exit 0
else
push_to_uptime_kuma "down" "DATUM Gateway not responding"
exit 1
fi
owner: root
group: root
mode: "0755"
- name: Create datum-gateway health check systemd service
copy:
dest: /etc/systemd/system/datum-gateway-healthcheck.service
content: |
[Unit]
Description=DATUM Gateway Health Check
After=network.target datum-gateway.service
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/datum-gateway-healthcheck-push.sh
Environment=UPTIME_KUMA_PUSH_URL=
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
owner: root
group: root
mode: "0644"
- name: Create datum-gateway health check systemd timer
copy:
dest: /etc/systemd/system/datum-gateway-healthcheck.timer
content: |
[Unit]
Description=DATUM Gateway Health Check Timer
[Timer]
OnBootSec=2min
OnUnitActiveSec=1min
Persistent=true
[Install]
WantedBy=timers.target
owner: root
group: root
mode: "0644"
- name: Reload systemd daemon after health check units
systemd:
daemon_reload: yes
- name: Enable and start datum-gateway health check timer
systemd:
name: datum-gateway-healthcheck.timer
enabled: yes
state: started
# ===========================================
# Uptime Kuma Push Monitor Setup
# ===========================================
- name: Create Uptime Kuma push monitor setup script for DATUM Gateway
delegate_to: localhost
become: no
copy:
dest: /tmp/setup_datum_gateway_monitor.py
content: |
#!/usr/bin/env python3
import sys
import time
import traceback
import yaml
try:
import socketio.exceptions
except ImportError:
pass
from uptime_kuma_api import UptimeKumaApi, MonitorType
try:
with open('/tmp/ansible_datum_gateway_config.yml', 'r') as f:
config = yaml.safe_load(f)
url = config['uptime_kuma_url']
username = config['username']
password = config['password']
monitor_name = config['monitor_name']
api = UptimeKumaApi(url, timeout=30)
api.login(username, password)
monitors = api.get_monitors()
# Find or create "services" group
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
if not group:
try:
api.add_monitor(type='group', name='services')
except Exception:
time.sleep(2)
monitors = api.get_monitors()
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
# Get ntfy notification ID
notifications = api.get_notifications()
ntfy_notification_id = None
for notif in notifications:
if notif.get('type') == 'ntfy':
ntfy_notification_id = notif.get('id')
break
# Check if monitor already exists
existing = next((m for m in monitors if m.get('name') == monitor_name), None)
push_url = None
if existing:
print(f"Monitor '{monitor_name}' already exists (ID: {existing['id']})")
push_token = existing.get('pushToken') or existing.get('push_token')
if push_token:
push_url = f"{url}/api/push/{push_token}"
else:
print(f"Creating push monitor '{monitor_name}'...")
try:
api.add_monitor(
type=MonitorType.PUSH,
name=monitor_name,
parent=group['id'],
interval=90,
maxretries=3,
retryInterval=60,
notificationIDList={ntfy_notification_id: True} if ntfy_notification_id else {}
)
except Exception as e:
# socketio timeout: add_monitor may have succeeded server-side
print(f"add_monitor raised (possibly timeout): {e}", file=sys.stderr)
time.sleep(2)
monitors = api.get_monitors()
new_monitor = next((m for m in monitors if m.get('name') == monitor_name), None)
if new_monitor:
push_token = new_monitor.get('pushToken') or new_monitor.get('push_token')
if push_token:
push_url = f"{url}/api/push/{push_token}"
api.disconnect()
if push_url:
print(f"PUSH_URL={push_url}")
with open('/tmp/datum_gateway_push_url.txt', 'w') as f:
f.write(push_url)
print("SUCCESS")
except Exception as e:
print(f"ERROR: {str(e)}", file=sys.stderr)
traceback.print_exc(file=sys.stderr)
sys.exit(1)
mode: "0755"
- name: Create temporary config for push monitor setup
delegate_to: localhost
become: no
copy:
dest: /tmp/ansible_datum_gateway_config.yml
content: |
uptime_kuma_url: "{{ uptime_kuma_api_url }}"
username: "{{ uptime_kuma_username }}"
password: "{{ uptime_kuma_password }}"
monitor_name: "DATUM Gateway"
mode: "0644"
- name: Run Uptime Kuma push monitor setup
command: python3 /tmp/setup_datum_gateway_monitor.py
delegate_to: localhost
become: no
register: monitor_setup
changed_when: "'SUCCESS' in monitor_setup.stdout"
ignore_errors: yes
- name: Display monitor setup output
debug:
msg: "{{ monitor_setup.stdout_lines }}"
when: monitor_setup.stdout is defined
- name: Read push URL from file
slurp:
src: /tmp/datum_gateway_push_url.txt
delegate_to: localhost
become: no
register: push_url_file
ignore_errors: yes
- name: Parse push URL
set_fact:
datum_push_url: "{{ push_url_file.content | b64decode | trim }}"
when: push_url_file.content is defined
- name: Update health check service with push URL
lineinfile:
path: /etc/systemd/system/datum-gateway-healthcheck.service
regexp: "^Environment=UPTIME_KUMA_PUSH_URL="
line: "Environment=UPTIME_KUMA_PUSH_URL={{ datum_push_url }}"
when: datum_push_url is defined
notify: Restart datum-gateway health check timer
- name: Clean up temporary files
delegate_to: localhost
become: no
file:
path: "{{ item }}"
state: absent
loop:
- /tmp/setup_datum_gateway_monitor.py
- /tmp/ansible_datum_gateway_config.yml
- /tmp/datum_gateway_push_url.txt
handlers:
- name: Restart datum-gateway
systemd:
name: datum-gateway
state: restarted
daemon_reload: yes
- name: Restart datum-gateway health check timer
systemd:
name: datum-gateway-healthcheck.timer
state: restarted
daemon_reload: yes
# ===========================================
# Caddy Reverse Proxy for DATUM Dashboard (on vipy)
# ===========================================
- name: Configure Caddy reverse proxy for DATUM Gateway dashboard on vipy
hosts: vipy
become: yes
vars_files:
- ../../infra_vars.yml
- ../../services_config.yml
- ../../infra_secrets.yml
- ./datum_gateway_vars.yml
vars:
datum_gateway_subdomain: "{{ subdomains.datum_gateway }}"
datum_gateway_domain: "{{ datum_gateway_subdomain }}.{{ root_domain }}"
caddy_sites_dir: "{{ caddy_sites_dir }}"
uptime_kuma_api_url: "https://{{ subdomains.uptime_kuma }}.{{ root_domain }}"
tasks:
- name: Ensure Caddy sites-enabled directory exists
file:
path: "{{ caddy_sites_dir }}"
state: directory
owner: root
group: root
mode: "0755"
- name: Ensure Caddyfile includes import directive for sites-enabled
lineinfile:
path: /etc/caddy/Caddyfile
line: "import sites-enabled/*"
insertafter: EOF
state: present
backup: yes
create: yes
mode: "0644"
- name: Create Caddy reverse proxy config for DATUM Gateway dashboard
copy:
dest: "{{ caddy_sites_dir }}/datum-gateway.conf"
content: |
{{ datum_gateway_domain }} {
basic_auth {
{{ datum_dashboard_username }} {{ datum_dashboard_password_hash }}
}
reverse_proxy knots-box:{{ datum_gateway_api_port }} {
# Resolve via Tailscale MagicDNS
transport http {
resolvers 100.100.100.100
}
}
}
owner: root
group: root
mode: "0644"
- name: Validate Caddy config
command: caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
register: caddy_validate
changed_when: false
- name: Display Caddy validation output
debug:
msg: "{{ caddy_validate.stdout_lines + caddy_validate.stderr_lines }}"
- name: Reload Caddy
command: systemctl reload caddy
register: caddy_reload
- name: Display Caddy reload output
debug:
msg: "{{ caddy_reload.stdout_lines + caddy_reload.stderr_lines }}"
- name: Display DATUM Gateway dashboard URL
debug:
msg: "DATUM Gateway dashboard: https://{{ datum_gateway_domain }}"
# ===========================================
# Uptime Kuma HTTP Monitor for Public Dashboard
# ===========================================
- name: Create Uptime Kuma HTTP monitor setup script for DATUM dashboard
delegate_to: localhost
become: no
copy:
dest: /tmp/setup_datum_http_monitor.py
content: |
#!/usr/bin/env python3
import sys
import time
import traceback
import yaml
from uptime_kuma_api import UptimeKumaApi, MonitorType
try:
with open('/tmp/ansible_datum_http_config.yml', 'r') as f:
config = yaml.safe_load(f)
url = config['uptime_kuma_url']
username = config['username']
password = config['password']
monitor_url = config['monitor_url']
monitor_name = config['monitor_name']
api = UptimeKumaApi(url, timeout=30)
api.login(username, password)
monitors = api.get_monitors()
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
if not group:
try:
api.add_monitor(type='group', name='services')
except Exception:
time.sleep(2)
monitors = api.get_monitors()
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
notifications = api.get_notifications()
ntfy_notification_id = None
for notif in notifications:
if notif.get('type') == 'ntfy':
ntfy_notification_id = notif.get('id')
break
existing = next((m for m in monitors if m.get('name') == monitor_name), None)
if existing:
print(f"Monitor '{monitor_name}' already exists (ID: {existing['id']})")
else:
print(f"Creating HTTP monitor '{monitor_name}'...")
try:
api.add_monitor(
type=MonitorType.HTTP,
name=monitor_name,
url=monitor_url,
parent=group['id'],
interval=60,
maxretries=3,
retryInterval=60,
notificationIDList={ntfy_notification_id: True} if ntfy_notification_id else {}
)
except Exception as e:
print(f"add_monitor raised (possibly timeout): {e}", file=sys.stderr)
time.sleep(2)
api.disconnect()
print("SUCCESS")
except Exception as e:
print(f"ERROR: {str(e)}", file=sys.stderr)
traceback.print_exc(file=sys.stderr)
sys.exit(1)
mode: "0755"
- name: Create temporary config for HTTP monitor
delegate_to: localhost
become: no
copy:
dest: /tmp/ansible_datum_http_config.yml
content: |
uptime_kuma_url: "{{ uptime_kuma_api_url }}"
username: "{{ uptime_kuma_username }}"
password: "{{ uptime_kuma_password }}"
monitor_url: "https://{{ datum_gateway_domain }}"
monitor_name: "DATUM Gateway Dashboard"
mode: "0644"
- name: Run Uptime Kuma HTTP monitor setup
command: python3 /tmp/setup_datum_http_monitor.py
delegate_to: localhost
become: no
register: http_monitor_setup
changed_when: "'SUCCESS' in http_monitor_setup.stdout"
ignore_errors: yes
- name: Display HTTP monitor setup output
debug:
msg: "{{ http_monitor_setup.stdout_lines }}"
when: http_monitor_setup.stdout is defined
- name: Clean up HTTP monitor temporary files
delegate_to: localhost
become: no
file:
path: "{{ item }}"
state: absent
loop:
- /tmp/setup_datum_http_monitor.py
- /tmp/ansible_datum_http_config.yml
# ===========================================
# Stratum Port Forwarding on vipy via systemd-socket-proxyd
# Miners connect to vipy:23334; traffic is forwarded to knots-box:23334
# over the Tailscale network, matching the Bitcoin P2P proxy pattern.
# ===========================================
- name: Setup public Stratum port forwarding on vipy via systemd-socket-proxyd
hosts: vipy
become: yes
vars_files:
- ../../infra_vars.yml
- ../../services_config.yml
- ../../infra_secrets.yml
- ./datum_gateway_vars.yml
vars:
datum_tailscale_hostname: "knots-box"
uptime_kuma_api_url: "https://{{ subdomains.uptime_kuma }}.{{ root_domain }}"
tasks:
- name: Create Stratum proxy socket unit
copy:
dest: /etc/systemd/system/datum-stratum-proxy.socket
content: |
[Unit]
Description=DATUM Stratum Proxy Socket
[Socket]
ListenStream={{ datum_gateway_stratum_port }}
[Install]
WantedBy=sockets.target
owner: root
group: root
mode: "0644"
notify: Restart datum-stratum-proxy socket
- name: Create Stratum proxy service unit
copy:
dest: /etc/systemd/system/datum-stratum-proxy.service
content: |
[Unit]
Description=DATUM Stratum Proxy to {{ datum_tailscale_hostname }}
Requires=datum-stratum-proxy.socket
After=network.target
[Service]
Type=notify
ExecStart=/lib/systemd/systemd-socket-proxyd {{ datum_tailscale_hostname }}:{{ datum_gateway_stratum_port }}
owner: root
group: root
mode: "0644"
- name: Reload systemd daemon
systemd:
daemon_reload: yes
- name: Enable and start Stratum proxy socket
systemd:
name: datum-stratum-proxy.socket
enabled: yes
state: started
- name: Allow Stratum port through UFW
ufw:
rule: allow
port: "{{ datum_gateway_stratum_port | string }}"
proto: tcp
comment: "DATUM Gateway Stratum public access"
- name: Verify connectivity to knots-box Stratum via Tailscale
wait_for:
host: "{{ datum_tailscale_hostname }}"
port: "{{ datum_gateway_stratum_port }}"
timeout: 10
ignore_errors: yes
- name: Display public Stratum endpoint
debug:
msg: "DATUM Stratum public endpoint: {{ ansible_host }}:{{ datum_gateway_stratum_port }}"
# ===========================================
# Uptime Kuma TCP Monitor for Public Stratum
# ===========================================
- name: Create Uptime Kuma TCP monitor setup script for Stratum
delegate_to: localhost
become: no
copy:
dest: /tmp/setup_datum_stratum_tcp_monitor.py
content: |
#!/usr/bin/env python3
import sys
import time
import traceback
import yaml
from uptime_kuma_api import UptimeKumaApi, MonitorType
try:
with open('/tmp/ansible_datum_stratum_config.yml', 'r') as f:
config = yaml.safe_load(f)
url = config['uptime_kuma_url']
username = config['username']
password = config['password']
monitor_host = config['monitor_host']
monitor_port = config['monitor_port']
monitor_name = config['monitor_name']
api = UptimeKumaApi(url, timeout=30)
api.login(username, password)
monitors = api.get_monitors()
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
if not group:
try:
api.add_monitor(type='group', name='services')
except Exception:
time.sleep(2)
monitors = api.get_monitors()
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
notifications = api.get_notifications()
ntfy_notification_id = None
for notif in notifications:
if notif.get('type') == 'ntfy':
ntfy_notification_id = notif.get('id')
break
existing = next((m for m in monitors if m.get('name') == monitor_name), None)
if existing:
print(f"Monitor '{monitor_name}' already exists (ID: {existing['id']})")
else:
print(f"Creating TCP monitor '{monitor_name}'...")
try:
api.add_monitor(
type=MonitorType.PORT,
name=monitor_name,
hostname=monitor_host,
port=monitor_port,
parent=group['id'],
interval=60,
maxretries=3,
retryInterval=60,
notificationIDList={ntfy_notification_id: True} if ntfy_notification_id else {}
)
except Exception as e:
print(f"add_monitor raised (possibly timeout): {e}", file=sys.stderr)
time.sleep(2)
api.disconnect()
print("SUCCESS")
except Exception as e:
print(f"ERROR: {str(e)}", file=sys.stderr)
traceback.print_exc(file=sys.stderr)
sys.exit(1)
mode: "0755"
- name: Create temporary config for Stratum TCP monitor
delegate_to: localhost
become: no
copy:
dest: /tmp/ansible_datum_stratum_config.yml
content: |
uptime_kuma_url: "{{ uptime_kuma_api_url }}"
username: "{{ uptime_kuma_username }}"
password: "{{ uptime_kuma_password }}"
monitor_host: "{{ ansible_host }}"
monitor_port: {{ datum_gateway_stratum_port }}
monitor_name: "DATUM Stratum (public)"
mode: "0644"
- name: Run Uptime Kuma TCP monitor setup
command: python3 /tmp/setup_datum_stratum_tcp_monitor.py
delegate_to: localhost
become: no
register: tcp_monitor_setup
changed_when: "'SUCCESS' in tcp_monitor_setup.stdout"
ignore_errors: yes
- name: Display TCP monitor setup output
debug:
msg: "{{ tcp_monitor_setup.stdout_lines }}"
when: tcp_monitor_setup.stdout is defined
- name: Clean up Stratum TCP monitor temporary files
delegate_to: localhost
become: no
file:
path: "{{ item }}"
state: absent
loop:
- /tmp/setup_datum_stratum_tcp_monitor.py
- /tmp/ansible_datum_stratum_config.yml
handlers:
- name: Restart datum-stratum-proxy socket
systemd:
name: datum-stratum-proxy.socket
state: restarted

View file

@ -25,6 +25,9 @@ subdomains:
# Mempool Block Explorer (on mempool_box, proxied via vipy) # Mempool Block Explorer (on mempool_box, proxied via vipy)
mempool: mempool mempool: mempool
# DATUM Gateway dashboard (on knots_box, proxied via vipy)
datum_gateway: datum
# Caddy configuration # Caddy configuration
caddy_sites_dir: /etc/caddy/sites-enabled caddy_sites_dir: /etc/caddy/sites-enabled