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: []

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please reload the page.