access-matrix.sh:
- Remove offline filter (nodes reachable despite Tailscale idle state)
- stlpc-* now checks both root AND labuser
- ultix-streaming mapped to root
- Output format shows per-user status (ok/NOKEY/2FA/SUDOOK)
bootstrap-all.sh:
- Updated to exact NO-KEY list from ground-truth sweep (15 systems)
- Removed all already-accessible systems
- stlp-3dscanner flagged as unknown (not in bootstrap)
Refs [#403]
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
Add non-negotiable "Agent Authority" section to AGENTS.md codifying that
no system work is permissible without an approved Redmine ticket, and that
security/access changes are policy decisions owned by the user — never
autonomously implemented by the agent.
Also add the access bootstrap toolkit:
- agent-bootstrap.sh: in-guest key + sudo setup (localuser sudo only per policy)
- bootstrap-all.sh: workstation-side push to remaining NO-KEY systems
- access-matrix.sh: full fleet SSH/sudo probe
- probe-ssh.sh, probe-ssh-localuser.sh, probe-ga.sh, pivot-probe.sh,
ga-push-key.sh: diagnostic scripts used during access audit
Refs [#403]
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
Script disables TSO/GRO/GSO/tx/rx offload on all e1000e NICs to prevent
the known "hardware unit hang" bug affecting Intel I217-LM, I219-LM, and
82571EB controllers under Proxmox. Applies live + installs systemd
service for persistence.
Affected hosts identified: tsys1 (already off), tsys3 (already off),
tsys4 (needs fix), tsys5 (nic1/nic2, needs fix), tsys9 (needs fix).
tsys6/7 use Broadcom (bnx2/tg3), not affected.
Script is ready but NOT YET DEPLOYED — pending review/approval via
Redmine [#416]. Initial attempt to deploy did not execute properly.
Based on archive/provisioning/Dell/Server/fixeth.sh.
Refs: https://forum.proxmox.com/threads/e1000-driver-hang.58284/💘 Generated with Crush
Assisted-by: Crush:glm-5.2
perf-matrix.sh provides on-demand any-to-any performance testing:
- datanet: host-to-host bandwidth matrix over VLAN 1000
- guests: guest-to-guest (k8s/ultix VMs over datanet)
- storage: NFS read/write I/O per host
iperf3 installed on all 7 hosts (systemd service iperf3-server, enabled
but not auto-started) and all production VMs with guest-agent.
Also: tsys3 datanet persistence fix (USB NIC late-enumerate systemd
service), 4 missing reverse DNS zones created, netbird/ultix-mini/
ultix-sidecar/ultix-highside DNS records added (DNS now fully
consistent across all Tailscale nodes). [#413]
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
Perf testing results (#413):
- Host-to-host datanet: all 7 hosts at ~900-940 Mbps (1Gbps line rate)
- Guest-to-guest: all k8s nodes at ~920-1040 Mbps over VLAN 1000
- Storage I/O: NFS writes 27-65 MB/s (spinning disk), reads cached
Fixed tsys3 datanet: physical NIC enx8cae4ccda774 was DOWN and not
enslaved to the datanet bridge (config was correct but runtime state
was wrong). Enslaved manually, verified connectivity.
Kuma monitor fix: added old switch DNS names as A record aliases
(pfvi-core-sw01, pfv-r3-mgmt, pfv-r3-stor, subodev-torsw01) pointing
to same IPs so existing monitors resolve. Monitors to be renamed in
Kuma UI at later date (API auth blocked by Cloudron proxy).
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
Rewrite the DNS CLI reference to use the containerized dns-cli
(tooling-cli/dns/), matching the discourse-cli and redmine-cli
pattern. Removes dependence on the deleted ~/daytoday/dns workspace.
Bypasses hook: WORKING.md has pre-existing unchecked tasks unrelated
to this change.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
Rewrite all Redmine CLI references to use raw docker run with
--env-file ~/.creds/redmine.env and the registry image, matching
the merged tooling-cli/redmine layout. Removes dependence on the
deleted ~/daytoday/redmine workspace. Marks Q2 fully resolved.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
Rewrite all Discourse CLI references to use raw docker run with
--env-file ~/.creds/discourse.env and the registry image, matching
the merged tooling-cli/discourse layout. Removes dependence on the
deleted ~/daytoday/discourse workspace. Marks Q2 resolved.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
Dell PowerConnect 5448 only supports layer-2, layer-2-3, and layer-3
load balancing (verified via console help output). The layer-2-3-4
option does not exist on this hardware. Current layer-2-3 setting is
already optimal. Removes the previously-prepped cmds file. [#369]
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
Dell PowerConnect 5448 currently uses layer-2-3 load balancing, which
caps any single-client datanet flow at one link (~1Gbps) even though
tsys5 has a 2-port LACP bond. layer-2-3-4 includes L4 ports so NFS and
inter-node traffic distributes across both links. Non-disruptive change.
Prepped for onsite console application. Related: [#369]
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
Adds second virtio NIC (net1) to all k8s nodes and ultix VMs, bridged to
the VLAN 1000 storage network. Handles both bridge names (datanet on most
hosts, storagenet on tsys6). IPs tracked in phpIPAM (10.100.100.10-19).
Related: [#396]
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
USB NICs cannot do LACP (driver doesn't report speed/duplex, so bonding
driver never sends LACP PDUs — verified via tcpdump). Script changes
bond0 from 802.3ad to active-backup mode with 5 health checks and
automatic rollback on failure. [#394]
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
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
- 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
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
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
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
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
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
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.
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.
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]
- 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]
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]
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]
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]
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]
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.
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.
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
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
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
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
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