Commit Graph
371 Commits
Author SHA1 Message Date
mrcharles 8081d09e73 docs(agents): add DNS CLI to key scripts, update inventory ref
Add ~/daytoday/dns/bin/dns to Key Scripts table. Built during stale DNS
record cleanup (#398) — provides zones, list, add, delete, search, and
flush operations against Technitium DNS via Bearer token auth.

[#398]

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-08-07 09:18:36 -05:00
mrcharles 7814227259 docs(agents): add system inventory reference + access audit script
- AGENTS.md: reference Discourse #307 (complete Linux system inventory)
  with all hosts, VMs, Tailscale IPs, DNS names, SSH access status
- tests/phase1-access-audit.sh: reusable script to audit SSH access
  (localuser + root) against all Tailscale-reachable systems

[#397]

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-08-06 17:26:57 -05:00
mrcharles d22db46e9f docs(agents): move operational knowledge from harness memory to AGENTS.md
Make operational instructions portable across AI harnesses (Crush,
Hermes, OpenWebUI). Previously lived in .crush/memory/operational.md
which is Crush-specific.

New sections added to AGENTS.md:
- Switch Console Access (conman): server, script, console map, usage
- Mandatory Documentation Protocol: every infra change must be ticketed
  + logged in Discourse before moving on
- Tooling: available tools, shellcheck, HTTP write constraints
- Key Commands Quick Reference: common commands for all access patterns
- Credential Management: current state + Vault migration goal

Updated:
- "SSH in Crush" generalized to "SSH access" (harness-agnostic)
- Key Scripts table updated with new audit/tuned scripts

The .crush/memory/operational.md file is now redundant; all its content
lives in AGENTS.md.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-08-06 17:17:37 -05:00
mrcharles 7fb37d5b40 feat(proxmox): add guest I/O audit and tuned deployment scripts
Three new scripts for VM-level infrastructure management:

- audit-vm-disks.sh: Audit disk cache config on all VMs across hosts
- audit-guest-io.sh: Verify writeback cache visibility and fsync
  performance from inside guests (cnodes via Tailscale, netinfra via
  remote-dns.sh)
- deploy-tuned-guests.sh: Deploy tuned profiles to VMs via qemu-guest-agent
  (template for when guest agents are deployed fleet-wide)

These establish repeatable processes for VM disk/performance auditing
instead of ad-hoc one-off probes.

[#395]

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-08-06 17:09:36 -05:00
mrcharles 7be2be4027 feat(k8s): add tuned network-latency deployment to post-setup
Adds step to install tuned and set network-latency profile on all
k8s control-plane nodes during cluster bootstrap. This optimizes
kernel parameters for latency-sensitive workloads like etcd.

Also renumbers verification step from [3/3] to [4/4].

[#395]

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-08-06 15:41:52 -05:00
mrcharles 608c006c9e docs(switches): add MAC/LLDP command files for switch audits
Add .cmds files for pulling MAC address tables and LLDP neighbor info
from core-sw01 and tor3-stor via conman console.

[#394]

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-08-06 15:32:47 -05:00
mrcharles 16db9b0426 feat(k8s,proxmox): add etcd tuning for spinning-disk storage + VM disk audit script
k8s/install-cp.sh: add --etcd-arg heartbeat-interval=1000 and
election-timeout=5000 to both bootstrap and join commands. Gives etcd 5x
margin to absorb transient fsync stalls on spinning-disk-backed NFS
storage (default 500ms/1500ms too tight for this environment).

proxmox/perf/scripts/audit-vm-disks.sh: new script to audit disk cache
configuration on all VMs across Proxmox hosts. Identifies VMs that would
benefit from cache=writeback (especially etcd/database workloads on
NFS-backed spinning disk).

[#392] [#393]

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-08-06 15:26:58 -05:00
mrcharles 5614138919 docs(agents): document Redmine + Discourse CLI access
Both tracking systems have read/write CLI wrappers in ~/daytoday/ (Docker
containers backed by gitignored .env files). Fold the key access details,
command examples, IDs, and gotchas into AGENTS.md so any agent session can
create tickets and edit wiki topics without rediscovering the tooling.
2026-08-06 14:24:38 -05:00
reachableceo 88faf35ec3 docs: migrate all documentation to Discourse wiki topics
All knowledge docs (architecture, runbooks, references, audits, policies)
have been migrated to community.turnsys.com as wiki topics in the VP
TechOps category. Discourse is now the canonical source of truth for
documentation; git edit history no longer serves as the doc changelog.

37 .md files converted to short pointers linking to their Discourse
topics. AGENTS.md updated with new documentation workflow policy.
Code (scripts, configs, playbooks) remains authoritative in git.
2026-08-06 14:08:34 -05:00
mrcharles d6988bb35f fix(netinfra): disable Pi-hole rate-limiting causing Uptime Kuma flapping
Root cause of intermittent DNS up/down alerts: Pi-hole's default
rate-limit (1000 queries/60s per client) was throttling Uptime Kuma
on the Cloudron VPS (tsys-cloudron, 100.107.35.78). Uptime Kuma sends
high-volume DNS queries to monitor dozens of hosts; when it exceeded
the limit, Pi-hole responded REFUSED, which Uptime Kuma detected as
DNS being down. This happened every 1-2 minutes.

Evidence: 40 rate-limiting events against 100.107.35.78 in recent
netinfra-01 Pi-hole logs. Also 10.53.0.1 (Docker bridge gateway) hit
4852 queries in a single 60s window.

Fix: Set dns.rateLimit.count=0 and dns.rateLimit.interval=0 on both
nodes (private tailnet, no DNS amplification risk). Also persisted in
docker-compose.yml via FTLCONF env vars.

Combined with prior IPv6 fix (4f82520), this resolves all known causes
of DNS flapping.

[#376]
2026-08-06 13:59:45 -05:00
mrcharles bad40feae6 feat(k8s): add worker join script + 6 worker nodes to env config
- env.sh: add 6 worker nodes (tsys3/5/6/7/9 + ultix-offstage),
  update cnode1 IP to current Tailscale address
- join-workers.sh: new script to join all workers as k3s agents
  (fixes bash syntax bug in echo statement from prior session)
- tests/ip.sh: pass-through wrapper for ip command

[#367] [#368]
2026-08-06 13:11:43 -05:00
mrcharles 4f82520e0e fix(netinfra): kill IPv6 on DNS nodes to stop Pi-hole flapping
Root cause of Uptime Kuma DNS up/down alerts: Pi-hole's upstream config
included Google IPv6 DNS (2001:4860:4860::8888), but netinfra-01 has no
IPv6 internet route. Every forwarded query to the IPv6 upstream failed
with "Network unreachable", causing intermittent DNS resolution
failures every ~8 seconds.

Fix applied to both netinfra-01 and netinfra-02:
- Pi-hole upstream set to 8.8.8.8 only (IPv4); removed 192.168.3.16
  (retired netboot) and 2001:4860:4860::8888 (IPv6 Google DNS)
- IPv6 disabled at kernel level (/etc/sysctl.d/99-disable-ipv6.conf)
- knel.net authoritative resolution unchanged (Technitium via revServers)

Verified: zero IPv6 warnings, zero connection errors, DNS resolving
cleanly from all paths after fix.

[#376]
2026-08-06 13:11:38 -05:00
mrcharles d4850a3c02 docs(tailscale): add exit node HA to netinfra-02 for full 01/02 parity
netinfra-02 now advertises exit node routes (0.0.0.0/0, ::/0) in
addition to the subnet route, matching netinfra-01's configuration.
Both nodes are now fully redundant subnet routers + exit nodes.

Subnet route (192.168.0.0/22) is approved and active. Exit node
routes (0.0.0.0/0, ::/0) require separate approval in the Tailscale
admin console.

[#377]
2026-08-06 12:54:45 -05:00
mrcharles 75e229c0ab docs(tailscale): document subnet router HA + fix resolv.conf immutable bug
Adds section documenting the redundant subnet router architecture:
both netinfra-01 and netinfra-02 now advertise 192.168.0.0/22 for
automatic failover. This fixes the SPOF where all remote monitoring
(Uptime Kuma on Cloudron VPS) routed through netinfra-01 alone —
any DERP bounce or Docker LinkChange event took down visibility to
every LAN host simultaneously.

Also documents the resolv.conf immutable flag fix on netinfra-01
(chattr -i) that was preventing tailscaled from updating DNS config
after every link change event.

[#377]
2026-08-06 12:53:36 -05:00
mrcharles 30ddb37b34 fix(netinfra): commit Pi-hole hardening config previously missing from repo
Commit 33b5c76 claimed to harden Pi-hole on both DNS nodes but only
modified markdown — the working docker-compose.yml, gravity-validate.sh,
healthcheck, and autoheal config were never written to the repo, leaving
the DNS hardening unreproducible from version control.

This commits the live, verified-working config from the boxes into
netinfra/pihole/:
- docker-compose.yml (shm_size 1024M root-cause fix, healthcheck, autoheal)
- gravity-validate.sh (pre-start SQLite header check, auto-quarantine corrupt DB)
- .env.example (web UI password templated; real .env gitignored)

Defends against the gravity.db / /dev/shm corruption production outage.
The live password is templated as ${PIHOLE_WEB_PASSWORD} so no secret
enters git.

[#376]
2026-08-06 12:37:46 -05:00
mrcharles 6c58671d15 docs: add Redmine ticket references to subsystem READMEs
Each component README now links to its corresponding Redmine tickets
(closed for completed work, open for pending items) for bidirectional
traceability between code and system of record.
2026-08-06 11:25:19 -05:00
mrcharles 317a98b9b1 docs: migrate work tracking to Redmine system of record
Replace STATUS.md checklists with a Redmine ticket index. All work
status, completion tracking, and issue management now lives in Redmine
(version: Potential to Kinetic Ready, due 2026-09-30). AGENTS.md gains
a Redmine Tracking Policy section with rules for ticket references,
closing procedures, and STATUS.md's new read-only role.
2026-08-06 11:24:48 -05:00
mrcharles 33b5c76bec feat(netinfra): rebuild netinfra-02 as redundant DNS/DHCP + harden Pi-hole
netinfra-02 was accidentally wiped during storage moves. Rebuilt from
netinfra-01 config: Docker + Technitium (167 zones, rsync timer every 60s),
Pi-hole v6 (healthy, gravity.db validated), NTP container, ISC DHCP failover
secondary (load-balancing with primary, 99+84 active leases).

Pi-hole hardened on BOTH nodes with defense-in-depth against the
gravity.db corruption outage:
- gravity-validate.sh: pre-start SQLite header check, auto-removes corrupt DB
- Healthcheck: validates DNS resolution + gravity.db file integrity
- autoheal container: restarts unhealthy containers automatically
- Root cause (/dev/shm too small) already fixed by operator (shm_size: 1024M)

Also corrected DHCP lease count in audit (98 unique active, not 753 raw blocks).

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-08-06 09:18:45 -05:00
mrcharles d159504121 docs(audit): full ground-truth re-orientation + sync index files to module layout
Read-only audit (nmap/SNMP/SSH/API) reconciling the repo and fleet after the
operator's module/subproject restructure and infrastructure changes. New
authoritative audit doc captures: 4 switches up / 1 down (r2-tor-top offline),
63 VMs (was 28), 167 DNS zones (was 124), 753 DHCP leases (was 333), tsys5
storage now configured, tsys4 upgraded to 9.2.5, k8s cluster DOWN (cnode wiped,
no etcd quorum), netinfra-02 wiped, 3-tier Tailscale fleet (80 nodes).

Index files (README, AGENTS, docmap, STATUS) updated to the new layout
(provisioning→archive, powerman/console/ups→dcinfra, dns-cluster-setup/switches/
dhcp→netinfra, perf→proxmox/perf) and all stale root-level path references fixed.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-08-06 00:49:30 -05:00
mrcharles 04ed433e84 . 2026-08-01 20:31:13 -05:00
mrcharles ea54beea70 meta bits... 2026-08-01 16:45:18 -05:00
mrcharles 959c3a5bb9 more prompt work... 2026-08-01 16:41:13 -05:00
mrcharles 4d86012762 . 2026-08-01 15:50:26 -05:00
mrcharles 6244c1cc25 prep for next ai session 2026-08-01 15:45:23 -05:00
mrcharles 46c35106fb prep for next ai session 2026-08-01 15:44:59 -05:00
mrcharles a1beb6cd3e fix(tuning): persistent NFS tuning fleet-wide + apply-tunings.sh bug fix
Fleet-wide changes (live, documented for audit/AWX):

Persistent tuning via systemd oneshot (all 7 hosts):
- New pfv-nfs-tuning.service runs AFTER tuned.service to override
  16MB TCP buffer caps with 128MB for high-BDP NFS
- New 99-pfv-nfs.conf sysctl fragment (128MB rmem/wmem, 250K backlog)
- Deployed to: tsys1, tsys3, tsys4, tsys5, tsys6, tsys7, tsys9
- Survives reboot — no more tuned clobbering our sysctl values

tsys5 fixes:
- VM 509 stopped, NFS D2/D3/D5 remounted with nconnect=4, VM 509
  restarted (nconnect now live on all tsys5 NFS client mounts)
- SK hynix SSD (sdb1) persisted in fstab at /mnt/ssd2 (UUID-based)
- ssd2 added to Proxmox storage.cfg as dir storage
- Stale S3 export removed from /etc/exports (disk not mounted)
- Broken S4 dir entry removed from storage.cfg
- Duplicate noatime,noatime in fstab fixed
- version=4.2 bug in storage.cfg options fixed (invalid mount option)

tsys4 fixes:
- SSH keys re-standardized to 2 approved (upgrade regenerated host key)
- Tuning persistence working (128MB buffers confirmed live + persistent)

apply-tunings.sh fix:
- Removed version=4.2 from NFS options string (not a valid mount.nfs
  option — causes mount failures; Proxmox auto-negotiates NFSv4.2)

Config files saved to provisioning/ConfigFiles/ for AWX/Ansible consumption:
- 99-pfv-nfs.conf
- pfv-nfs-tuning.service

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-31 08:17:30 -05:00
mrcharles 9c2882ea00 fix: Mermaid diagram syntax + deploy-tuning.sh path + fleet quick fixes
Mermaid diagrams fixed for Gitea rendering:
- Replaced all <br/> → <br> (86 tags across 4 docs)
- Replaced Unicode chars with ASCII (em dash, multiply, middle dot, star)
- Fixed invalid bidirectional dotted arrow in NETWORK-TOPOLOGY.md
  (<--. "..." .--> is not valid Mermaid; replaced with two -.-> arrows)
- Simplified node labels for cleaner rendering

Host fixes applied (live, not in repo — documented here for audit trail):
- SSH keys standardized to exactly 2 approved keys on all 7 hosts
  (removed host-generated RSA keys, added missing VP TechOps key to tsys9)
- Packages installed: iperf3 (tsys9), sysstat (tsys5), nvme-cli (tsys4/5),
  net-tools (tsys1/6/7)
- tsys4 tuning: rmem/wmem 16MB→128MB, netdev_max_backlog 5000→250000,
  tcp_max_syn_backlog 1024→2048, tuned profile→network-throughput
- tsys5: nconnect options added to storage.cfg (3 NFS stanzas patched;
  will apply on next Proxmox NFS remount/reboot)
- noatime applied to root fs on tsys4 + tsys5 (was only tsys5)
- tsys1 lldpd: installed but systemd/dbus issue prevents enabling
  ("Transport endpoint is not connected") — needs investigation/reboot

Bug fix: deploy-tuning.sh computed wrong script path (scripts/ vs
perf/scripts/) — fixed to use dirname/scripts/apply-tunings.sh

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-31 00:12:04 -05:00
mrcharles 290245349f docs(drift): fleet drift report with executive briefing format
Dedicated drift report covering package/service/tuning/security
inconsistencies across all 7 Proxmox hosts. Executive briefing at the
top (what needs decisions, quick severity summary), detailed matrices
in appendixes.

Key drift findings:
- lldpd inactive on tsys1 (blind spot in topology)
- tsys9 missing 2 SSH keys vs fleet standard
- iperf3 missing on tsys9, net-tools missing on tsys1/6/7
- sysstat missing on tsys5, nvme-cli missing on tsys4/5
- tsys4 tuning drift: 16MB TCP buffers, low backlog, wrong tuned profile
- rsyslog + snmpd + beszel inactive fleet-wide (Saturday OAM Day items)
- noatime only on tsys5 root fs (all others use relatime)

Added perf/scripts/probe-drift.sh as a portable reusable drift probe.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 23:27:54 -05:00
mrcharles 0a8865de38 docs: full documentation suite — network, storage, hosts, VMs
Four new standalone reference documents with Mermaid diagrams (renderable
on Gitea), built from live ground-truth data gathered 2026-07-30:

- NETWORK-TOPOLOGY.md: physical topology diagram, complete switch port
  maps (core-sw01 + tor3-stor), VLAN/subnet tables, cross-rack trunk
  analysis with Mermaid, NFS data-flow diagram, per-host NIC reference
- STORAGE-ARCHITECTURE.md: tier definitions, all 14 physical disks
  inventoried with SMART/size/tier, NFS export→mount map with Mermaid,
  per-spindle VM placement (IOPS hotspot analysis), capacity summary
- HOST-REFERENCE.md: one-page-per-host for all 7 nodes with hardware,
  network config, storage, tuning state, role, and running VMs
- VM-INVENTORY.md: VM placement diagram, all 28 running VMs with
  disk/CPU/RAM/config details, optimization audit (cache mode, CPU type,
  qga, disk controller, k8s wnode sizing), stopped VM reference

Updated docmap.md with all four new entries.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 23:10:38 -05:00
mrcharles cd1c2f7332 docs(status): record operator schedule + remote vs onsite work split
Operator onsite Monday 0700. Until then: software/docs/OAM only (Sunday
off). Switch changes + physical cable work deferred to Monday onsite.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 23:03:48 -05:00
mrcharles 24b08aa592 docs(audit): document observability stack + Saturday OAM day plan
Operator-defined monitoring architecture (no tool duplication):
- Beszel (Cloudron VPS): CPU/RAM/disk metrics on all Proxmox hosts via Tailscale
- Uptime Kuma (Cloudron VPS): up/down alerting via Pushover
- LibreNMS (VMID 104, on-prem): SNMP polling for switches (non-Tailsale gear)
- Home Assistant (VMID 100, on-prem): power/temp instrumentation + alerting

Saturday 2026-08-01 is designated OAM Day: goal is 100% instrumentation
coverage across all four tools + centralized syslog.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 23:02:11 -05:00
mrcharles 26de312c42 docs(audit): full ground-truth audit — network, storage, compute, switches
Comprehensive end-to-end audit of the Proxmox cluster covering all three
layers (network, storage, compute) with live data gathered from all 7 hosts
+ 4 switches via SNMP, conman consoles, and LLDP.

Key findings documented in AUDIT-2026-07-30.md:

Network (cross-rack NFS bottleneck root-caused):
- core-sw01 LAG hash = layer-2-3 (no L4 ports) — NFS caps at 1 Gbps per
  host pair regardless of nconnect. tor3-stor already uses layer-2-3-4.
- core-sw01 ch1 member mismatch: g16 is UP but not in LAG; g17 is DOWN.
  Only 3 of 4 cross-rack links active.
- Both switches use static mode (no LACP) for the cross-rack trunk.
- tsys5 bond0 degraded (1 of 2 slaves, nic2 down).

Storage (major topology changes):
- tsys5 new NVMe (CT500P1 500GB) + relocated SK hynix SSD (512GB) — both
  installed but NOT mounted/configured yet.
- tsys4 D3 changed (SK hynix SSD → Seagate HDD, still USB tier-3).
- tsys4 /mnt/albert (Hitachi 2TB internal) + /mnt/backup (WD 5TB USB) added.
- tsys5 S3 unmounted (stale export), S4 gone (broken storage.cfg entry).
- Samsung 860 PRO SSD at 93% full.

Fleet consistency:
- tsys4 behind on PVE 9.1.5/kernel 6.17 (fleet: 9.2.5/7.0.14).
- tsys4/5 bond hash + nconnect + rmem/wmem inconsistent with tsys6/7.
- 10+ VMs using cache=writethrough on NFS (slow synchronous writes).

Gardening: updated STATUS.md, docmap.md, AGENTS.md key scripts table.
Superseded AUDIT-2026-07-29 as the authoritative audit.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 23:01:43 -05:00
mrcharles d9b32e4eef fix(console): fix udev symlink naming bug + add portable audit tooling
Console fix: generate-config.sh wrote SYMLINK+="console/$name" (singular)
but ser2net.yaml opens /dev/consoles/$name (plural). They never matched,
so after every reboot the console ports failed until setup.sh's manual
fallback re-created the symlinks. Fixed the udev rule to use "consoles/"
to match ser2net and the README.

New portable read-only audit tools (AGPLv3-friendly, config-driven):
- perf/scripts/probe-storage.sh: disk/mount/export/SMART/storage.cfg probe
- perf/scripts/probe-network.sh: NIC/bond/LLDP/NFS/nconnect probe
- perf/scripts/conman-console.py: PTY-based conman console driver (replaces
  the old sw-capture.py that conflicted with ser2net)
- perf/scripts/snmp-switch-audit.py: SNMP-based switch inventory (interfaces,
  LLDP, LAG, VLANs) via pysnmp or net-snmp

Removed stale pre-conman switch tooling (sw-capture-remote.sh, sw-capture.py,
sw-probe.sh, sw-conman-probe.sh) and old .cmds files. Added fresh .cmds
files for the two cross-rack trunk endpoint switches.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 22:51:27 -05:00
mrcharles e7312ad460 chore: retire pfv-tsys8 and fix stale host-skip comment in deploy-check.sh
pfv-tsys8 is permanently retired. Strike its only doc reference and
correct the stale comment in perf/deploy-check.sh that wrongly listed
pfv-tsys9 as "off the air" (tsys9 is online and in the host array;
tsys8 is the retired host).

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 22:30:24 -05:00
mrcharles 48cdd81779 docs(ups): document apcupsd test results and Energy Dashboard limitations
The APC Smart-UPS C 1500 (FW 02.2, mfg 2012) does not expose ups.load or
ups.realpower over USB HID. Tested apcupsd as an alternative — it reads the
same HID descriptor and returned COMMLOST. Debian's apcupsd package also
conflicts with nut-server (mutually exclusive).

Documented that the HA NUT integration provides battery/runtime/status sensors
only, not wattage for the Energy Dashboard. Practical path is a smart plug on
the UPS output. Updated daily-ops examples (removed ups.load reference since
the variable doesn't exist on this UPS).

NUT stack fully restored after the apcupsd test cycle.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 12:02:15 -05:00
mrcharles 4b26aca5ee feat(ups): add Home Assistant NUT integration via REST config-flow API
Drive HA's REST config-flow endpoint to add the NUT integration programmatically,
no manual UI clicks required. The script (setup-ha-nut.sh + ha-nut-setup.py) is
idempotent — skips if a NUT entry already exists.

Key finding: HAOS runs Tailscale as an isolated add-on container, so the HA core
container cannot route to Tailscale IPs. Added a LAN listener (192.168.3.11:3493)
to upsd so HA can reach it over the shared vmbr0 bridge. Both VMs (pfv-bms HA at
192.168.3.12 and pfv-tsys1 at 192.168.3.11) are on the same bridge.

Integration is live — sensors for battery charge (100%), status (Online), and
status data (OL) are reporting.

Secrets (HA token, NUT password) are read from ~/.config/pfvcluster/ and never
committed to the repo.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 11:42:25 -05:00
mrcharles 5456c783c6 feat(ups): NUT setup for APC Smart-UPS C 1500 on pfv-tsys1
Deploy Network UPS Tools to monitor the server-room UPS units and feed data to
Home Assistant for power-usage tracking.

APC Smart-UPS C 1500 is fully operational: battery charge, runtime, load,
voltage, and status all reporting via usbhid-ups + APC HID 0.100 subdriver.
upsd listens on Tailscale (100.121.189.98:3493) for HA polling; local upsmon
provides graceful hypervisor shutdown on battery-low.

Key deployment fix: the udev rule must cover SUBSYSTEM=="usb" (raw
/dev/bus/usb) in addition to hidraw, because usbhid-ups opens the raw USB
device file after dropping to the nut user via setuid(). Matching only hidraw
causes EACCES on driver start.

Tripp Lite UPS (09ae:3016) is blocked — driver matches TrippLite HID 0.85
subdriver but fails reading the 878-byte HID Report Descriptor (EAGAIN via USB
hub). Needs physical reseat to direct motherboard USB port. Driver masked to
prevent restart-loop spam.

Files: ups/discover.sh, ups/setup.sh (idempotent), ups/status.sh, ups/README.md

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 09:34:05 -05:00
mrcharles 0fa0692c37 chore: enforce shellcheck across the repo
Establish shellcheck as a mandatory pre-commit quality gate and bring all 93
shell scripts to a clean state.

- tests/shellcheck.sh: wrapper that runs koalaman/shellcheck:stable via Docker
  (no native binary needed), skips vendored + upstream librenms-agent scripts.
- .shellcheckrc: documents intentional codebase-wide disables (dynamic source
  paths SC1090/SC1091, client-side ssh expansion SC2029).
- AGENTS.md: new Git Policy rule mandating clean shellcheck for every shell
  script before commit.

Fixes applied (real bugs + quality): missing quote in netinfra/gather-configs.sh
(caused cascading parse errors), unquoted expansions, declare-and-assign masking,
egrep -> grep -E, $FUNCNAME array indexing, unused variable removal, cd || exit.
Intentional patterns (sourced config, sysfs/ps diagnostics, ssh heredocs that
expand local config) get justified targeted disables.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 08:56:31 -05:00
mrcharles 54e9927167 docs(status): mark console validation complete with pfv-r2-sw known issue
6/7 consoles validated working via conman from workstation. pfv-r2-sw
(port 2007) produces zero bytes at all baud rates — adapter is present
and correctly mapped but the device is physically unreachable (powered
off, cable disconnected, or console port disabled). Requires onsite
investigation.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-30 08:31:46 -05:00
mrcharles 72ab6b4b32 docs(status): add UPS USB tracing action item
APC + TrippLite UPS units not enumerating on tsys1/tsys4. tsys4 shows no HID
device at all; tsys1 may have USB passed through to pfv-bms. Need to trace
cables and confirm physical connection before NUT driver setup.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 19:53:34 -05:00
mrcharles 25a7234f40 fix(console): switch ser2net accepter to telnet(rfc2217) for proper NVT
The original ser2net config used raw TCP accepters, but conman's
dev="host:port" speaks telnet protocol. This protocol mismatch caused
conman's telnet NVT state machine to strip bare CR characters from
device output — particularly from Dell switches that send \n\r (LF+CR)
line endings — producing stair-stepped and garbled terminal display.

Fix: change ser2net accepter from `tcp,IP,PORT` to
`telnet(rfc2217),tcp,IP,PORT`. With both sides speaking telnet, binary
mode is negotiated and CR/LF translation is handled correctly by the
telnet NVT layer. RFC2217 also enables baud rate negotiation for
future use.

Validated at the byte level: PTY capture confirms all prompts render
cleanly at column 0 with proper CRLF handling across 5+ Enter presses.
All 7 consoles connected, 7 log files actively capturing.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 19:35:05 -05:00
mrcharles 51db52f862 security: redact plaintext password from all files
Replace all occurrences of the fleet admin password with REDACTED_PASSWORD.
Git history will be rewritten next to purge it from all prior commits.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 19:28:22 -05:00
mrcharles 050e235f8b feat(awx): deploy AWX 24.6.1 on tsys-awx via k3s + AWX Operator
AWX (Ansible automation controller) deployed on tsys-awx.knel.net (VMID 600
on pfv-tsys6). Accessible at http://tsys-awx.knel.net:80 (admin/REDACTED_PASSWORD).

Deployment details:
- k3s v1.36.2 single-node (no Traefik) on Debian 13 trixie
- AWX Operator 2.19.1 + AWX CR (LoadBalancer service via k3s ServiceLB)
- PostgreSQL 15 bundled (8 GiB PVC on local-path provisioner)
- VM disk resized 32->60 GB; swap partition converted to swapfile
- Fixed kube-rbac-proxy image (gcr.io removed -> quay.io/brancz replacement)
- Fixed AWX CR field names (postgres_data_volume_size ->
  postgres_storage_requirements)

Scripts: awx/install-k3s.sh, awx/deploy-awx.sh, awx/verify-awx.sh
Manifests: awx/awx-instance.yaml, awx/namespace.yaml
Docs: awx/README.md

All pods running: operator 2/2, postgres 1/1, web 3/3, task 4/4.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 19:12:03 -05:00
mrcharles fa0c77fb58 fix(console): switch conman from ser2net-telnet to direct serial access
The ser2net layer between conman and the serial devices was causing
stair-stepping in terminal output. conman's dev="host:port" uses telnet
protocol, but ser2net's accepter was raw TCP (no telnet mode). The telnet
NVT state machine in conman was stripping bare CR characters from device
output — particularly from Dell switches that use old-style \n\r (LF+CR)
line endings instead of standard \r\n. In telnet, a bare \r not followed
by \n or NUL is non-compliant and gets dropped, leaving bare \n that
causes stair-stepping in the raw-mode terminal.

Fix: conman now opens serial devices directly via the stable udev
symlinks (/dev/consoles/<name>) with seropts, eliminating the telnet
layer entirely. ser2net is stopped and disabled but remains installed
for emergency TCP access (documented workflow: stop conmand, start
ser2net, use telnet, then reverse).

Architecture change:
  Before: device → serial → ser2net (raw TCP) → conman (telnet NVT) → terminal
  After:  device → serial → conman (direct) → terminal

Verified: 7/7 serial devices held by conmand, 7/7 log files capturing,
all \r bytes preserved in terminal output (confirmed via PTY capture).

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 18:29:31 -05:00
mrcharles 80693e4f8e feat(awx): scaffold AWX deployment scripts and manifests
Create top-level awx/ directory with k3s install script, AWX operator +
instance deployment script, namespace manifest, and AWX custom resource
(LoadBalancer service type, bundled PostgreSQL on local-path).

Target: tsys-awx.knel.net (VMID 600 on pfv-tsys6, 4c/12GB/60GB disk).

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 17:45:26 -05:00
mrcharles 396349640f feat(netinfra): migrate DHCP from pfv-netboot to netinfra-01/02 with failover
ISC DHCP server with failover-peer (primary/secondary) deployed on
pfv-netinfra-01 (192.168.3.252) and pfv-netinfra-02 (192.168.3.253). Both
servers in "normal" state, load-balancing 333 active leases.

Migration details:
- Copied all 37 host reservations + subnet/pool config from pfv-netboot
- DHCP lease database copied from netboot for zero-disruption cutover
- DNS servers changed from 192.168.3.250 (netboot) to 252/253 (netinfra pair)
- NTP servers (252/253) added to DHCP options (netboot didn't hand out NTP)
- Netmask on both nodes fixed /24 -> /22 to match the network
- Webmin + DHCP module installed on both nodes (port 10000, SSL)
- pfv-netboot DHCP stopped + disabled
- Tested via sectestbed-sandbox (DHCP lease obtained from 252, verified DNS/NTP/gateway)
- Snapshot "pre-dhcp-migration" on sandbox as rollback point

Configs: netinfra/dhcp/dhcpd-{primary,secondary}.conf
Plan + results: netinfra/dhcp-migration.md

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 17:22:15 -05:00
mrcharles 9c47d93a2f docs(goals): codify local storage policy — ephemeral scratch, not OS drives
Add CAPACITY-GOALS.md §6: local storage is a scratch tier for ephemeral workloads
(container image cache, build artifacts, emptyDir), NOT for VM OS drives (which
stay on NFS where redundancy is handled via 01/02 pairs + cross-disk spreading).
Documents per-host local storage suitability: tsys3/9/2 excellent (NVMe/SSD),
tsys5 usable (HDD bulk), tsys6/7 unusable (USB 2.0 at 30 MB/s). Includes the
future k8s StorageClass mapping (local-fast for scratch, nfs-hdd for persistent).
Adds forward-looking §12 to the audit referencing this policy.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 16:33:56 -05:00
mrcharles edb34a75ec docs(status): sync action items with audit, fix stale disk refs
Rewrite STATUS Pending section to match the audit's phased action plan (§11)
exactly. Remove stale "wnode-tsys6 D5->S4, wnode-tsys7 D5->S1" line (wnode6
already on S4, wnode7 on S3, S1 is not a target). Fix section reference (§12 ->
§11). Correct actual-usage percentages (73%/37%/62%) and CPU idle range (0-5%).
Add D2 IOPS hotspot as a known issue. Fix sectestbed suite count in audit (x9 ->
x10). Verified all VM counts and host data against fresh ground truth.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 16:29:17 -05:00
mrcharles 23e5ecc4cb docs(audit): mark tsys6/tsys9 S1 storage.cfg fixes as resolved
Operator corrected the tsys6 S1 export path (S21 -> S1) and added the missing
S1 entry to tsys9. Verified both via pvesm status: all 8 NFS stores active on
each host. Updated audit findings table, §5.1/§5.2, and Phase 0 checklist.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 16:23:37 -05:00
mrcharles 915111e9dd docs(audit): consolidate to single audit with corrected storage topology
Delete AUDIT-2026-07-28.md (old audit). Rewrite AUDIT-2026-07-29.md as the
sole authoritative audit after reading live /etc/pve/storage.cfg, /etc/exports,
and /etc/fstab on every host.

Key corrections:
- local-nonprod on tsys5 IS the S1 physical disk (dir storage, not separate).
  S1 carries 16 sectestbed VMs, not 0 — it is NOT a migration target.
- storage.cfg is per-host, not cluster-wide — each node defines its own entries.
- BUG: tsys6 S1 export path typo (S21 instead of S1) — S1 broken on tsys6.
- GAP: tsys9 has no S1 defined in storage.cfg.
- Revised disk migration plan avoids S1, targets S2/S3/S4/D5 for IOPS spreading.
- D2 goes 17->5 VMs; S2/S3 absorb the load (7 each); S1 untouched (16 idle VMs).
- Refreshed ground truth: cnode5 deleted, cnode4 stopped (user actions this session).

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
2026-07-29 14:32:43 -05:00