Edit YAML
version: 3
resources:
- kind: AccessPoint
model: Mercusys ME60X
speed: 0.1
name: me60x-pool
notes: |-
## ME60X — Pool Range Extender
**Model:** Mercusys ME60X
**IP:** 192.168.86.13
**Hostname:** me60x-pool
**Role:** Wi-Fi Range Extender (Layer-2 bridge only)
### Wireless Configuration
**Extended 2.4 GHz**
- MAC: 30:16:9D:80:6E:66
- Channel: 11
- SSID: o==[]:::::::::::::::>
**Extended 5 GHz**
- MAC: 30:16:9D:80:6E:65
- Channel: 36
- SSID: o==[]:::::::::::::::>
### Architectural Notes
- Pure Layer-2 extension of existing LAN
- No routing, NAT, DHCP, or firewalling
- All Layer-3 authority remains with ISP Gateway
- Operates within flat subnet 192.168.86.0/24
- kind: AccessPoint
model: UniFi U7 Pro
name: u7pro-garage
labels:
mac: 74:f9:2c:40:fd:84
ip: 192.168.86.208
notes: |-
## U7 Pro — Garage
**Model:** UniFi U7 Pro
**MAC:** 74:f9:2c:40:fd:84
**IP:** 192.168.86.208
**Location:** Garage
Replaces decommissioned Google WiFi puck (wifi-puck-garage) in the same role. Wired uplink to usw-lite-8-poe:1.
- kind: AccessPoint
model: UniFi U7 Pro
name: u7pro-juniorretreat
labels:
mac: 8c:30:66:6e:b9:94
ip: 192.168.86.126
notes: |-
## U7 Pro — Junior Retreat
**Model:** UniFi U7 Pro
**MAC:** 8c:30:66:6e:b9:94
**IP:** 192.168.86.126
**Location:** Junior Retreat (bedroom)
Replaces decommissioned Google WiFi puck (wifi-puck-juniorretreat) in the same role. Wired uplink to usw-lite-8-poe:2.
- kind: AccessPoint
model: UniFi U7 Pro
name: u7pro-masterretreat
labels:
mac: 74:f9:2c:40:28:20
ip: 192.168.86.151
notes: |-
## U7 Pro — Master Retreat
**Model:** UniFi U7 Pro
**MAC:** 74:f9:2c:40:28:20
**IP:** 192.168.86.151
**Location:** Master Retreat (bedroom)
Replaces decommissioned Google WiFi puck (wifi-puck-masterretreat) in the same role. Wired uplink to usw-lite-8-poe:3.
- kind: AccessPoint
model: Google WiFi
speed: 1
name: wifi-puck-garage
notes: |-
## Google Wi-Fi Puck — Garage
**Model:** Google WiFi (1st gen)
**Location:** Garage
**Role:** Access Point (bridge mode only)
**SSID:** o==[]:::::::::::::::> ("sword")
- Wired LAN backhaul to gs608v4 secondary switch
- Bridges wireless clients onto 192.168.86.0/24
- Performs no DHCP, DNS, NAT, routing, or firewalling
- Does not alter packet authority
- kind: AccessPoint
model: Google WiFi
speed: 1
name: wifi-puck-juniorretreat
notes: |2-
Google Wi-Fi puck (Garage location).
Operating strictly in access point mode.
SSID: o==[]:::::::::::::::> ("sword")
- Wired LAN backhaul to asus-core
- Bridges wireless clients onto 192.168.86.0/24
- Performs no DHCP, DNS, NAT, routing, or firewalling
- Does not alter packet authority
- kind: AccessPoint
model: Google WiFi
speed: 1
name: wifi-puck-masterretreat
notes: |-
## Google Wi-Fi Puck — Master Retreat
**Model:** Google WiFi (1st gen)
**Location:** Master Retreat (bedroom)
**Role:** Access Point (bridge mode only)
**SSID:** o==[]:::::::::::::::> ("sword")
- Wired LAN backhaul to gs608v4 secondary switch
- Bridges wireless clients onto 192.168.86.0/24
- Performs no DHCP, DNS, NAT, routing, or firewalling
- Does not alter packet authority
- kind: Desktop
name: manhattan
tags:
- unconfirmed-kind
labels:
aliases: b3
notes: |-
Placeholder — added for port-map completeness (ls108g:7) during hardware/topology reconciliation, 2026-07-29.
Kind guessed as Desktop; not confirmed by user. Specs, OS, model unknown.
Formerly aliased "b3" in older wiring notes.
- kind: Desktop
name: moron
tags:
- unconfirmed-kind
labels:
aliases: b1
notes: |-
Placeholder — added for port-map completeness (ls108g:5) during hardware/topology reconciliation, 2026-07-29.
Kind guessed as Desktop; not confirmed by user. Specs, OS, model unknown.
Formerly aliased "b1" in older wiring notes.
- kind: Desktop
name: tau
tags:
- unconfirmed-kind
labels:
aliases: b2
notes: |-
Placeholder — added for port-map completeness (ls108g:3) during hardware/topology reconciliation, 2026-07-29.
Kind guessed as Desktop; not confirmed by user. Specs, OS, model unknown.
Formerly aliased "b2" in older wiring notes.
- kind: Laptop
ram:
size: 6
mts: 0
cpus:
- model: AMD Ryzen 5 Microsoft Surface Edition
cores: 4
threads: 8
drives:
- type: nvme
size: 256
name: chromebullet
tags:
- x86_64
- windows
- tailscale-node
labels:
mac: f0:1d:bc:9d:aa:6e
notes: |-
## Hardware Overview
| Attribute | Value |
|------------|--------|
| Platform | Microsoft Surface Laptop 3 |
| Architecture | x86_64 |
| CPU | AMD Ryzen 5 Microsoft Surface Edition |
| Cores / Threads | 4C / 8T |
| RAM (usable) | 6 GB |
| RAM (physical installed) | 6.4 GB |
| Storage | 256 GB NVMe SSD |
| NVMe Model | HFM256GDGTNG-87A0A |
| GPU | Integrated Radeon Graphics |
| Primary Network | WiFi (DHCP assigned) |
| LAN IP | 192.168.86.55 |
---
## System Classification
- Endpoint workstation
- Non-server device
- User interactive compute node
- Windows-based system
nics:
- type: wifi
speed: 1.0
ports: 1
mac: f0:1d:bc:9d:aa:6e
- kind: Laptop
name: phone
tags:
- mobile
- android
- health-connect
- tasker
notes: Android phone (Samsung Galaxy Watch paired). Uses HTTP Shortcuts app to share images to ironclad via clipboard.unbiasedrhino.cyou. Tasker runs Health Post task every 5 min on home WiFi — reads steps, HR, distance, calories from Health Connect (Google Fit as primary source for granular timestamped records), filters by data origin, and POSTs typed records to claude-viewer /api/health/ingest on ironclad:5100. ClipCascade syncs clipboard to ironclad.
- kind: Router
model: UniFi UCG-Ultra
managed: true
ports:
- type: rj45
speed: 1
count: 5
name: ucg-ultra
tags:
- managed
- gateway
- unifi
- dhcp
labels:
mac: 58:d6:1f:3d:e2:67
ip: 192.168.86.1
unifi_device_name: Asparagus
notes: |-
## UCG-Ultra — Sole L3 Gateway
**Model:** UniFi Cloud Gateway Ultra
**MAC:** 58:d6:1f:3d:e2:67
**LAN IP:** 192.168.86.1
**UniFi device name:** Asparagus
Replaced the ISP router as sole Layer-3 authority and DHCP server on 2026-06-10 — see [[Home Network Architecture - Authoritative Reference]]. Added to RackPeek inventory 2026-07-29 (was live but unmodeled).
## Physical Port Mapping
| Port | Connected To | Role |
|------|-------------|------|
| 1 | tl-sg108e:8 | Uplink toward WAN side |
| 5 | usw-lite-8-poe:8 | Downlink toward LAN side |
- kind: Server
ram:
size: 4
mts: 0
ipmi: false
cpus:
- model: Cortex-A72
cores: 4
threads: 4
drives:
- type: sd
size: 16
ports:
- type: rj45
speed: 1
count: 1
name: aperture
tags:
- arm64
- tailscale-node
labels:
mac: dc:a6:32:23:ae:b5
notes: |-
### Hardware Notes — aperture
- **Platform:** Raspberry Pi 4 (Cortex-A72 @ 1.5 GHz)
- **Architecture:** ARM64 (aarch64)
- **CPU:** 4 cores / 4 threads
- **RAM:** 4 GB
- **Boot Device:** 16 GB microSD
- **GPU:** Integrated VideoCore (not modeled; no discrete GPU)
- **Network Interfaces:**
- `eth0` — 1 Gb onboard Ethernet (primary LAN uplink)
- `wlan0` — Present but not intentionally used (not modeled)
- **Capability Tags:**
- Participates in Tailscale overlay
- kind: Server
ram:
size: 4
mts: 0
ipmi: false
cpus:
- model: Cortex-A72
cores: 4
threads: 4
drives:
- type: sd
size: 32
- type: ssd
size: 233
- type: hdd
size: 2000
ports:
- type: rj45
speed: 1
count: 1
name: drogher
tags:
- arm64
- tailscale-node
labels:
mac: dc:a6:32:23:aa:23
notes: |-
### Hardware Notes — drogher
- **Platform:** Raspberry Pi 4 (Cortex-A72 @ 1.5 GHz)
- **Architecture:** ARM64 (aarch64)
- **CPU:** 4 cores / 4 threads
- **RAM:** 4 GB
- **Boot Device:** 32 GB microSD
- **Additional Storage:**
- 232 GB SSD (mounted at /srv/ssd)
- 2 TB USB HDD (mounted at /mnt/backup_mariadb)
- **GPU:** Integrated VideoCore (not modeled; no discrete GPU)
- **Network Interfaces:**
- `eth0` — 1 Gb onboard Ethernet (primary LAN uplink)
- `wlan0` — Present (not intentionally modeled)
- **Capability Tags:**
- Participates in Tailscale overlay
- kind: Server
ports:
- type: rj45
speed: 1
count: 1
name: fibarohc2
labels:
mac: 00:22:4d:cf:f9:c4
notes: 'TODO: Hardware audit required'
- kind: Server
ram:
size: 8
mts: 0
ipmi: false
cpus:
- model: Cortex-A76
cores: 4
threads: 4
drives:
- type: sd
size: 64
ports:
- type: rj45
speed: 2.5
count: 1
- type: rj45
speed: 0.1
count: 1
name: forge
tags:
- arm64
- docker-host
- tailscale-node
labels:
mac_eth0: 00:e0:4c:68:99:e9
mac_wlan0: d8:3a:dd:f2:15:c7
notes: |-
### Hardware Notes — forge
- **Platform:** Raspberry Pi 5 Model B Rev 1.0
- **Architecture:** ARM64 (aarch64)
- **CPU:** Cortex-A76, 4 cores @ 2.4 GHz
- **RAM:** 8 GB
- **Boot Device:** 64 GB microSD
- **GPU:** Integrated VideoCore (not modeled; no discrete GPU)
- **Network Interfaces:**
- `eth0` — 2.5 Gb capable; LAN uplink (tl-sg108e:5)
- `eth1` — 1 Gb USB Ethernet adapter; dedicated SPAN/mirror ingress from managed switch (tl-sg108e:6)
- `wlan0` — Present but unused (not modeled)
- **NIC Modeling Policy:**
- Modeled at hardware capability, not negotiated link speed
- eth0 capable of 2.5 Gb
- eth1 limited to 1 Gb
- kind: Server
ram:
size: 32
mts: 6000
ipmi: false
cpus:
- model: Intel Core i7-14700F
cores: 20
threads: 28
drives:
- type: nvme
size: 1000
- type: sata
size: 4000
gpus:
- model: Asus Dual RTX 5060 Ti OC 16GB
vram: 16
ports:
- type: rj45
speed: 2.5
count: 1
name: ironclad
tags:
- x86_64
- gpu
- dev-workstation
- tailscale-node
labels:
mac: 34:5a:60:c9:77:d6
notes: |-
### Hardware Notes — ironclad
- **Platform:** Custom build (Antec C5 ARGB Mid Tower)
- **Architecture:** x86_64
- **CPU:** Intel Core i7-14700F, 20 cores (8P+12E) / 28 threads
- **RAM:** 32 GB DDR5 6000MHz (2x16GB, XMP enabled)
- **Boot Device:** Kingston NV3 1TB NVMe M.2 Gen4 SSD
- **GPU:** Asus Dual RTX 5060 Ti OC 16GB GDDR7 (Blackwell)
- **Motherboard:** MSI MAG B760 Tomahawk WiFi (ATX, DDR5, LGA1700)
- **PSU:** Gigabyte 750W UD750GM PG5 V2 ICE (80+ Gold, Fully Modular)
- **CPU Cooler:** Thermalright Peerless Assassin 120 SE (dual tower)
- **Thermal Paste:** Thermal Grizzly Kryonaut
- **Network Interfaces:**
- Intel I226-V 2.5 Gb Ethernet (onboard)
- Intel Wi-Fi 6E (onboard, unused)
- **BIOS Configuration:**
- XMP Profile 1 enabled (DDR5 6000MHz)
- Wake-on-LAN: Resume by PCI-E Device enabled
- Restore After AC Power Loss: Power On
- Intel Virtualization Technology: Enabled
- **Role:** Primary development workstation, SSH-first. Runs Immich photo management (migrated from surfaceblack 2026-03-07), offloads heavy Claude Code sessions, and local inference workloads from chromebullet.
### Storage — seastore
- **Device:** /dev/sda1 (SATA 6.0 Gb/s)
- **Model:** Seagate ST4000DM000-1F2168 (Desktop HDD.15)
- **Serial:** Z301NLGY
- **Firmware:** CC54
- **Capacity:** 4TB (3.6T usable, 1% reserved)
- **Filesystem:** ext4, label "seastore"
- **UUID:** 69107d4c-9e48-4d01-b642-3be53c858b42
- **Mount:** /mnt/seastore (noatime, nofail)
- **Role:** Media and secondary backup storage (3-2-1 methodology). All data is replaceable.
- **SMART monitoring:** smartd — daily short test 2am, long test Saturdays 3am, temp alerts at 45/55°C
- **Commissioned:** 2026-04-03. Drive has ~43,871 power-on hours (~5 years). Past warranty but SMART clean.
drives_info:
- size: 4000.0
type: sata
model: Seagate ST4000DM000-1F2168
serial: Z301NLGY
label: seastore
mount: /mnt/seastore
filesystem: ext4
role: media and secondary backup storage (3-2-1)
- kind: Server
ports:
- type: rj45
speed: 1
count: 1
name: master_ac
labels:
mac: 10:20:ba:2f:4f:e8
notes: 'TODO: Hardware audit required'
- kind: Server
ports:
- type: rj45
speed: 1
count: 1
name: nastronaut
labels:
mac: 78:72:64:44:16:27
model: Asustor AS3302Tv2 (Drivestor 2 Pro Gen2)
cpu: Realtek RTD1619B (ARM64 Cortex-A55, 4 cores)
ram: 2GB
os: ADM 5.1.2
serial: AS25063304EM02F2
storage: 3.7TB RAID (md1), 1.9TB free
notes: Asustor NAS. ARM64/RTD1619B — no hardware transcoding, no Docker. File storage only. SSH access via gagarin user, key-based auth. OpenSSH 9.8 with PerSourcePenalties (can auto-block IPs on failed connections).
- kind: Server
ram:
size: 1
mts: 0
ipmi: false
cpus:
- model: Cortex-A53
cores: 4
threads: 4
drives:
- type: sd
size: 8
ports:
- type: rj45
speed: 1
count: 1
- type: rj45
speed: 1
count: 1
name: pihole
labels:
mac_eth1_lan: 6c:6e:07:01:99:e9
notes: |-
### Hardware Notes — pihole
- Platform: Raspberry Pi 3 Model B Rev 1.2
- Boot Device: microSD (8 GB)
- Architecture: ARM (32-bit)
### Network Interfaces
- eth1 — LAN-facing interface, connected to ls108g:2
### Network Role — DNS/Ad-block Only (superseded 2026-06-10)
UCG-Ultra (ucg-ultra) is now the sole Layer-3 gateway, NAT, firewall, and DHCP
authority for the LAN (cut over 2026-06-10). pihole no longer performs routing —
it is a downstream DNS resolver only (Pi-hole + Unbound), reached via ls108g → tl-sg108e → ucg-ultra.
### Topology Context
Internet
↓
NBN NTD (L1/L2)
↓
tl-sg108e (L2 only)
↓
ucg-ultra (L3 boundary — gateway/NAT/DHCP)
↓
usw-lite-8-poe / gs608v4 / ls108g
↓
pihole (DNS only, no routing)
- kind: Server
ram:
size: 8
mts: 0
ipmi: false
cpus:
- model: AMD Ryzen 5 Microsoft Surface Edition
cores: 4
threads: 8
drives:
- type: nvme
size: 256
ports:
- type: rj45
speed: 1
count: 1
name: surfaceblack
tags:
- x86_64
- docker-host
- tailscale-node
labels:
mac: c8:a3:62:d0:60:38
notes: |-
### Hardware Notes — surfaceblack
- **Platform:** Microsoft Surface Laptop 3 (Model 1873)
- **Architecture:** x86_64
- **CPU:** AMD Ryzen 5 Microsoft Surface Edition, 4 cores / 8 threads
- **RAM:** 8 GB
- **Storage:** 256 GB NVMe (HFM256GDGTNG-87A0A)
- **Battery:** Li-ion, 45.8 Wh design capacity, ~39.3 Wh actual (85.7% health), 79 cycles as of 2026-03-07
- **Kernel:** linux-surface 6.14.2-surface-1 (surface_battery, surface_charger, surface_aggregator modules)
- **Battery Control:** No software charge thresholds available. UEFI battery limit (50% fixed) exists but is read-only at runtime. SAM probing is dangerous — avoid unknown CIDs.
- **Always-on:** Runs 24/7 plugged in as a server. Smart plug recommended for charge cycling.
- **GPU:** Integrated Radeon graphics (not modeled; no discrete GPU)
- **Network Interfaces:**
- USB Ethernet adapter (primary LAN uplink; bridged via br0)
- WiFi present but not used (not modeled)
- **Modeling Policy:**
- Physical capability modeled, not negotiated link state
- Integrated components not represented as discrete devices
- kind: Server
ram:
size: 1
mts: 0
ipmi: false
cpus:
- model: AMD EPYC 7551
cores: 2
threads: 2
drives:
- type: ssd
size: 47
ports:
- type: rj45
speed: 1
count: 1
name: vps
tags:
- x86_64
- tailscale-node
- cloud
notes: |-
### Hardware Notes — vps
- **Platform:** Oracle Cloud Always Free VM (VM.Standard.E2.1.Micro)
- **Architecture:** x86_64
- **CPU:** AMD EPYC 7551, 2 vCPUs
- **RAM:** 1 GB
- **Storage:** 47 GB block volume (SSD)
- **Hostname:** rabbithole.unbiasedrhino.cyou
- **Provider:** Oracle Cloud Infrastructure (Sydney region)
- **Tier:** Always Free
- **Network:**
- Public IP (static)
- Tailscale node (headscale self-hosted)
- SSH alias: vps
- **Key Services:**
- Headscale (Tailscale coordination server)
- Caddy (reverse proxy, TLS termination)
- kind: Service
name: agora-api-prod
tags:
- service
- daemon
- api
- kleos
- prod
labels:
lan_alias: https://agora-api-prod.internal
port: 8026
notes: |
Prod instance of Agora multi-retailer grocery catalog API (Kleos sub-project).
Service: agora_api_prod.service — deployed to ~/prod/kleos/projects/agora/api/ with venv at ~/.virtualenvs/agora-prod/.
LAN alias: agora-api-prod.internal.
Deployed via vivo-release from ironclad ~/working/kleos/.
runsOn:
- forge-os
- kind: Service
name: agora-api-trunk
tags:
- service
- daemon
- api
- kleos
- trunk
labels:
port: 8016
lan_alias: https://agora-trunk.internal
source_location: /home/gandalf/working/kleos/projects/agora (ironclad)
deploy_target: /home/gandalf/trunk/kleos/projects/agora (forge)
venv: /home/gandalf/.virtualenvs/agora (editable install)
systemd_unit: agora-api-trunk.service
path_watcher: agora-api-trunk-watcher.path
path_watcher_target: agora-api-trunk-watcher.service
notes: |
Trunk instance of Agora multi-retailer grocery catalog + curation engine (Kleos sub-project).
Service: agora-api-trunk.service — runs uvicorn via /usr/services/agora-api-trunk/start.sh with venv at ~/.virtualenvs/agora/ (editable install of ~/trunk/kleos/projects/agora/api).
Listens on 0.0.0.0:8016. LAN alias: agora-trunk.internal (caddy-lan on ironclad proxies /api/agora/* → forge:8016).
Deployed via vivo from ironclad ~/working/kleos/projects/agora/.
Auto-restart on file change: agora-api-trunk-watcher.path triggers agora-api-trunk-watcher.service (oneshot systemctl restart). Watched roots: all subdirs under api/src/agora/ (adapters, categories, curation, lifecycle, products, rules, scheduling, search, tags), plus api/config/settings.json, start.sh, and ~/trunk/secrets.json. NOTE: PathModified on a parent dir does not bubble to its subdirs — each module subdir must be listed explicitly. Path unit was missing rules/categories/scheduling/search until 2026-04-16; those four were added that day.
runsOn:
- forge-os
- kind: Service
name: angelos-api-prod
tags:
- service
- daemon
- api
- kleos
- prod
labels:
lan_alias: https://angelos-api-prod.internal
port: 8012
notes: |
Prod instance of Angelos content aggregator API (Kleos sub-project).
Service: angelos_api_prod.service — deployed to ~/prod/kleos/projects/angelos/backend/ with venv at ~/.virtualenvs/angelos-prod/.
LAN alias: angelos-api-prod.internal.
Deployed via vivo-release from ironclad ~/working/kleos/.
runsOn:
- forge-os
- kind: Service
name: angelos-api-trunk
tags:
- service
- daemon
- api
- kleos
- trunk
labels:
lan_alias: http://angelos-api-trunk.internal
port: 8002
notes: |
Trunk instance of Angelos content aggregator API (Kleos sub-project).
Service: angelos_api_trunk.service — deployed to ~/trunk/kleos/projects/angelos/backend/ with venv at ~/.virtualenvs/angelos/.
LAN alias: angelos-api-trunk.internal.
Deployed via vivo from ironclad ~/working/kleos/projects/angelos/.
runsOn:
- forge-os
- kind: Service
name: aperture-bugatti-camera-driveway
tags:
- service
- daemon
- execution-standard
notes: 'Bugatti camera acquire — driveway (nestcam_driveway). Template unit bugatti_camera@driveway. Run-as: gandalf. Pre-existing: DB auth failure, crash-looping.'
runsOn:
- aperture-os
- kind: Service
name: aperture-bugatti-camera-frontdoor
tags:
- service
- daemon
- execution-standard
notes: 'Bugatti camera acquire — frontdoor (nestcam_frontdoor). Template unit bugatti_camera@frontdoor. Run-as: gandalf. Pre-existing: DB auth failure, crash-looping.'
runsOn:
- aperture-os
- kind: Service
name: aperture-bugatti-camera-lounge
tags:
- service
- daemon
- execution-standard
notes: 'Bugatti camera acquire — lounge (googlehome_lounge). Template unit bugatti_camera@lounge. Run-as: gandalf. Watcher .path restarts all instances on code change. Full trio. Pre-existing: DB auth failure (archer@aperture.lan → drogher MariaDB).'
runsOn:
- aperture-os
- kind: Service
name: aperture-bugatti-camera-pool
tags:
- service
- daemon
- execution-standard
notes: 'Bugatti camera acquire — poolpatio (nestcam_poolpatio). Template unit bugatti_camera@poolpatio. Run-as: gandalf. Pre-existing: DB auth failure.'
runsOn:
- aperture-os
- kind: Service
name: aperture-glances-mqtt
tags:
- service
- daemon
- execution-standard
notes: 'Glances MQTT exporter. Always-on. Run-as: root. Venv: glances_mqtt. Hardening: NoNewPrivileges, PrivateTmp. Watcher .path unit active. Full trio.'
runsOn:
- aperture-os
- kind: Service
name: aperture-kodiak-ecmcatalog
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'Kodiak ECM catalog — catalogs new files every minute. Timer: every minute. Job: kodiak_ecmcatalog. Python venv: kodiak. Full trio in /usr/services/kodiak_ecmcatalog/.'
runsOn:
- aperture-os
- kind: Service
name: aperture-kodiak-ecmsourceaudit
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'Kodiak ECM source audit — scans endpoints for new/changed files. Timer: every 3 hours. Job: kodiak_ecmsourceaudit. Was P1 — ran via legacy cron_wrapper.sh in root crontab, CLI path pointed to non-existent /home/gandalf/projects/. Fixed to /home/gandalf/prod/kodiak/cli.py. Full trio.'
runsOn:
- aperture-os
- kind: Service
network:
ip: 192.168.86.43
name: aperture-lan
runsOn:
- aperture-os
- kind: Service
name: aperture-sweep-prod
tags:
- service
- systemd-timer
- sweep
- execution-standard
notes: Venv hygiene checks. Runs every 6 hours via sweep_prod.timer. Code at ~/prod/sweep/. Results persisted to Drogher MariaDB.
runsOn:
- aperture-os
- kind: Service
name: aperture-vaultsink-backup
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'VaultSink backup. Migrated from cron to systemd timer. Timer: daily at 03:00. Job: vaultsink_backup. Full trio in /usr/services/vaultsink_backup/.'
runsOn:
- aperture-os
- kind: Service
name: apestaart
tags:
- service
- daemon
- api
- gmail
labels:
lan_alias: http://apestaart-prod.internal
cloudflare: https://apestaart.unbiasedrhino.cyou
port: 8000
dependsOn: forge-os,vps-caddy
notes: |
Gmail automation service — FastAPI app managing labels, filters, and rule-based email processing across 4 accounts (personal, anabasis, caremappr, funlhn).
Port 8000. Deployed to ~/prod/apestaart/ with venv at ~/.virtualenvs/apestaart/.
Systemd service: apestaart_api_prod.service (Type=simple, Restart=always) via /usr/services/service_exec.sh wrapper.
File watcher: apestaart_api_prod-watcher.path → apestaart_api_prod-watcher.service (oneshot restart).
All API routes under /api/ prefix (e.g. /api/events, /api/accounts/{acct}/messages). Webhooks at /webhooks/*, admin at /admin, health at /health. React SPA served inline from frontend/dist/ static mount as the catch-all route.
Real-time Pub/Sub push notifications via webhook at https://apestaart.unbiasedrhino.cyou/webhooks/gmail.
OIDC auth on webhook endpoints; Atoll JWT auth on API routes.
Deployed via vivo from ironclad ~/working/apestaart/.
GitHub: bankersheldrake/apestaart
runsOn:
- forge-os
- kind: Service
name: apestaart-fullcalsync
tags:
- service
- systemd-timer
- scheduled
- playwright
- calendar
notes: |
O365 full calendar sync (daily 03:00) — scrapes OWA calendar print preview via headless Playwright, syncs events to Google Calendar via apestaart API. Current + 6 months ahead. Matches attendance by email to set free/busy transparency.
Systemd timer: apestaart_fullcalsync.timer (OnCalendar=*-*-* 03:00:00) on ironclad.
Entry point: apestaart-o365cal --full (venv: apestaart).
Shares browser state with apestaart_funlhnfwd and apestaart_funlhncal.
Auth guards: quiet hours (8pm-7am Adelaide), shared auth lock file, ntfy unlock flow.
runsOn:
- ironclad-os
- kind: Service
name: apestaart-funlhncal
tags:
- service
- systemd-timer
- scheduled
- playwright
- calendar
notes: |
O365 calendar sync (hourly) — scrapes OWA calendar print preview via headless Playwright, syncs events to Google Calendar via apestaart API. Current + next month. Matches attendance by email to set free/busy transparency.
Systemd timer: apestaart_funlhncal.timer (OnUnitActiveSec=3600s) on ironclad.
Entry point: apestaart-o365cal (venv: apestaart).
Shares browser state with apestaart_funlhnfwd and apestaart_fullcalsync.
Auth guards: quiet hours (8pm-7am Adelaide), shared auth lock file, ntfy unlock flow.
runsOn:
- ironclad-os
- kind: Service
name: apestaart-funlhnfwd
tags:
- service
- systemd-timer
- scheduled
- playwright
- email
notes: |
O365 email forwarder (hourly) — logs into OWA via headless Playwright, forwards emails with 'to-forward' category to rohan.ward.work@gmail.com, applies 'synced' category. Category-as-state design, no local persistence.
Systemd timer: apestaart_funlhnfwd.timer (OnUnitActiveSec=3600s) on ironclad.
Entry point: apestaart-o365fwd (venv: apestaart).
Shares browser state with apestaart_funlhncal and apestaart_fullcalsync.
Auth guards: quiet hours (8pm-7am Adelaide), shared auth lock file, ntfy unlock flow.
runsOn:
- ironclad-os
- kind: Service
name: apestaart-trunk
tags:
- service
- daemon
- api
- gmail
- trunk
labels:
lan_alias: http://apestaart-trunk.internal
lan_alias_api: https://apestaart-api-trunk.internal
api_port: 8001
ui_port: 5182
notes: |
Trunk (dev) instance of apestaart Gmail automation service — same 4-account setup as prod (personal, anabasis, caremappr, funlhn).
API: apestaart_api_trunk.service — deployed to ~/trunk/apestaart/ with venv at ~/.virtualenvs/apestaart-trunk/, API on port 8001. All routes under /api/ prefix.
API LAN alias: apestaart-api-trunk.internal (HTTPS via caddy-lan).
UI: apestaart_ui_trunk.service — Vite dev server on port 5182 with hot module reload, so source edits under ~/trunk/apestaart/frontend/ reflect without rebuilds.
UI LAN alias: apestaart-trunk.internal.
File watcher: apestaart_api_trunk-watcher.path → apestaart_api_trunk-watcher.service (oneshot restart) for backend changes.
Both services run via /usr/services/service_exec.sh wrapper.
Deployed via vivo from ironclad ~/working/apestaart/.
runsOn:
- forge-os
- kind: Service
name: apestaart-watch
tags:
- service
- systemd-timer
- scheduled
labels:
systemd_unit: apestaart_watch.timer
interval: 518400s
notes: |
Renews Gmail Pub/Sub watches for all apestaart accounts (watches expire after 7 days).
Systemd: apestaart_watch.service (Type=oneshot) + apestaart_watch.timer (OnUnitActiveSec=518400s ≈ 6 days, Persistent=true).
Calls POST http://localhost:8000/webhooks/gmail/renew on the local apestaart_api_prod service.
Runs via /usr/services/service_exec.sh wrapper, target /usr/services/apestaart_watch/start.sh.
runsOn:
- forge-os
- kind: Service
name: carapace
tags:
- carapace
- monorepo
- reference
labels:
source: ~/working/carapace/
canonical_store: ~/nextcloud/Technical/claude/sessions.db
wiki: '[[Carapace]]'
notes: |
Claude tooling monorepo — umbrella resource. Tracks 'what Claude is working on' across the session lifecycle.
Packages (pnpm workspace):
- packages/sessions/ — Python shared access layer (Store, models, schema)
- packages/sessions-mcp/ — MCP server → resource: ironclad-sessions-mcp
- packages/server/ — Flask backend (port 47120): /api/*, /tessera/summary, /api/harvest/* → resource: claude-viewer
- packages/viewer/ — React + Vite frontend (port 5174 dev) → served by claude-viewer
- packages/harvest/ — Transcript pipeline → resources: ironclad-harvest-detect-trunk, ironclad-harvest-worker-trunk
- packages/cli/ — carapace CLI (session-start resolver)
Component services (all runsOn ironclad-os, tagged 'carapace'):
- claude-viewer (Flask + viewer static)
- ironclad-sessions-mcp
- carapace_sessions_integrity (nightly SQLite check)
- ironclad-harvest-detect-trunk
- ironclad-harvest-worker-trunk
LAN aliases: https://carapace-{prod,trunk}.internal (UI), https://carapace-api-{prod,trunk}.internal (Flask backend).
Canonical store: ~/nextcloud/Technical/claude/sessions.db (SQLite, post-2026-04-11 cutover).
See [[Carapace]] on the wiki for full detail.
runsOn:
- ironclad-os
- kind: Service
network:
ip: 127.0.0.1
port: 47120
protocol: http
name: carapace-api-prod
tags:
- service
- systemd-service
- execution-standard
- carapace
- fastapi
- atoll
labels:
lan_alias: https://carapace-api-prod.internal
lan_alias_frontend: https://carapace-prod.internal
wiki: '[[Carapace]]'
monorepo: '[[Carapace]]'
atoll_client_id: carapace
atoll_audience: carapace (accepts kleos via cross_platform_audiences on /tessera/summary)
notes: |
Atoll-compliant FastAPI backend for Carapace (prod). Took over port 47120 from the retired claude_viewer Flask service on 2026-04-21 (Phase 6 cutover).
Entry point: /home/gandalf/.virtualenvs/carapace_api/bin/carapace-api (shared venv with trunk — same editable install).
Working dir: /home/gandalf/prod/carapace/packages/server (populated via rsync during cutover; vivo code-to-prod rule TBD pending release-manager registration).
Config: /home/gandalf/prod/carapace/packages/server/config/settings-prod-ironclad.json (synced via vivo 'carapace api trunk (source)' rule from working — yes, misnamed; covers both envs for config).
Systemd: carapace-api-prod.service + carapace-api-prod-watcher.{service,path} (watcher fires on config edits, not code — prod code eventually moves to release-manager).
Caddy: reverse_proxy 127.0.0.1:47120 via carapace-api-prod.internal (tls internal).
Secrets: carapace.atoll_service_token in ~/prod/secrets.json (aud=carapace, minted via /auth/admin/services/carapace/token on atoll-prod).
Same router surface as carapace-api-trunk. See [[Carapace]] wiki article for architecture.
runsOn:
- ironclad-os
- kind: Service
network:
ip: 127.0.0.1
port: 47121
protocol: http
name: carapace-api-trunk
tags:
- service
- systemd-service
- execution-standard
- carapace
- fastapi
- atoll
labels:
lan_alias: https://carapace-api-trunk.internal
lan_alias_frontend: https://carapace-trunk.internal
wiki: '[[Carapace]]'
monorepo: '[[Carapace]]'
atoll_client_id: carapace
atoll_audience: carapace (accepts kleos via cross_platform_audiences on /tessera/summary)
notes: |
Atoll-compliant FastAPI backend for Carapace (trunk). Replaces the retired claude_viewer Flask service as of the Phase 6 cutover 2026-04-21.
Entry point: /home/gandalf/.virtualenvs/carapace_api/bin/carapace-api (pip entry point from carapace/packages/server/pyproject.toml).
Working dir: /home/gandalf/trunk/carapace/packages/server (synced via vivo 'carapace api trunk (source)' rule).
Config: /home/gandalf/trunk/carapace/packages/server/config/settings-trunk-ironclad.json
Systemd: carapace-api-trunk.service + carapace-api-trunk-watcher.{service,path}. Path watcher restarts the service on edits to src/carapace_api/ or the trunk config.
Caddy: reverse_proxy 127.0.0.1:47121 via carapace-api-trunk.internal (tls internal).
Secrets: carapace.atoll_service_token in ~/trunk/secrets.json (aud=carapace).
Routers under src/carapace_api/api/: briefs, projects, ideas, stats, metrics, files, harvest, health, sync, tessera, system. All JWT-protected via AtollAuth except /health, /ping, and /api/health/ingest (static bearer — phone origin has no Atoll login).
Shute event types registered at startup: carapace.brief.updated, carapace.project.updated, carapace.idea.promoted, carapace.harvest.finished. Emits brief.updated on PUT /api/briefs/<id> and project.updated on PUT /api/projects/<slug>.
runsOn:
- ironclad-os
- kind: Service
name: carapace_sessions_integrity
tags:
- service
- systemd-timer
- carapace
- maintenance
labels:
monorepo: '[[Carapace]]'
notes: Runs sessions-integrity --fix against the carapace sessions SQLite store. Creates missing session entries for orphaned projects/briefs, fixes status/phase mismatches, deduplicates. Invoked via the carapace-sessions venv (~/.virtualenvs/carapace/).
runsOn:
- ironclad-os
- kind: Service
name: chromebullet-glances-mqtt
tags:
- service
- daemon
- mqtt
- scheduled-task
- not-yet-migrated
notes: "NOT YET MIGRATED 2026-08: chromebullet rebuilt from Windows to native Ubuntu 24.04; this Windows scheduled task ('Glances MQTT Export', wscript.exe launching C:\\_WorkingDir\\services\\launch_glances.vbs) no longer exists. Not reimplemented natively yet — revisit if glances MQTT export is still wanted on this host."
runsOn:
- chromebullet-os
- kind: Service
network:
ip: 192.168.86.55
name: chromebullet-lan
runsOn:
- chromebullet-os
- kind: Service
name: chromebullet-speedtest-mqtt
tags:
- service
- scheduled-task
- mqtt
- timer
- not-yet-migrated
notes: "NOT YET MIGRATED 2026-08: chromebullet rebuilt from Windows to native Ubuntu 24.04; this Windows scheduled task ('Speedtest MQTT Export', wscript.exe launching C:\\_WorkingDir\\services\\launch_speedtest.vbs, hourly) no longer exists. Not reimplemented natively yet — revisit if speedtest MQTT export is still wanted on this host."
runsOn:
- chromebullet-os
- kind: Service
name: chromebullet-wsl-ssh
tags:
- service
- scheduled-task
- ssh
- wsl
- deprecated
notes: "DEPRECATED 2026-08: Windows scheduled task, no longer applicable — chromebullet rebuilt to native Ubuntu (no Windows, no WSL). Native sshd runs directly on chromebullet-os now. Historical record only. Was: 'WSL SSH', ran 'wsl -u root service ssh start'."
runsOn:
- chromebullet-os
- kind: Service
name: chromebullet-wsl-startup
tags:
- service
- scheduled-task
- wsl
- deprecated
notes: "DEPRECATED 2026-08: Windows scheduled task, no longer applicable — chromebullet rebuilt to native Ubuntu (no Windows, no WSL). Historical record only. Was: 'Start WSL on Login', ran wsl.exe -d Ubuntu -u rohan with sleep infinity keepalive."
runsOn:
- chromebullet-os
- kind: Service
name: chromebullet-ydotoold
tags:
- service
- daemon
- systemd
- input-injection
notes: "systemd system service, root-run (needs /dev/uinput access). Unit: /etc/systemd/system/ydotoold.service, ExecStart=/usr/bin/ydotoold, ExecStartPost chmods /tmp/.ydotool_socket to 666 so user rohan's scripts can talk to it without sudo. Enabled + running. Used by GNOME custom-keybinding scripts (~/.local/bin/claude-tab.sh, claude-tab-files.sh) to inject keystrokes into ghostty for the Ctrl+Shift+6/7 Claude Code launcher shortcuts (SSH to ironclad, run claude-launcher.sh / claude-launcher-yazi.sh). Compositor-independent (kernel uinput), replaced an earlier wtype-based attempt that failed on GNOME/Mutter (no zwp_virtual_keyboard_manager_v1 support)."
runsOn:
- chromebullet-os
- kind: Service
name: cloudflare-dns-unbiasedrhino
tags:
- dns
- cloudflare
- domain
labels:
domain: unbiasedrhino.cyou
provider: cloudflare
record_count: 20
notes: |
Cloudflare DNS zone for unbiasedrhino.cyou.
All records are A records, DNS-only (no Cloudflare proxy), Auto TTL.
### Target IPs
- **192.9.185.36** — VPS public IP, proxied through vps-caddy to internal services via Tailscale
- **103.248.18.192** — Home network WAN IP (direct)
### A Records → 192.9.185.36 (VPS / Caddy)
| Subdomain | Service |
|-----------|---------|
| apestaart | Gmail API service (forge) |
| bitwarden | Bitwarden |
| clipboard | ClipRelay upload endpoint (ironclad) |
| dadsmusic | Dad's Music Library Audit viewer (ironclad-dadsmusic-serve, port 8766) |
| fibarorhc2 | Fibaro HC2 |
| grafana | Grafana |
| home | Home Assistant |
| kitchenowl | KitchenOwl |
| manyfold | Manyfold |
| nextcloud | Nextcloud |
| ntfy | ntfy push notifications |
| orbotnik | Orbotnik web app |
| paperless | Paperless-ngx |
| pihole | Pi-hole admin |
| rabbithole | VPS root / Headscale |
| rackpeek | RackPeek |
| sputnikshute | Sputnik (legacy name) |
| vikunja | Vikunja task management |
| wiki | TiddlyWiki |
### A Records → 103.248.18.192 (Home WAN direct)
| Subdomain | Notes |
|-----------|---------|
| bollardeggings | Direct WAN access to home network |
| unbiasedrhino.cyou | Domain root |
- kind: Service
name: cquota-poll-prod
tags:
- service
- systemd-timer
- scheduled
- claude
- usage-monitor
notes: |
Polls the Anthropic Claude usage endpoint every 5 minutes and writes the result to ~/.claude/.cquota_cache.json plus weekly trend samples at ~/.claude/.cquota_weekly_trend.json. Consumed by the Claude Code statusline (~/.claude/statusline.sh) and by `cquota --no-poll` interactive clients. Replaces the old pattern of running `cquota -w` in a tmux session for cache maintenance.
Systemd timer: cquota-poll-prod.timer (OnUnitActiveSec=300s) on ironclad.
Entry point: /home/gandalf/.virtualenvs/cquota/bin/cquota --poll-only (venv: cquota, installed editable from ~/prod/cquota/).
Source: ~/working/bob/cquota/ (vivo rule: cquota). MAX_RUNTIME=240s (below 300s interval to prevent overlap).
runsOn:
- ironclad-os
- kind: Service
name: drogher-adminer-prod
tags:
- service
- daemon
- execution-standard
notes: 'Adminer database management UI. Always-on, PHP built-in server on 0.0.0.0:8080. Run-as: www-data. Hardening: full (NoNewPrivileges, PrivateTmp, ProtectSystem=full, ProtectHome=true). Watcher .path unit active. Full trio in /usr/services/adminer_prod/.'
runsOn:
- drogher-os
- kind: Service
name: drogher-apestaart-prod-db
tags:
- service
- database
- mariadb
- apestaart
- prod
labels:
db.type: mariadb
db.database: apestaart
db.seal_alias: apestaart_mariadb
notes: 'Prod MariaDB instance backing apestaart (Gmail/Calendar automation service): rules, accounts, messages.'
runsOn:
- drogher-os
- kind: Service
name: drogher-glances-mqtt
tags:
- service
- daemon
- execution-standard
notes: 'Glances MQTT exporter — publishes system metrics to MQTT broker. Always-on. Run-as: root (needs /proc, /sys). Hardening: NoNewPrivileges, PrivateTmp. Watcher .path unit active. Full trio in /usr/services/glances_mqtt/.'
runsOn:
- drogher-os
- kind: Service
name: drogher-kleos-prod-db
tags:
- service
- database
- mariadb
- kleos
- prod
labels:
db.type: mariadb
db.database: kleos
db.seal_alias: kleos_mariadb
notes: Prod MariaDB instance backing kleos and its sub-projects (epistole, angelos, tessera, pragma, oikonomos, skopos, agora) — shared single DB per project.
runsOn:
- drogher-os
- kind: Service
name: drogher-kodiak-archivelogstokoofr
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'Rclone move bugatti archive logs to Koofr cloud. Timer: hourly at :13. Job: kodiak_archivelogs (renamed from kodiak_archivelogstokoofr). Uses encrypted rclone config via secrets.json. Max duration: 45m.'
runsOn:
- drogher-os
- kind: Service
name: drogher-kodiak-bugattil2tokoofr
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'Rclone copy bugatti L2 catalog to Koofr cloud backup. Timer: 3x daily (04:00, 18:00, 23:00). Job: kodiak_bugattil2tokoofr. Uses encrypted rclone config via secrets.json.'
runsOn:
- drogher-os
- kind: Service
name: drogher-kodiak-bugattitol2
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'Rclone copy bugatti local catalog to L2 storage. Timer: hourly at :24. Job: kodiak_bugattitol2. Uses encrypted rclone config via secrets.json. Max duration: 25m.'
runsOn:
- drogher-os
- kind: Service
name: drogher-kodiak-ecmcleanup
tags:
- systemd-timer
- scheduled
- execution-standard
notes: |
Kodiak ECM cleanup — removes orphaned records and clears space. Timer: daily at 04:39. Job: kodiak_ecmcleanup. Python venv: kodiak. Settings: settings_info.json.
KNOWN ISSUE (2026-04-20): currently Type=simple with Restart=no, blocking on a Python process that regularly overruns the 24h timer interval. While the service is active(running), systemd dedupes subsequent timer fires as no-ops — ExecStart does not re-run, so job_process_ctl.sh never evicts the overrun process. Observed: 28h+ active run on 2026-04-19–20, today's 04:39 timer fire suppressed. PLANNED REFACTOR: convert to the Detached Worker Pattern (Type=oneshot + KillMode=process + `bash -c 'nohup … &'`), which releases the unit to inactive within a second so the timer fires on schedule and job_process_ctl can do its job. See wiki: Detached Worker Pattern + Cron Wrapper & Action Script Standard → Service Type Rules (corrected 2026-04-20).
runsOn:
- drogher-os
- kind: Service
name: drogher-kodiak-ecmmanage
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'Kodiak ECM manage aperture — runs source audit + cleanup for bugatti_local endpoint. Timer: every 30 min. Job: kodiak_ecmmanage. Renamed from kodiak_ecm_manage_aperture. Python venv: kodiak. Settings: settings_info.json.'
runsOn:
- drogher-os
- kind: Service
name: drogher-kodiak-ecmsourceaudit
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'Kodiak ECM source audit — scans all endpoints for new/changed files. Timer: every 3 hours. Job: kodiak_ecmsourceaudit. Python venv: kodiak. Settings: settings_info.json.'
runsOn:
- drogher-os
- kind: Service
name: drogher-kodiak-koofrtobugattil4
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'Rclone copy koofr 2025/MM subfolders to seastore offload tier (storagelocationid=5). Timer: 4x daily at 01:07, 07:07, 13:07, 19:07 (staggered :07 past hour to clear ecmmanage :00/:30 and l2tokoofr :15/:45). MAX_RUNTIME=5h45m; each rclone gets whatever remains of that budget (dynamic --max-duration based on elapsed time since script start; --cutoff-mode=hard). Per-path: writes log to /srv/bugatti_local/rclonelogs_l4.staging/, Python rewrites .object with sub-path prefix (so processor matches d_Location via koofr.Object type filter + passes non-JSON summary lines verbatim), atomic rename into /srv/bugatti_local/rclonelogs_l4/ for ecmmanage consumption. Line-count assertion catches any rewrite truncation. Job: kodiak_koofrtobugattil4. Config (rclone_paths, maxsize, etc.) in mariadb s_Admin_Settings.ecm.endpoints.bugatti_l4. Legacy underscore naming retained for consistency with kodiak_bugattil2tokoofr.'
runsOn:
- drogher-os
- kind: Service
network:
ip: 192.168.86.46
name: drogher-lan
runsOn:
- drogher-os
- kind: Service
name: drogher-mariadb-backup-full
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'MariaDB full backup via mariabackup. Timer: daily at 02:30. Job: mariadb_backupfull (renamed from shared mariadb_backup to resolve name collision). Run-as: root. Backup target: /mnt/backup_mariadb/full/. Retention: 30 days. Clears incremental chain on completion.'
runsOn:
- drogher-os
- kind: Service
name: drogher-mariadb-backup-incremental
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'MariaDB incremental backup via mariabackup. Timer: every 6 hours. Job: mariadb_backupincr (renamed from shared mariadb_backup to resolve name collision). Run-as: root. Chained incremental off last full. Skips gracefully if no chain base exists. Note: mariabackup writes progress to stderr (non-error noise).'
runsOn:
- drogher-os
- kind: Service
name: drogher-sage-prod-db
tags:
- service
- database
- mariadb
- sage
- prod
labels:
db.type: mariadb
db.database: sage
- kind: Service
name: drogher-sweep-prod
tags:
- service
- systemd-timer
- sweep
- execution-standard
notes: Venv hygiene + schema_dump checks. Runs every 6 hours via sweep_prod.timer. Code at ~/prod/sweep/. Results persisted to drogher MariaDB. schema_dump posts schemas of all 7 tracked MariaDB aliases (apestaart, kleos, media, sage, sensordata, trends, vivo_releases) to seal-api-prod on surfaceblack.
runsOn:
- drogher-os
- kind: Service
name: drogher-vaultsink-backup
tags:
- systemd-timer
- scheduled
- execution-standard
notes: 'VaultSink backup. Migrated from cron to systemd timer. Timer: daily at 03:00. Job: vaultsink_backup. Run-as: root. Full trio in /usr/services/vaultsink_backup/.'
runsOn:
- drogher-os
- kind: Service
name: epistole-api-prod
tags:
- service
- daemon
- api
- kleos
- prod
labels:
lan_alias: https://epistole-api-prod.internal
port: 8025
notes: |
Prod instance of Epistole email triage API (Kleos sub-project).
Service: epistole_api_prod.service — deployed to ~/prod/kleos/projects/epistole/backend/ with venv at ~/.virtualenvs/epistole-prod/.
LAN alias: epistole-api-prod.internal.
Deployed via vivo-release from ironclad ~/working/kleos/.
runsOn:
- forge-os
- kind: Service
name: epistole-api-trunk
tags:
- service
- daemon
- api
- kleos
- trunk
labels:
port: 8015
lan_alias: https://epistole-api-trunk.internal
notes: |
Trunk instance of Epistole email triage API (Kleos sub-project).
Service: epistole-api-trunk.service — deployed to ~/trunk/kleos/projects/epistole/backend/ with venv at ~/.virtualenvs/epistole/.
LAN alias: epistole-api-trunk.internal.
Deployed via vivo from ironclad ~/working/kleos/projects/epistole/.
runsOn:
- forge-os
- kind: Service
network:
ip: 192.168.86.171
name: fibarohc2-lan
labels:
lan_alias: http://fibarohc2-prod.internal
cloudflare: https://fibarohc2.unbiasedrhino.cyou
runsOn:
- fibarohc2
- kind: Service
network:
ip: forge.lan
port: 17080
protocol: http
name: forge-activepieces
tags:
- docker-stack
- automation
- execution-standard
labels:
lan_alias: http://activepieces-prod.internal
cloudflare: https://activepieces.unbiasedrhino.cyou
notes: 'ActivePieces workflow automation. 3 containers (app, postgres, redis). Logging + no-new-privileges applied. LAN alias: activepieces-prod.internal. Path: ~/prod/activepieces/'
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 8404
protocol: http
name: forge-angus-mcp
tags:
- docker-stack
- mcp
- execution-standard
notes: 'Angus MCP server — TiddlyWiki knowledge base with Sage RAG pipeline. Full hardening (read_only, cap_drop ALL, no-new-privileges). Path: ~/prod/zoltar/angus-mcp/'
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 13378
protocol: TCP
name: forge-audiobookshelf
tags:
- docker-stack
- media
notes: |
Audiobookshelf — audiobook and ebook server. Docker compose at /home/gandalf/prod/audiobookshelf/. Mounts nastronaut coldstore media: Audible, eBooks, Comics (read-only in container). Config and metadata stored locally on forge. External access via https://bookshelf.unbiasedrhino.cyou (VPS Caddy reverse proxy over Tailscale).
- Audiobookshelf web UI
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 3080
protocol: http
name: forge-caddy
tags:
- docker-stack
- reverse-proxy
- execution-standard
notes: 'Caddy reverse proxy on forge (custom build with Cloudflare DNS plugin). Serves Orbotnik production frontend as static files on port 3080, with Atoll auth/API paths proxied to ironclad (192.168.86.5:8452 via LAN). Nextcloud cloud domain removed 2026-03-30 after migration to ironclad. Path: ~/prod/caddy/'
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 18090
protocol: http
name: forge-clipcascade
tags:
- docker-stack
- clipboard
- execution-standard
labels:
lan_alias: http://clipcascade-prod.internal
public_url: https://clipcascade.unbiasedrhino.cyou
notes: 'ClipCascade clipboard sync. Logging + no-new-privileges applied. LAN alias: clipcascade-prod.internal. Path: ~/prod/clipcascade/'
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 8081
protocol: http
name: forge-dozzle
tags:
- docker-stack
- monitoring
- execution-standard
notes: 'Dozzle Docker log viewer. Connects to agents on surfaceblack, ironclad, aperture. Path: ~/prod/dozzle/'
runsOn:
- forge-os
- kind: Service
name: forge-glances-mqtt
tags:
- service
- daemon
notes: |
Glances metrics exporter publishing to MQTT.
runsOn:
- forge-os
- kind: Service
name: forge-javelin-api-service
tags:
- service
- daemon
notes: |
Javelin REST API service.
runsOn:
- forge-os
- kind: Service
name: forge-javelin-scheduler-service
tags:
- service
- daemon
notes: |
Javelin task scheduling service.
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 18080
protocol: http
name: forge-kitchenowl
tags:
- docker-stack
- household
- execution-standard
labels:
lan_alias: http://kitchenowl-prod.internal
cloudflare: https://kitchenowl.unbiasedrhino.cyou
notes: |
STOPPED 2026-04-18 — shut down to free memory after forge swap-thrash incident (~100 MB swapped out by uWSGI worker alone). Restart policy 'unless-stopped' honours manual stop, so will not come back on reboot. To resurrect: `cd /home/gandalf/prod/kitchenowl && sudo docker compose start`.
KitchenOwl grocery and recipe manager. 2 containers (front, back). LAN alias: kitchenowl-prod.internal. Path: ~/prod/kitchenowl/
runsOn:
- forge-os
- kind: Service
network:
ip: 192.168.86.4
name: forge-lan
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 3214
protocol: http
name: forge-manyfold
tags:
- docker-stack
- 3d-printing
- execution-standard
labels:
lan_alias: http://manyfold-prod.internal
cloudflare: https://manyfold.unbiasedrhino.cyou
notes: 'Manyfold 3D model library. 3 containers (app, postgres, redis). Libraries on /srv/manyfold_libraries. Secrets in .env. LAN alias: manyfold-prod.internal. Path: ~/prod/manyfold/'
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 17090
protocol: http
name: forge-nextcloud
tags:
- docker-stack
- cloud-storage
- decommissioned
labels:
lan_alias: http://nextcloud-prod.internal
cloudflare: https://nextcloud.unbiasedrhino.cyou
notes: DECOMMISSIONED 2026-03-24. Migrated to ironclad-nextcloud. Stack stopped, data on NAS (unchanged). Keep ~/prod/nextcloud/ on forge for 1 week before cleanup.
- kind: Service
network:
ip: forge.lan
port: 9090
protocol: http
name: forge-ntfy
tags:
- docker-stack
- notifications
- execution-standard
labels:
lan_alias: http://ntfy-prod.internal
cloudflare: https://ntfy.unbiasedrhino.cyou
notes: 'ntfy push notification server. Logging + no-new-privileges applied. LAN alias: ntfy-prod.internal. Path: ~/prod/ntfy/'
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 3080
protocol: http
name: forge-orbotnik
tags:
- docker-stack
- automation
- execution-standard
labels:
lan_alias: http://orbotnik-prod.internal
cloudflare: https://orbotnik.unbiasedrhino.cyou
notes: 'Orbotnik home automation web app platform. LAN alias: orbotnik-prod.internal. Cloudflare: orbotnik.unbiasedrhino.cyou.'
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 8400
protocol: http
name: forge-rackpeek-mcp
tags:
- docker-stack
- mcp
- execution-standard
notes: 'RackPeek MCP server. Full hardening (read_only, cap_drop ALL, no-new-privileges). Path: ~/prod/zoltar/rackpeek-mcp/'
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 8090
protocol: http
name: forge-rackpeek-web
tags:
- docker-stack
- monitoring
- execution-standard
labels:
lan_alias: http://rackpeek-prod.internal
cloudflare: https://rackpeek.unbiasedrhino.cyou
notes: 'RackPeek web UI (aptacode/rackpeek). Reads config.yaml. LAN alias: rackpeek-prod.internal. Path: ~/prod/rackpeek/'
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 11333
protocol: http
name: forge-rspamd-trunk
tags:
- docker-stack
- spam
- rspamd
- trunk
notes: |
Rspamd spam classifier (trunk install). HTTP-only.
Scanner: 11333 (/checkv2, /ping). Controller: 11334 (/learnspam, /learnham).
Backed by dedicated Redis (rspamd-trunk-redis) for Bayes corpus.
Path: ~/trunk/rspamd/. Controller password in squirrel: rspamd.controllerpw (trunk).
runsOn:
- forge-os
- kind: Service
name: forge-scooby-dogpack
tags:
- monitoring
- systemd-timer
notes: Scooby health monitor running as systemd timer (60s interval). Monitors ClipCascade HTTP health.
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 8402
protocol: http
name: forge-scooby-dogpack-mcp
tags:
- docker-stack
- mcp
- execution-standard
notes: 'Scooby Dogpack MCP server. Full hardening (read_only, cap_drop ALL, no-new-privileges). Path: ~/prod/zoltar/scooby-dogpack-mcp/'
runsOn:
- forge-os
- kind: Service
network:
port: 8888
protocol: http
name: forge-searxng
tags:
- docker-stack
- search
- execution-standard
labels:
lan_alias: http://searxng-prod.internal
notes: |
STOPPED 2026-04-18 — shut down to free memory after forge swap-thrash incident (~45 MB swapped out across workers). Restart policy 'unless-stopped' honours manual stop, so will not come back on reboot. To resurrect: `cd /home/gandalf/prod/searxng && sudo docker compose start`.
SearXNG metasearch engine. 2 containers (searxng, valkey). Had logging already, no-new-privileges added. LAN alias: searxng-prod.internal. Path: ~/prod/searxng/
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 8080
protocol: http
name: forge-stirling-pdf
tags:
- docker-stack
- documents
- execution-standard
notes: 'Stirling PDF toolkit. Single container. Path: ~/prod/stirling-pdf/'
runsOn:
- forge-os
- kind: Service
name: forge-sweep-prod
tags:
- service
- systemd-timer
- sweep
- execution-standard
notes: Venv hygiene checks on forge. Runs every 6 hours via sweep_prod.timer. Code at ~/prod/sweep/, venv at ~/.virtualenvs/sweep/. Results persisted to Drogher MariaDB. Replaced sweep_fleet (old SSH fleet sweep) on 2026-03-23.
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 5181
protocol: http
name: forge-tycho
tags:
- docker-stack
- monitoring
- execution-standard
labels:
lan_alias: http://tycho-prod.internal
notes: 'Tycho service observability dashboard (rhino monorepo). LAN alias: tycho-prod.internal.'
runsOn:
- forge-os
- kind: Service
network:
ip: forge.lan
port: 8403
protocol: http
name: forge-vaultsink-mcp
tags:
- docker-stack
- mcp
- execution-standard
notes: 'VaultSink MCP server. Full hardening (read_only, cap_drop ALL, no-new-privileges). Path: ~/prod/zoltar/vaultsink-mcp/'
runsOn:
- forge-os
- kind: Service
name: forge-vaultsink-prod
tags:
- cron
- scheduled
notes: |
VaultSink production backup task.
runsOn:
- forge-os
- kind: Service
name: forge-watchtower-connections
tags:
- service
- daemon
notes: |
Watchtower NAT connection capture service.
runsOn:
- forge-os
- kind: Service
name: forge-watchtower-packets
tags:
- service
- daemon
notes: |
Watchtower packet capture service (SPAN ingest).
runsOn:
- forge-os
- kind: Service
name: forge-watchtower-pihole
tags:
- service
- daemon
notes: |
Watchtower Pi-hole DNS integration service.
runsOn:
- forge-os
- kind: Service
network:
ip: 192.168.86.31
port: 8123
protocol: http
name: haos-home-assistant
tags:
- service
- daemon
- automation
labels:
lan_alias: http://home-assistant-prod.internal
cloudflare: https://home.unbiasedrhino.cyou
notes: 'Home Assistant home automation platform — dashboards, automations, integrations. LAN alias: home-assistant-prod.internal. Cloudflare: home.unbiasedrhino.cyou.'
runsOn:
- haos
- kind: Service
network:
ip: 192.168.86.31
name: haos-lan
runsOn:
- haos
- kind: Service
name: haos-sweep-config
tags:
- sweep
- config
notes: |
ssh_alias: haos
ssh_user: root
ssh_host: haos.lan
checks:
- haos_info
- haos_addons
- tailscale
- system
runsOn:
- haos
- kind: Service
name: ironclad-ashbox
tags:
- service
- sandbox
- libvirt
- tor
labels:
scripts: /home/gandalf/prod/ashbox/scripts/
bob_tool_id: ashbox
socat_ports: 9091,9696,8989,7878,5656,5657,8080
notes: 'Anonymous sandbox environment. Two libvirt VMs (ashbox-gateway: Tor router, ashbox-workstation: isolated workspace) on sandbox-isolated network (10.99.99.0/24). Managed via Bob launcher (build/start/shutdown/destroy). Host-side socat forwards expose VM services on localhost. 9p storage passthrough at /mnt/ashbox-storage. Internal services: Transmission, SABnzbd, Prowlarr, Sonarr, Radarr, Unpackerr, SearXNG. Scripts: build.sh (full rebuild), start.sh (activate network + start VMs + mounts + forwards), shutdown.sh (graceful stop), destroy.sh (full teardown). Encrypted tooling (.age) contains Ansible playbooks for internal service installation.'
runsOn:
- ironclad-os
- kind: Service
network:
ip: localhost
port: 8405
protocol: http
name: ironclad-askii-mcp
tags:
- docker-stack
- mcp
- execution-standard
notes: 'Askii MCP server — RAG-powered semantic search across Sage knowledge bases. Stateless HTTP transport. Path: ~/prod/zoltar/askii-mcp/'
runsOn:
- ironclad-os
- kind: Service
name: ironclad-atoll-prod
tags:
- service
- daemon
- execution-standard
- auth
- event-bus
- prod
notes: 'Atoll centralised auth service prod. Backend: atoll-api-prod :8452 (Python aiohttp, RS256 JWT auth, user prefs, service tokens, user API tokens, intra-app service roles, shute WebSocket event bus). Frontend: atoll_ui_prod :5183 (Python static file server serving React SPA from ~/prod/atoll/frontend/dist/). HTTPS via ironclad caddy-lan at https://atoll-prod.internal. Managed by Vivo Release Manager.'
runsOn:
- ironclad-os
- kind: Service
name: ironclad-atoll-trunk
tags:
- service
- daemon
- execution-standard
- auth
- event-bus
notes: 'Atoll centralised auth service (HTTPS via Caddy, backend :8451). RS256 JWT auth, user prefs, service tokens, user API tokens, intra-app service roles, shute WebSocket event bus. Always-on, systemd+wrapper. Python venv atoll. Frontend: Vite dev server :5182. Source: ~/working/atoll/, trunk: ~/trunk/atoll/. Vivo-deployed. Bob launcher: atoll-trunk.'
runsOn:
- ironclad-os
- kind: Service
network:
port: 80
protocol: http
name: ironclad-caddy-lan
tags:
- docker-stack
- reverse-proxy
- execution-standard
notes: 'Caddy LAN reverse proxy — .internal aliases for all internal services + HTTPS for cloud domains with pihole local DNS overrides (Cloudflare DNS-01 certs). Custom build with caddy-dns/cloudflare plugin (Dockerfile + xcaddy). Docker container with network_mode: host on port 80 (HTTP) + 443 (HTTPS). Routes by Host header to backend services across ironclad, forge, surfaceblack, and other hosts. Wildcard DNS (*.internal → 192.168.86.5) via pihole dnsmasq. Cloud domains currently handled: nextcloud.unbiasedrhino.cyou. Path: ~/prod/caddy-lan/'
runsOn:
- ironclad-os
- kind: Service
network: {}
name: ironclad-claude-nextsync
tags:
- service
- systemd-timer
- sync
- execution-standard
notes: 'Claude docs bisync (Nextcloud ↔ local). Timer every 15min (OnUnitActiveSec=900s). Rclone bisync with encrypted config. Hardening: base4.'
runsOn:
- ironclad-os
- kind: Service
network: {}
name: ironclad-cliprelay-listener
tags:
- service
- daemon
- clipboard
- decommissioned
notes: Retired 2026-03-10. Was the STOMP bridge to ClipCascade on forge — no longer needed since ClipCascade is now accessed directly via public URL (clipcascade.unbiasedrhino.cyou).
runsOn:
- ironclad-os
- kind: Service
name: ironclad-cliprelay-quarantine
tags:
- service
- systemd-timer
- clipboard
- decommissioned
notes: DECOMMISSIONED — replaced by ironclad-hopper-quarantine. Old ClipRelay quarantine sweeper, disabled 2026-04-06.
runsOn:
- ironclad-os
- kind: Service
network:
port: 19090
protocol: http
name: ironclad-cliprelay-upload
tags:
- service
- daemon
- clipboard
- decommissioned
labels:
lan_alias: http://cliprelay-prod.internal
cloudflare: https://clipboard.unbiasedrhino.cyou
notes: DECOMMISSIONED — replaced by ironclad-hopper-upload. Old ClipRelay upload endpoint, disabled 2026-04-06.
runsOn:
- ironclad-os
- kind: Service
network:
port: 8847
protocol: http
name: ironclad-crashpad
tags:
- service
- daemon
- web
- fastapi
- react
labels:
lan_alias: http://start.internal
notes: 'Crashpad tool launcher — FastAPI backend serving React SPA. Dashboard for launching and managing dev tools, bookmarks, and services. Tool registry via tools.json. Prod systemd service: crashpad_backend.service. Trunk on port 8848 (crashpad_trunk_backend.service). Vivo-deployed from ~/working/crashpad/. Virtualenv: crashpad.'
runsOn:
- ironclad-os
- kind: Service
name: ironclad-dadsmusic-serve
- kind: Service
network:
port: 3000
name: ironclad-excalidraw-canvas
tags:
- service
- daemon
- excalidraw
- mcp
- execution-standard
labels:
lan_alias: http://excalidraw-prod.internal
notes: 'Excalidraw canvas server (:3000). Always-on, systemd+wrapper. Watcher pair watches ~/prod/excalidraw-mcp/dist/, package.json. Node.js. Hardening: base4. LAN alias: excalidraw-prod.internal.'
runsOn:
- ironclad-os
- kind: Service
network:
port: 8765
protocol: http
name: ironclad-gptex-search
tags:
- service
- daemon
- search
labels:
lan_alias: http://gptex-search-prod.internal
notes: 'GPTex Search — search 2,700+ processed ChatGPT conversations. Python app (venv: gptex). LAN alias: gptex-search-prod.internal. Path: ~/working/bob/gptex/'
runsOn:
- ironclad-os
- kind: Service
name: ironclad-harvest-detect-trunk
tags:
- service
- systemd-timer
- execution-standard
- claude
- harvest
- carapace
labels:
wiki: '[[Carapace Harvest]]'
monorepo: '[[Carapace]]'
notes: "5-minute token-delta detector + harvest queue seeder. Parses ~/.claude/projects/*.jsonl, emits claude_tokens points to InfluxDB (bucket carapace_harvest_*), and enqueues idle (≥30min) sessions into ~/.claude/harvest-queue.db with priority=0 for today's files and priority=1 for older. Never calls Sage. Timer: OnUnitActiveSec=300s. Entry point: /home/gandalf/.virtualenvs/harvest-trunk/bin/harvest-detect. Source: ~/working/carapace/packages/harvest/ (vivo rule: 'harvest trunk (source)' → ~/trunk/harvest/). Depends on InfluxDB (influxdb.surfaceblack.carapace.*) for metric emission; queue upsert still occurs on Influx failure (acceptance #8). Part of carapace/harvest-live-service project."
runsOn:
- ironclad-os
- kind: Service
name: ironclad-harvest-worker-trunk
tags:
- service
- systemd-timer
- execution-standard
- claude
- harvest
- sage
- carapace
labels:
wiki: '[[Carapace Harvest]]'
monorepo: '[[Carapace]]'
notes: "2-minute harvest queue consumer. flock-guarded singleton. Claims one pending row from ~/.claude/harvest-queue.db (priority 0 first = today, then priority 1 = backfill), runs Sage gist/link/extract/investigate pipeline on that session's JSONL, writes harvest-context.md to ~/nextcloud/Technical/claude/_attachments/<session>/, registers as tool_output attachment via carapace_sessions. Sage priority: 8 for today, 9 for backfill. Timer: OnUnitActiveSec=120s. Entry point: /home/gandalf/.virtualenvs/harvest-trunk/bin/harvest-worker. Source: ~/working/carapace/packages/harvest/ (vivo rule: 'harvest trunk (source)' → ~/trunk/harvest/). Depends on ironclad-sage. Reads same queue DB as harvest-detect-trunk. Part of carapace/harvest-live-service project."
runsOn:
- ironclad-os
- kind: Service
network:
port: 19091
protocol: http
name: ironclad-hopper-api-trunk
tags:
- service
- daemon
- upload-relay
- execution-standard
- trunk
labels:
lan_alias: http://hopper-trunk.internal
notes: 'Hopper upload API (TRUNK). FastAPI + Atoll JWT auth. Service: hopper-api-trunk. Venv: hopper-trunk. Port 19091.'
runsOn:
- ironclad-os
- kind: Service
name: ironclad-hopper-quarantine
tags:
- service
- systemd-timer
- upload-relay
- execution-standard
notes: 'Hopper quarantine sweeper. Timer every 10min (OnUnitActiveSec=600s). Checks paperless consume folder for stale files, moves failures to quarantine dirs. Python venv hopper. Hardening: base4.'
runsOn:
- ironclad-os
- kind: Service
name: ironclad-hopper-trunk-frontend
- kind: Service
name: ironclad-hopper-ui-prod
- kind: Service
network:
port: 19090
protocol: http
name: ironclad-hopper-upload
tags:
- service
- daemon
- upload-relay
- execution-standard
labels:
lan_alias: http://hopper-prod.internal
cloudflare: https://clipboard.unbiasedrhino.cyou
notes: 'Hopper upload API (PROD). FastAPI + Atoll JWT auth. Service: hopper-api-prod. Venv: hopper-prod. Port 19090.'
runsOn:
- ironclad-os
- kind: Service
name: ironclad-immich
tags:
- docker-stack
- photos
- gpu-accelerated
- execution-standard
labels:
port: 2283
image: ghcr.io/immich-app/immich-server:release
notes: 'Immich photo management. 4 containers (server, ML with CUDA GPU, postgres, redis). Fixed restart:always to unless-stopped. Logging + no-new-privileges applied. Path: ~/prod/immich/'
runsOn:
- ironclad-os
- kind: Service
network:
ip: 192.168.86.5
name: ironclad-lan
runsOn:
- ironclad-os
- kind: Service
name: ironclad-metrics-harvest
tags:
- service
- systemd-timer
- execution-standard
notes: 'Claude session metrics harvester. Timer daily 04:00. Harvests usage data from Claude session transcripts. Python3 (system). Hardening: base4.'
runsOn:
- ironclad-os
- kind: Service
network:
ip: ironclad.lan
port: 17090
protocol: http
name: ironclad-nextcloud
tags:
- docker-stack
- cloud-storage
labels:
cloudflare: https://nextcloud.unbiasedrhino.cyou
dependsOn: nastronaut,ironclad-caddy-lan
notes: 'Nextcloud file sync and sharing. 4 containers (app, db, redis, cron). Data on NAS mount at /mnt/nastronaut/users/.nextcloud-data. Paperless consume bind mount at /mnt/paperless_inbox. Paperless archive mounted as Nextcloud Documents folder. LAN HTTP via caddy-lan (nextcloud-prod.internal). LAN HTTPS via caddy-lan (nextcloud.unbiasedrhino.cyou, Cloudflare DNS-01 cert). External HTTPS via VPS Caddy. Migrated from forge to ironclad 2026-03-23. Path: ~/prod/nextcloud/'
runsOn:
- ironclad-os
- kind: Service
network: {}
name: ironclad-ntfy-heartbeat
tags:
- service
- systemd-timer
- monitoring
- execution-standard
notes: "Dead-man's-switch heartbeat for ntfy. Timer every 300s, publishes test msg to https://ntfy-prod.internal (forge), pings healthchecks.io on success/fail. Detects ntfy/forge/ironclad outage independent of Uptime Kuma (which alerts via ntfy itself). Runs /usr/services/ntfy-heartbeat-prod/start.sh as gandalf. Hardening: base4 (NoNewPrivileges, PrivateTmp, ProtectSystem=true, ProtectHome=false)."
runsOn:
- ironclad-os
- kind: Service
network: {}
name: ironclad-ollama
tags:
- service
- daemon
- gpu-accelerated
- llm
notes: 'Ollama local LLM inference server. GPU-accelerated on RTX 5060 Ti 16GB. Models: llama3.2:3b (174 tok/s), llama3.2-vision:11b (49 tok/s). API at localhost:11434.'
runsOn:
- ironclad-os
- kind: Service
network:
port: 5055
protocol: http
name: ironclad-paperless-admin
tags:
- service
- daemon
- documents
labels:
lan_alias: http://paperless-admin-prod.internal
notes: 'Paperless Admin — triage unclassified documents in Paperless-ngx. Python app (venv: tool_launcher). LAN alias: paperless-admin-prod.internal. Path: ~/working/bob/paperless-admin/'
runsOn:
- ironclad-os
- kind: Service
network:
ip: ironclad.lan
port: 17000
protocol: http
name: ironclad-paperless-ngx
tags:
- docker-stack
- documents
- execution-standard
labels:
lan_alias: http://paperless-ngx-prod.internal
cloudflare: https://paperless.unbiasedrhino.cyou
notes: 'Paperless-ngx document management. 5 containers (webserver, db, broker, gotenberg, tika). Migrated from forge 2026-03-08. Logging + no-new-privileges applied. LAN alias: paperless-ngx-prod.internal. Path: ~/prod/paperless-ngx/'
runsOn:
- ironclad-os
- kind: Service
network:
port: 8420
protocol: http
name: ironclad-sage
tags:
- docker-stack
- daemon
- llm
- api
- kb
- rag
labels:
lan_alias: http://sage-prod.internal
lan_alias_trunk: http://sage-trunk.internal
dependsOn: ironclad-docker
notes: 'Sage local LLM chat tool — Docker Compose stack (Ollama + FastAPI). Provides REST/SSE API, React web UI with React Router (Chat + KB Chat pages). Three memory modes (anonymous, session, persistent). SSE streaming chat, model management, GPU metrics. KB system: ChromaDB vector store, Ollama embeddings (nomic-embed-text), RAG-powered Q&A with citation sources. KB Chat UI with source selector, inline article preview, wiki launch links. Syncs persistent data to MariaDB on drogher. Hardened: read_only, cap_drop ALL, no-new-privileges, internal network for Ollama. LAN alias: sage-prod.internal (also sage-trunk.internal). Model routing config lives at ~/working/sage/config/settings-{prod,trunk}.json under routing.task_profiles; profiles are reloaded per-call (no restart needed). Note: the `extract` task profile carries a quality≥4 minimum (as of 2026-04-20) — without it qwen3.5:2b (max_context 4096) outscores qwen3.5:9b whenever input fits, producing fragmented output on chunked work. See wiki: Sage Inference Queue → Task profiles.'
runsOn:
- ironclad-os
- kind: Service
network:
ip: localhost
port: 8409
protocol: http
name: ironclad-sage-mcp
tags:
- docker-stack
- mcp
- zoltar
notes: 'Sage MCP server — exposes local LLM chat and file processing to Claude Code. Wraps the Sage API (port 8420) with tools for chat, file ingestion, chunked processing, and model listing. Stateless HTTP transport. Path: ~/working/dev/zoltar/sage-mcp/'
runsOn:
- ironclad-os
- kind: Service
network: {}
name: ironclad-scooby-dogpack
tags:
- service
- systemd-timer
- monitoring
- execution-standard
notes: 'Scooby dogpack health checks. Timer every 60s. Python venv scooby, pip entry point. Hardening: base4.'
runsOn:
- ironclad-os
- kind: Service
network:
ip: localhost
port: 8406
protocol: http
name: ironclad-sessions-mcp
tags:
- systemd-service
- mcp
- execution-standard
- carapace
labels:
wiki: '[[Sessions MCP Server]]'
monorepo: '[[Carapace]]'
notes: 'Sessions MCP server — manages Claude session docs (briefs, projects, ideas, session registry). SQLite-backed via carapace-sessions shared access layer. Source: ~/working/carapace/packages/sessions-mcp/ (editable install in ~/.virtualenvs/carapace/). Service unit: /etc/systemd/system/sessions_mcp.service. Deploy: git pull + sudo systemctl restart sessions_mcp.service. Logs: journalctl -u sessions_mcp.service.'
runsOn:
- ironclad-os
- kind: Service
network:
port: 8099
protocol: http
name: ironclad-sonic-broker
tags:
- service
- daemon
- playwright
- broker
- docker-stack
notes: "Sonic Playwright Broker — priority queue and exclusive access broker for Playwright browser automation. FastAPI service issuing session tokens for token-gated browser access. Manages reservation queue (priority + FIFO), overstay watchdog, head-of-queue callbacks. Replaces direct Playwright MCP access. Runs as Docker Compose stack 'sonic-broker-prod' (container, host networking), NOT systemd — the prior execution-standard systemd unit (sonic_broker.service) was decommissioned 2026-08-16: it predated the Docker migration and had been crash-looping (~147k restarts) failing to bind :8099, which the Docker container already held. Container bind-mounts ~/prod/sonic/src/broker into /app/src/broker, so code edits deploy via vivo copy to ironclad prod + a container restart (no image rebuild needed). Compose file: ~/prod/sonic/docker-compose.yml."
runsOn:
- ironclad-os
- kind: Service
name: ironclad-sonic-chrome-cdp
- kind: Service
network:
port: 8098
protocol: http
name: ironclad-sonic-trunk
tags:
- service
- daemon
- playwright
- broker
- trunk
- execution-standard
notes: Sonic Playwright Broker — TRUNK deployment. Same codebase as sonic_broker but running from ~/trunk/sonic/ with separate venv (sonic-trunk). For development and testing. Watcher pair (sonic_trunk-watcher.path) watches start.sh, src/broker/, pyproject.toml, settings/sites/, trunk secrets.
runsOn:
- ironclad-os
- kind: Service
network:
port: 2247
name: ironclad-ssh
tags:
- service
- daemon
- ssh
notes: OpenBSD Secure Shell server on non-standard port 2247.
runsOn:
- ironclad-os
- kind: Service
name: ironclad-sweep-prod
tags:
- service
- systemd-timer
- sweep
- execution-standard
notes: 'Local sweep: venv hygiene + workdir compliance checks. Runs every 6 hours via sweep_prod.timer. Code at ~/prod/sweep/, venv at ~/.virtualenvs/sweep/. Results persisted to Drogher MariaDB.'
runsOn:
- ironclad-os
- kind: Service
network:
ip: 100.64.0.4
name: ironclad-tailscale
tags:
- service
- daemon
- tailscale
- networking
notes: Tailscale client (tailscaled.service). Connected to self-hosted Headscale on vps.
runsOn:
- ironclad-os
- kind: Service
network: {}
name: ironclad-vaultsink-backup
tags:
- service
- systemd-timer
- backup
- execution-standard
notes: 'VaultSink backup. Timer daily 03:00. Runs run_vaultsink_backup.sh from ~/prod/vaultsink/. Python venv vaultsink. Hardening: base4.'
runsOn:
- ironclad-os
- kind: Service
name: kleos-api-prod
tags:
- service
- daemon
- api
- kleos
- prod
labels:
port: 5186
notes: |
Prod instance of Kleos backend API — auth proxy (Atoll), events proxy, and backend services for kleos-app.
Service: kleos_api_prod.service — deployed to ~/prod/kleos/projects/api/backend/ with venv at ~/.virtualenvs/kleos-api-prod/.
Deployed via vivo-release from ironclad ~/working/kleos/.
runsOn:
- forge-os
- kind: Service
name: kleos-api-trunk
tags:
- service
- daemon
- api
- kleos
- trunk
labels:
lan_alias: http://kleos-api-trunk.internal
port: 5185
notes: |
Trunk instance of Kleos backend API — auth proxy (Atoll), events proxy, and backend services for kleos-app.
Service: kleos-api-trunk.service — deployed to ~/trunk/kleos/projects/api/backend/ with venv at ~/.virtualenvs/kleos-api/.
Uses create_atoll_proxy_router() from shared atoll.py.
LAN alias: kleos-api-trunk.internal.
Deployed via vivo from ironclad ~/working/kleos/projects/api/.
runsOn:
- forge-os
- kind: Service
name: kleos-ui-prod
tags:
- service
- daemon
- ui
- kleos
- prod
labels:
port: 5184
notes: |
Prod instance of Kleos frontend — Python stdlib static file server (static_server.py) serving pre-built dist/ with SPA fallback.
Service: kleos_ui_prod.service — serves ~/prod/kleos/packages/kleos-app/dist/ using ~/.virtualenvs/kleos-api-prod/bin/python.
No Node.js or Vite dependency at runtime. Deployed via vivo-release from ironclad ~/working/kleos/.
runsOn:
- forge-os
- kind: Service
name: kleos-ui-trunk
tags:
- service
- daemon
- ui
- kleos
- trunk
labels:
port: 5183
notes: |
Trunk instance of Kleos frontend — Vite dev server with HMR.
Service: kleos_ui_trunk.service — runs from ~/trunk/kleos/packages/kleos-app/.
Path watcher (kleos-ui-trunk-watcher.path) auto-restarts on vite.config.ts changes.
Source deployed via vivo from ironclad ~/working/kleos/.
runsOn:
- forge-os
- kind: Service
network:
ip: 192.168.86.29
name: master_ac-lan
runsOn:
- master_ac
- kind: Service
network:
ip: 192.168.86.13
name: me60x-pool-os-network
runsOn:
- me60x-pool-os
- kind: Service
network:
ip: 192.168.86.74
name: nastronaut-lan
runsOn:
- nastronaut
- kind: Service
name: nastronaut-sweep-config
tags:
- sweep
- config
notes: |
ssh_alias: nastronaut
ssh_user: gagarin
checks:
- os
- apt
- system
skip:
- docker
- pihole
- tailscale
- runtimes
runsOn:
- nastronaut
- kind: Service
name: nut_server
tags:
- service
- daemon
notes: |
### Network UPS Tools (NUT)
- Driver: usbhid-ups
- UPS Name: cps_ups
- Connected via USB to local UPS
- Poll Interval: 2 seconds
- Poll Frequency: 30 seconds
Provides power state telemetry for:
- Safe shutdown orchestration
- Monitoring integrations
runsOn:
- forge-os
- kind: Service
name: oikonomos-api-trunk
tags:
- service
- daemon
- api
- kleos
- trunk
labels:
port: 8027
lan_alias: https://oikonomos-api-trunk.internal
source_location: /home/gandalf/working/kleos/projects/oikonomos (ironclad)
deploy_target: /home/gandalf/trunk/kleos/projects/oikonomos (forge)
venv: /home/gandalf/.virtualenvs/oikonomos (editable install)
systemd_unit: oikonomos-api-trunk.service
path_watcher: oikonomos-api-trunk-watcher.path
path_watcher_target: oikonomos-api-trunk-watcher.service
notes: |
Trunk instance of Oikonomos household automation (goals, habits, tasks, todos, projections) — Kleos sub-project.
Service: oikonomos-api-trunk.service — runs uvicorn via /usr/services/oikonomos-api-trunk/start.sh with venv at ~/.virtualenvs/oikonomos/ (editable install of ~/trunk/kleos/projects/oikonomos/api).
Listens on 0.0.0.0:8027. LAN alias: oikonomos-api-trunk.internal (caddy-lan on ironclad proxies to forge:8027).
Deployed via vivo from ironclad ~/working/kleos/projects/oikonomos/.
Auto-restart on file change: oikonomos-api-trunk-watcher.path triggers oikonomos-api-trunk-watcher.service (oneshot systemctl restart). Watched roots: start.sh, all subdirs under api/src/oikonomos/ (adapters, admin, edges, goals, habits, labels, lifeareas, overview, projections, sage, schedules, tasks, todos, types, util), api/config/settings.json, api/config/settings-trunk.json, and ~/trunk/secrets.json. NOTE: PathModified on a parent dir does not bubble to subdirs — each module subdir must be listed explicitly.
Secrets (squirrel): kleos.oikonomos.vikunja_token, kleos.oikonomos.caldav_token, kleos.oikonomos.habitica_api_user, kleos.oikonomos.habitica_api_token.
DB: shared kleos MariaDB pool on drogher.
runsOn:
- forge-os
- kind: Service
network:
ip: 192.168.86.1
port: 8443
protocol: https
name: pihole-admin-web
tags:
- service
- daemon
- networking
- dns
labels:
lan_alias: http://pihole-admin-prod.internal
cloudflare: https://pihole.unbiasedrhino.cyou
notes: 'Pi-hole admin web UI. HTTPS on port 8443. LAN alias: pihole-admin-prod.internal (Caddy terminates TLS). Cloudflare: pihole.unbiasedrhino.cyou.'
runsOn:
- pihole-os
- kind: Service
network:
ip: 192.168.86.1
port: 67
protocol: udp
name: pihole-dhcp
runsOn:
- pihole-os
- kind: Service
network:
ip: 192.168.86.1
port: 53
protocol: udp
name: pihole-dns
runsOn:
- pihole-os
- kind: Service
name: pihole-glances-mqtt
tags:
- service
- daemon
- execution-standard
notes: Glances MQTT exporter. Publishes system metrics to MQTT broker. Managed by systemd (glances_mqtt.service) via execution standard. Glances installed in /home/gandalf/.virtualenvs/glances/. Config at /etc/glances/glances.conf.
runsOn:
- pihole-os
- kind: Service
network:
ip: 192.168.86.1
name: pihole-lan
runsOn:
- pihole-os
- kind: Service
name: pihole-sweep-prod
tags:
- service
- systemd-timer
- sweep
- execution-standard
notes: Venv hygiene checks. Runs every 6 hours via sweep_prod.timer. Code at ~/prod/sweep/. Results persisted to Drogher MariaDB.
runsOn:
- pihole-os
- kind: Service
name: pihole-vaultsink-backup
tags:
- service
- systemd-timer
- backup
- execution-standard
notes: VaultSink backup on pihole. Daily at 03:00 via systemd timer (vaultsink_backup.timer). Migrated from cron + legacy vaultsink_prod to standard framework (2026-03-10). Prod code at /home/gandalf/prod/vaultsink/. Vivo deploys from vaultsink/.
runsOn:
- pihole-os
- kind: Service
name: pihole-watchtower-conntrackpipe
tags:
- service
- daemon
- execution-standard
notes: Streams conntrack events + IPv4/IPv6 environment snapshots to Watchtower receiver on surfaceblack (192.168.86.4:4712). Pure bash, no ~/prod/ code. Managed by systemd via execution standard.
runsOn:
- pihole-os
- kind: Service
name: pihole-watchtower-piholelogpipe
tags:
- service
- daemon
- execution-standard
notes: Streams Pi-hole DNS query log to Watchtower receiver on surfaceblack (192.168.86.4:4711). Pure bash, no ~/prod/ code. Managed by systemd via execution standard.
runsOn:
- pihole-os
- kind: Service
name: pragma-api-prod
tags:
- service
- daemon
- api
- kleos
- prod
labels:
lan_alias: https://pragma-api-prod.internal
port: 8014
notes: |
Prod instance of Pragma board governance API (Kleos sub-project).
Service: pragma_api_prod.service — deployed to ~/prod/kleos/projects/pragma/backend/ with venv at ~/.virtualenvs/pragma-prod/.
Deployed via vivo-release Ansible playbook (release-kleos.yml).
LAN alias: pragma-api-prod.internal (caddy-lan on ironclad → forge:8014).
Endpoints: /health, /roles, /meetings, /documents, /queue, /admin/pipeline, /preflight-rules, /ingest (POST — receives items from Epistole).
Lifecycle graph: epistole_ingest source node seeded at startup (on_pass=COMPLETE, items land in review queue).
runsOn:
- forge-os
- kind: Service
name: pragma-api-trunk
tags:
- service
- daemon
- api
- kleos
- trunk
labels:
port: 8004
notes: |
Trunk instance of Pragma board governance API (Kleos sub-project).
Service: pragma-api-trunk.service — deployed to ~/trunk/kleos/projects/pragma/backend/ with venv at ~/.virtualenvs/pragma/.
Deployed via vivo from ironclad ~/working/kleos/projects/pragma/.
LAN alias: pragma-api-trunk.internal (caddy-lan on ironclad → forge:8004).
Endpoints: /health, /roles, /meetings, /documents, /queue, /admin/pipeline, /preflight-rules, /ingest (POST — receives items from Epistole).
Lifecycle graph: epistole_ingest source node seeded at startup (on_pass=COMPLETE, items land in review queue).
runsOn:
- forge-os
- kind: Service
name: skopos-api-prod
tags:
- service
- daemon
- api
- kleos
- prod
labels:
lan_alias: https://skopos-api-prod.internal
port: 8017
notes: |
Prod instance of Skopos tender intelligence API (Kleos sub-project).
Service: skopos_api_prod.service — deployed to ~/prod/kleos/projects/skopos/api/ with venv at ~/.virtualenvs/skopos-prod/.
LAN alias: skopos-api-prod.internal.
Deployed via vivo-release from ironclad ~/working/kleos/.
runsOn:
- forge-os
- kind: Service
name: srt-control-server-trunk
tags:
- service
- daemon
- srt-control
- trunk
- execution-standard
labels:
port: 8780
ws_port: 8781
notes: |
srt-control web UI for managing sandbox-runtime (srt) network allowlist, plus xterm.js file interrogation sessions over WebSocket. Trunk instance on chromebullet.
Service: srt-control-server-trunk.service — code at ~/trunk/srt-control/ with venv at ~/.virtualenvs/srt-control/, entry point srt-control-server.
Deployed via vivo from ironclad ~/working/sage/srt-control/. run_as=rohan.
Sister instance: srt_control_prod on ironclad (legacy underscore naming, pre-dates Rule 11).
runsOn:
- chromebullet-os
- kind: Service
name: surfaceblack-boston-prod
tags:
- service
- daemon
- execution-standard
notes: 'Boston CLI listener. Always-on, systemd+wrapper. Watcher pair watches ~/prod/boston/src/boston/. Python venv boston. Port: none (listener). Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
network:
ip: 192.168.86.4
port: 9426
protocol: http
name: surfaceblack-cortex-decide
tags:
- service
- daemon
- execution-standard
notes: 'Cortex decision engine (uvicorn :9426). Always-on, systemd+wrapper. Watcher pair watches ~/prod/cortex/src/cortex/decision/. Python venv ha_tools. Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
network:
ip: surfaceblack.lan
port: 7007
protocol: grpc
name: surfaceblack-dozzle-agent
tags:
- docker-stack
- monitoring
- execution-standard
notes: 'Dozzle agent — connects to forge dozzle UI. Path: ~/prod/dozzle-agent/'
runsOn:
- surfaceblack-os
- kind: Service
network:
ip: 192.168.86.4
port: 8009
protocol: http
name: surfaceblack-exitnode-api-trunk
tags:
- service
- daemon
- execution-standard
- trunk
notes: 'Exitnode management API — TRUNK instance (uvicorn :8009). Always-on, systemd+wrapper (exitnode-api-trunk.service). Rewritten version of the legacy ha_exitnode.service: settings-driven via ~/trunk/exitnode/config/settings.json, structured phase state files at /var/lib/exitnode-api-trunk/state/{location}.json, and stepwise API endpoints (/health, /exitnodes, /exitnode/{loc}/{state,log,stream,create,destroy}). Python venv ha_tools. Code lives at ~/trunk/exitnode/ on surfaceblack, source of truth at ~/working/bob/exitnode/ on ironclad. Watcher pair restarts on changes to *.py, start.sh, config/settings.json, ~/trunk/secrets.json. main.tf changes do NOT trigger restart (read on-demand by the script).'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-forgejo
- kind: Service
name: surfaceblack-glances-mqtt
tags:
- service
- daemon
- execution-standard
notes: 'Glances MQTT exporter. Always-on, systemd+wrapper. Watcher pair watches /etc/glances/glances.conf. Python venv glances. Hardening: base4 (NoNewPrivileges, PrivateTmp).'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-grafana
tags:
- monitoring
- dashboard
- grafana
labels:
lan_alias: http://grafana-prod.internal
cloudflare: https://grafana.unbiasedrhino.cyou
port: 3000
service: grafana-server.service
notes: 'Grafana instance for monitoring dashboards. Runs as systemd service. LAN alias: grafana-prod.internal.'
runsOn:
- surfaceblack-os
- kind: Service
network:
ip: 192.168.86.4
port: 8008
protocol: http
name: surfaceblack-ha-exitnode
tags:
- service
- daemon
- execution-standard
notes: 'Exitnode management API (uvicorn :8008). Always-on, systemd+wrapper. Watcher pair watches ~/prod/tools/clouds/. Python venv ha_tools. Hardening: base4. Legacy exitnode-api.service disabled (duplicate). Output file moved to /var/log/ha_exitnode/exitnode_last_output.txt (2026-03-16) — was previously in the watched directory, causing path watcher to restart the service on every create/destroy.'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-haos-git-backup
tags:
- service
- systemd-timer
- execution-standard
notes: 'HAOS config git backup. Timer every 6h. Rclone syncs SSH:/config/ to ~/prod/haos_config, git commit+push. Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-hc2-infra-mqtt
tags:
- service
- systemd-timer
- execution-standard
notes: |
HC2 infrastructure MQTT publisher. Timer every minute. Publishes HC2 controller metrics to MQTT. Python venv ha_tools. Hardening: base4.
Script: /home/gandalf/prod/cortex/src/cortex/hc2_infra_mqtt.py (SOT at ~/working/cortex/src/cortex/hc2_infra_mqtt.py, deployed via cortex vivo rule). Reads HC2 creds from javelin.fibaro.User/PW in secrets.json. NOTE: start.sh SCRIPT_EXEC was historically at /home/gandalf/prod/tools/ha/hc2_infra_mqtt.py (relocated 2026-04-14).
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-hc2-pings-mqtt
tags:
- service
- systemd-timer
- execution-standard
notes: 'HC2 ping results MQTT publisher. Timer every minute. Pings hosts and publishes to MQTT. Python venv ha_tools. Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
network:
ip: 192.168.86.30
name: surfaceblack-lan
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-prometheus
tags:
- monitoring
- prometheus
labels:
port: 9090
service: prometheus.service
url: http://surfaceblack.lan:9090
notes: Prometheus instance scraping Unpackerr metrics (ashbox). Feeds into Grafana.
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-rclone-canonicalmedia
tags:
- service
- systemd-timer
- execution-standard
notes: 'Canonical media backup to Koofr. Timer monthly on 12th at 01:19. Rclone copy from /mnt/nastronaut/media/ with bandwidth limits. Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-rclone-tiddlywiki
tags:
- service
- systemd-timer
- execution-standard
notes: 'TiddlyWiki bisync (Nextcloud ↔ local tiddlers). Timer hourly at :53. Uses rclone with encrypted config. Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
network:
port: 47391
protocol: http
name: surfaceblack-resourcespace
tags:
- docker-stack
- dam
- execution-standard
labels:
lan_alias: http://resourcespace-prod.internal
notes: 'ResourceSpace digital asset manager. 2 containers (app, mariadb). Logging + no-new-privileges applied. Frontend/backend network isolation. LAN alias: resourcespace-prod.internal. Path: ~/prod/resourcespace/'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-scooby-dogpack
tags:
- service
- systemd-timer
- execution-standard
notes: 'Scooby dogpack health checks. Timer every 60s. Python venv scooby, pip entry point. Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-seal-prod
tags:
- service
- systemd-service
- schema-docs
- execution-standard
notes: Committer API for the sweep_seal schema-docs repo. Python aiohttp, listens on https://seal-prod.internal:8450. Accepts POST /schema/dump (sot:live aliases) and POST /schema/compare (sot:repo aliases) from fleet sweep runs; commits dumps or reports drift. Code at ~/prod/seal/. Deployed via vivo; seal-api-prod-watcher.path auto-restarts on code changes (which also clears in-memory quarantine state).
- kind: Service
name: surfaceblack-sonic-test
tags:
- service
- systemd-timer
- execution-standard
notes: 'Sonic network test runner. Timer daily 06:00. Migrated from cron.d. Python venv sonic_env. Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-speedtest-mqtt
tags:
- service
- systemd-timer
- execution-standard
notes: 'Speedtest MQTT publisher. Timer hourly at :17. Runs speedtest-cli, publishes results to MQTT. Python venv ha_tools. Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
network:
ip: 192.168.86.30
port: 6443
protocol: https
name: surfaceblack-sputnik-prod
tags:
- service
- daemon
- execution-standard
labels:
lan_alias: https://sputnikshute-prod.internal
cloudflare: https://sputnikshute.unbiasedrhino.cyou
notes: 'Sputnik webserver (HTTPS :6443). Always-on, systemd+wrapper. Watcher pair watches ~/prod/sputnik/src/sputnik/. Python venv sputnik. TLS certs at /etc/sputnik/tls/. Hardening: base4. LAN alias: sputnikshute-prod.internal (caddy-lan on ironclad terminates TLS). Cloudflare: sputnikshute.unbiasedrhino.cyou (VPS Caddy → Tailscale 100.64.0.61:6443).'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-sweep-config
- kind: Service
name: surfaceblack-sweep-prod
tags:
- service
- systemd-timer
- sweep
- execution-standard
notes: Venv hygiene checks. Runs every 6 hours via sweep_prod.timer. Code at ~/prod/sweep/. Results persisted to Drogher MariaDB.
runsOn:
- surfaceblack-os
- kind: Service
network:
ip: 192.168.86.4
port: 9284
protocol: http
name: surfaceblack-tiddlywiki-prod
tags:
- service
- daemon
- execution-standard
labels:
lan_alias: http://wiki-prod.internal
cloudflare: https://wiki.unbiasedrhino.cyou
notes: 'TiddlyWiki knowledgebase HTTP server (:9284). Always-on, systemd+wrapper. Watcher pair watches ~/prod/tiddlywiki/. Node.js. Hardening: base4. LAN alias: wiki-prod.internal.'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-vaultsink-backup
tags:
- service
- systemd-timer
- execution-standard
notes: 'VaultSink backup. Timer daily 03:00. Runs run_vaultsink_backup.sh from ~/prod/vaultsink/. Python venv vaultsink. Hardening: base4 (PrivateTmp=no for staging).'
runsOn:
- surfaceblack-os
- kind: Service
network:
ip: surfaceblack.lan
port: 8080
protocol: http
name: surfaceblack-vaultwarden
tags:
- docker-stack
- security
- execution-standard
labels:
lan_alias: http://bitwarden-prod.internal
cloudflare: https://bitwarden.unbiasedrhino.cyou
notes: |-
Vaultwarden (Bitwarden-compatible password manager). Converted from docker-run to compose. Secrets in .env. LAN alias: bitwarden-prod.internal. Path: ~/prod/vaultwarden/
Updated 2026-08-01: v1.35.1 -> v1.37.1 (Web-Vault 2026.6.4). Fix for Chrome ext login 404 on POST /identity/accounts/prelogin/password (route missing on old build, client had moved to new prelogin flow). image tag `vaultwarden/server:alpine` is mutable/untagged-version -- re-pull periodically or version drift silently breaks client compat again.
runsOn:
- surfaceblack-os
- kind: Service
network:
ip: surfaceblack.lan
port: 3456
protocol: http
name: surfaceblack-vikunja
tags:
- docker-stack
- productivity
- execution-standard
labels:
lan_alias: http://vikunja-prod.internal
cloudflare: https://vikunja.unbiasedrhino.cyou
notes: 'Vikunja task manager. 2 containers (app, postgres). Secrets in .env. LAN alias: vikunja-prod.internal. Path: ~/prod/vikunja/'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-vivo-prod
tags:
- service
- daemon
- execution-standard
notes: 'Vivo deployment listener. Always-on, systemd+wrapper. Watcher pair watches ~/prod/vivo/modules/, vivo_settings.json, .vivo_commands.json. Python venv vivo. Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
name: surfaceblack-watchtower-receiver
tags:
- service
- daemon
- execution-standard
notes: 'Watchtower receiver (ports 9001 connections, 9002 packets). Always-on, systemd+wrapper. Watcher pair watches ~/prod/watchtower/src/. Python venv in ~/prod/watchtower/.venv/. Hardening: base4.'
runsOn:
- surfaceblack-os
- kind: Service
name: tessera-api-prod
tags:
- service
- daemon
- api
- kleos
- prod
labels:
lan_alias: https://tessera-api-prod.internal
port: 8013
notes: |
Prod instance of Tessera daily briefing API (Kleos sub-project).
Service: tessera_api_prod.service — deployed to ~/prod/kleos/projects/tessera/backend/ with venv at ~/.virtualenvs/tessera-prod/.
LAN alias: tessera-api-prod.internal.
Deployed via vivo-release from ironclad ~/working/kleos/.
runsOn:
- forge-os
- kind: Service
name: tessera-api-trunk
tags:
- service
- daemon
- api
- kleos
- trunk
labels:
lan_alias: https://tessera-api-trunk.internal
port: 8003
notes: |
Trunk instance of Tessera daily briefing API (Kleos sub-project).
Service: tessera_api_trunk.service — deployed to ~/trunk/kleos/projects/tessera/backend/ with venv at ~/.virtualenvs/tessera/.
LAN alias: tessera-api-trunk.internal.
Deployed via vivo from ironclad ~/working/kleos/projects/tessera/.
runsOn:
- forge-os
- kind: Service
name: vivo-mcp
tags:
- mcp
- vivo
- docker
- zoltar
- deployment
labels:
transport: streamable-http
deploy-method: vivo (source hot-reload + infra docker rebuild)
host: surfaceblack
port: 8408
url: http://surfaceblack.lan:8408/mcp
notes: Vivo deployment system MCP server — 12 tools for managing deployment rules, pushing files, and tracking deployments. Runs on surfaceblack alongside vivo daemon and pushlog DB. Config writes SCP back to ironclad working copy (source of truth).
- kind: Service
name: vps-caddy
tags:
- service
- daemon
- web
- proxy
notes: |
Caddy reverse proxy on VPS.
Handles TLS termination for rabbithole.unbiasedrhino.cyou.
Proxies to Headscale on 127.0.0.1:8080.
Proxies apestaart.unbiasedrhino.cyou to forge (100.64.0.6:8000) via Tailscale.
Proxies dadsmusic.unbiasedrhino.cyou to ironclad-dadsmusic-serve (100.64.0.4:8766) via Tailscale.
runsOn:
- vps-os
- kind: Service
name: vps-headscale
tags:
- service
- daemon
- tailscale
- networking
notes: |
### Headscale — Self-hosted Tailscale Coordination Server
- **Binary:** /usr/local/bin/headscale
- **Config:** /etc/headscale/config.yaml
- **Unit:** headscale.service (systemd)
- **URL:** https://rabbithole.unbiasedrhino.cyou
- **Listen:** 127.0.0.1:8080 (behind Caddy reverse proxy)
- **DNS:**
- MagicDNS enabled, base_domain: lan
- Extra records for non-Tailscale devices
- Nameservers: Cloudflare (1.1.1.1, 1.0.0.1)
- **Tailnet Nodes:** ~12 devices (servers, laptops, TVs, exit node)
- **User namespace:** home
- **Policy:** /etc/headscale/policy.hujson
runsOn:
- vps-os
- kind: Service
name: vps-sweep-config
tags:
- sweep
- config
notes: |
ssh_alias: vps
ssh_user: ubuntu
ssh_host: rabbithole.unbiasedrhino.cyou
checks:
- os
- apt
- tailscale
- runtimes
- system
skip:
- docker
- pihole
runsOn:
- vps-os
- kind: Service
network:
ip: 192.168.86.10
name: wifi-puck-garage-lan
runsOn:
- wifi-puck-garage-os
- kind: Service
network:
ip: 192.168.86.12
name: wifi-puck-juniorretreat-lan
runsOn:
- wifi-puck-juniorretreat-os
- kind: Service
network:
ip: 192.168.86.11
name: wifi-puck-masterretreat-lan
runsOn:
- wifi-puck-masterretreat-os
- kind: Service
network:
port: 3000
protocol: http
name: wsl-excalidraw-canvas
tags:
- service
- daemon
- excalidraw
- mcp
- execution-standard
notes: Excalidraw Canvas MCP server on WSL. Node.js (v20) Express + WebSocket app serving live diagram canvas. Managed by systemd (excalidraw_canvas.service) via execution standard. Watcher pair (excalidraw_canvas-watcher.path) watches dist/, package.json, start.sh. Prod code at /home/rohan/prod/excalidraw-mcp/. NO vivo rule — deployment gap, must be addressed separately.
runsOn:
- chromebullet-wsl
- kind: Service
name: wsl-vaultsink-backup
tags:
- service
- systemd-timer
- backup
- execution-standard
notes: VaultSink backup on WSL. Daily at 03:00 via systemd timer (vaultsink_backup.timer). Migrated from cron to systemd timer (2026-03-10). Prod code at /home/rohan/prod/vaultsink/. Vivo deploys from vaultsink/.
runsOn:
- chromebullet-wsl
- kind: Switch
model: NETGEAR GS608v4
ports:
- type: rj45
speed: 1
count: 8
name: gs608v4
notes: |-
## GS608v4 — Unmanaged Switch
**Model:** NETGEAR GS608v4
**Type:** Unmanaged Desktop Switch
**Layer:** Layer-2 only
**Ports:** 8 × 10/100/1000 Mbps (auto-negotiating, Auto-MDI/MDIX)
**Switching Capacity:** 16 Gbps
This device performs no routing, NAT, firewalling, DHCP, VLAN tagging, or port mirroring.
It forwards Ethernet frames only and has no management interface.
Verified 2026-07-29 by physical audit — supersedes all earlier port-mapping text.
---
## Physical Port Mapping
| Port | Connected To | Role |
|------|-------------|------|
| 1 | — | Unused |
| 2 | usw-lite-8-poe:5 | Uplink |
| 3 | Lounge (Sony TV) | Direct run, no intermediate device |
| 4 | — | Unused |
| 5 | fibarohc2 | Fibaro Home Center 2 |
| 6 | NAS | |
| 7 | ls108g:8 | Uplink |
| 8 | surfaceblack | |
---
## Architectural Notes
All ports participate in the same flat LAN broadcast domain.
This switch introduces no routing authority and does not alter packet flow.
All Layer-3 control remains with the UCG-Ultra gateway (see [[Home Network Architecture - Authoritative Reference]]).
- kind: Switch
model: TP-Link LS108G
ports:
- type: rj45
speed: 1
count: 8
name: ls108g
tags:
- unmanaged
- layer2
notes: |-
## LS108G — LAN Distribution Switch
**Model:** TP-Link LS108G (LiteWave series; hardware version not confirmed on label)
**Type:** Unmanaged Desktop Switch
**Layer:** Layer-2 only
**Ports:** 8 x 10/100/1000 Mbps (auto-negotiating, Auto-MDI/MDIX)
**Power:** UPS-protected (cps_value600eigp)
Replaced the previous TP-Link TL-SG108 V9.40 in the same position (hardware refresh, same role). Verified 2026-07-29 by physical audit — supersedes all earlier port-mapping text.
## Physical Port Mapping
| Port | Connected To | Role |
|------|-------------|------|
| 1 | tl-sg108e:1 | Uplink |
| 2 | pihole | DNS server (no longer L3 gateway — see [[Home Network Architecture - Authoritative Reference]]) |
| 3 | tau | |
| 4 | drogher | Database/kodiak server |
| 5 | moron | |
| 6 | aperture | Camera/kodiak server |
| 7 | manhattan | |
| 8 | gs608v4:7 | Uplink |
## Topology Position
Internet → NBN NTD → tl-sg108e → ucg-ultra (L3 boundary) → usw-lite-8-poe → gs608v4 → **ls108g** → LAN devices
- kind: Switch
model: TP-Link TL-SG108 V9.40
ports:
- type: rj45
speed: 1
count: 8
name: tl-sg108
tags:
- unmanaged
- layer2
notes: |-
## TL-SG108 V9.40 — LAN Distribution Switch
**Model:** TP-Link TL-SG108 V9.40
**Type:** Unmanaged Desktop Switch
**Layer:** Layer-2 only
**Ports:** 8 x 10/100/1000 Mbps (auto-negotiating, Auto-MDI/MDIX)
**Switching Capacity:** 16 Gbps
**Power:** UPS-protected (cps_value600eigp)
This is the LAN-side distribution switch. All LAN devices connect here.
## Physical Port Mapping
| Port | Connected To | Interface | Role |
|------|-------------|-----------|------|
| 1 | pihole | eth1 | LAN-side interface of gateway |
| 2 | surfaceblack | USB-Ethernet | Primary server |
| 3 | drogher | eth0 | Database/kodiak server |
| 4 | aperture | eth0 | Camera/kodiak server |
| 5 | forge | eth1 | USB-Ethernet LAN uplink |
| 6–8 | Available | — | Unused |
## Topology Position
Internet → NBN NTD → tl-sg108e → pihole (L3 boundary) → **tl-sg108** → LAN devices
- kind: Switch
model: TP-Link TL-SG108E
managed: true
ports:
- type: rj45
speed: 1
count: 8
name: tl-sg108e
tags:
- managed
- layer2
- span-capable
notes: |-
## TL-SG108E — Core Managed Switch
**Model:** TP-Link TL-SG108E
**Type:** Easy Smart Managed Switch
**Layer:** Layer-2 only
**Ports:** 8 x 10/100/1000 Mbps (auto-negotiating)
**Switching Capacity:** 16 Gbps
**Power:** UPS-protected (cps_value600eigp)
Sits directly between the ISP NTD and the UCG-Ultra gateway. Also carries a second uplink down to ls108g, and hosts forge's two LAN legs (including the SPAN/mirror source). Verified 2026-07-29 by physical audit (hand-drawn wiring map + on-site port trace) — supersedes all earlier port-mapping text, which described the pre-UCG-Ultra (pihole-as-gateway) topology.
## Physical Port Mapping
| Port | Connected To | Role |
|------|-------------|------|
| 1 | ls108g:1 | Downlink to LAN distribution switch |
| 2–4 | — | Unused |
| 5 | forge eth0 | LAN |
| 6 | forge eth1 | SPAN / mirror source |
| 7 | NBN NTD | WAN uplink |
| 8 | ucg-ultra:1 | Uplink to gateway |
## Topology Position
Internet → NBN NTD → **tl-sg108e** → ucg-ultra (L3 boundary) → usw-lite-8-poe → gs608v4 / ls108g → LAN
- kind: Switch
model: UniFi USW-Lite-8-PoE
managed: true
poe: true
ports:
- type: rj45
speed: 1
count: 8
name: usw-lite-8-poe
tags:
- managed
- layer2
- unifi
labels:
mac: 0c:ea:14:f4:08:9f
ip: 192.168.86.249
notes: |-
## USW-Lite-8-PoE — UniFi Access Switch
**Model:** UniFi USW-Lite-8-PoE
**MAC:** 0c:ea:14:f4:08:9f
**IP:** 192.168.86.249
Added to inventory 2026-07-29 — live UniFi hardware not previously modeled in RackPeek.
## Physical Port Mapping
| Port | Connected To | Role |
|------|-------------|------|
| 1 | u7pro-garage | AP uplink |
| 2 | u7pro-juniorretreat | AP uplink |
| 3 | u7pro-masterretreat | AP uplink |
| 4 | — | Unused |
| 5 | gs608v4:2 | Downlink |
| 6 | — | Unused |
| 7 | ironclad | Host uplink |
| 8 | ucg-ultra:5 | Uplink to gateway |
- kind: System
type: baremetal
os: Debian
cores: 4
ram: 4
name: aperture-os
runsOn:
- aperture
- kind: System
type: baremetal
os: Windows
cores: 4
ram: 6
name: chromebullet-os
runsOn:
- chromebullet
- kind: System
name: chromebullet-wsl
tags:
- wsl2
- ubuntu-24.04
- tailscale-node
notes: WSL2 Ubuntu 24.04 instance on chromebullet (Surface Laptop 3). Reachable via Tailscale IP (100.64.0.1) or wsl.lan DNS. SSH on port 2222, user rohan. Runs 3 managed services under execution standard.
runsOn:
- chromebullet
- kind: System
type: baremetal
os: Debian
cores: 4
ram: 4
name: drogher-os
runsOn:
- drogher
- kind: System
type: baremetal
os: Debian
cores: 4
ram: 8
name: forge-os
runsOn:
- forge
- kind: System
type: vm
os: Home Assistant OS
cores: 2
ram: 2
drives:
- type: nvme
size: 31
name: haos
tags:
- tailscale-node
notes: |
### HAOS Virtual Machine
- Hypervisor: KVM (libvirt)
- Host: surfaceblack
- Firmware: EFI
- CPU Mode: host-passthrough
- Disk Format: qcow2
- Network: bridged to br0 (LAN-attached)
This VM represents Home Assistant OS.
Hardware is abstracted and inherited from the host.
runsOn:
- surfaceblack
- kind: System
type: baremetal
os: Ubuntu Server 24.04 LTS
cores: 20
ram: 32
name: ironclad-os
runsOn:
- ironclad
- kind: System
name: me60x-pool-os
runsOn:
- me60x-pool
- kind: System
type: baremetal
os: Debian
cores: 4
ram: 2
name: pihole-os
runsOn:
- pihole
- kind: System
type: baremetal
os: Debian
cores: 4
ram: 8
name: surfaceblack-os
runsOn:
- surfaceblack
- kind: System
type: baremetal
os: Ubuntu
cores: 2
ram: 1
name: vps-os
runsOn:
- vps
- kind: System
type: embedded
os: Google WiFi Firmware
name: wifi-puck-garage-os
runsOn:
- wifi-puck-garage
- kind: System
type: embedded
os: Google WiFi Firmware
name: wifi-puck-juniorretreat-os
runsOn:
- wifi-puck-juniorretreat
- kind: System
type: embedded
os: Google WiFi Firmware
name: wifi-puck-masterretreat-os
runsOn:
- wifi-puck-masterretreat
- kind: Ups
model: CyberPower Value600EIGP
va: 360
name: cps_value600eigp
notes: |-
### Hardware Notes — CPS Value600EIGP
- Manufacturer: CyberPower Systems (CPS)
- Model: Value600EIGP
- Nominal Real Power: 360W
- Battery Type: 12V PbAcid
- Connected via USB to: forge
- NUT Driver: usbhid-ups
- Vendor ID: 0764
- Product ID: 0501
Status reported via NUT:
- Battery Charge: 100%
- Load: 24%
- Input Voltage: 244V
- Output Voltage: 244V
- State: OL (On Line)
connections: []