Commit Graph

215 Commits

Author SHA1 Message Date
vh e6907819b0 feat(waterland-studio): deploy b72425b — all three upstream findings fixed
One update.sh run on irv-ml1 carried both open upstream PRs, per the
operator's green-light on the job-store fix:

- #5 (464dfc2) declares cupy-cuda12x[ctk] on the gpu extra and takes uv
  out of the render path (sys.executable -m waterland.cli), retiring the
  runtime prune trap at the source.
- #6 (b72425b) rehydrates the job index from the data volume at startup,
  fixing the unbounded store growth reported from this side.

Verified after the update rather than assumed: healthy on backend cupy;
/api/jobs went 1 -> 16 against 16 directories on disk, so API and volume
agree for the first time; nothing wrongly reclaimed, correct since 16 is
under RETAIN=40 and adoption only makes them visible; a real 256^2 plate
render completes warm, so the kernel-cache volume survived the image swap.
A subsequent render took both counts to 17.

The image keeps its explicit [ctk] install and UV_NO_SYNC/UV_OFFLINE pins
even though both are now redundant. The header requirement is a property
of this slim base, not of the upstream extra, and the cost is measured
rather than assumed: uv sync satisfies it first, so the line reports
"Audited 1 package" and adds 0.3s to the build. The env pins are now
cheap defence-in-depth against any future path that re-enters uv.

Docs corrected in place: the README's upstream-finding section is now a
resolved-finding record, and the two "bounded ~500 MB" claims say which
commit made that bound hold across restarts rather than only within a
process. Comment-side changes pushed to the live compose dir; no restart
was needed for them.
2026-08-19 01:39:13 -07:00
vh a2b6bf409e memory: waterland-studio upstream fixes landed, container stays pinned
waterland-dev merged PR #5 (main now 464dfc2), fixing both landmines at
source: the gpu extra declares cupy-cuda12x[ctk], and the renderer spawns
sys.executable -m waterland.cli instead of re-entering uv mid-job.

The running container deliberately stays on 8025366. Its own [ctk] install
and UV_NO_SYNC/UV_OFFLINE pins already neutralise both defects, so a rebuild
would buy reliability that is already present — and the project is in
wind-down. Both guards are kept rather than dropped: the header requirement
is a property of this slim image, not of the upstream extra, and the uv pins
are now cheap defence-in-depth against any future path that re-enters uv.

Also records waterland-dev's confirmation of the unbounded job-store growth
and the operator's green-light on their startup-rehydrate fix. That PR
merging is the rebuild trigger: one update.sh run lands the rehydrate and
464dfc2 together. Marks the inbox drained.
2026-08-19 01:26:32 -07:00
vh bc3aada73a memory: snapshot — .internal DNS live, waterland containerised, homepage themed
Captures a long infra session: fleet *.internal DNS (git-sourced, 42 names,
three resolvers including a new colo one), waterland studio containerised on
irv-ml1, Homepage cleaned up and themed with Australis Skyfall over an
Arbo-generated background, and four unmanaged stacks adopted into stacks/.

Four detail files added. Auto-archived 4 entries to archival-memory.md
(Recent decisions 2, Tried and abandoned 2); 5 held back by the open-deferred
guard rather than moved.

Also records three operator-owned open items: the colo DNS repoint, the
static-v6 convention, and the deliberately belayed AI-tab Dormant regrouping.
2026-08-19 01:20:53 -07:00
vh 084ad924f0 memory: snapshot — ESH fiber live, esh-pve-nas migrated+patched, v6 mapped
Session captured: ESH cut over to Cityside 2Gb symmetric fiber and was
fully provisioned on it; esh-pve-nas completed its ZFS-root migration off
the USB DOM and took its 225-package security backlog with the reboot
deferred; ESH<->colo IPsec was rebuilt as a dialup tunnel with NAT-T after
CGNAT broke the statically-pinned one; IPv6 was mapped across all three
sites.

Auto-archival fired at the soft cap: 7 entries moved to
archival-memory.md (Recent decisions 3, Tried and abandoned 4), all
verified-complete arcs, with two detail files moved and removed. The
remaining pre-Aug-05 entries were held back by the open-deferred-work
guard, so the index stays slightly over cap at 313 lines rather than
losing a live pointer.

Also records the one self-inflicted outage of the session (missing
--make-rslave on a chroot rbind) and that three long-dead things
surfaced incidentally: pvestatd down 82 days, a vzdump hung 126 days,
and a VM sitting in prelaunch for four months.
2026-08-18 16:36:13 -07:00
vh 34d3f42bf5 docs: park the BGW210 v6 work pending the Device Access Code
Operator will retrieve the BGW210 Device Access Code from the NH3 office
and vault it, after which the IPv6 LAN settings page can be driven
remotely.

Parked on the henge as reclaim-nh3-s-7-unclaimed-ipv6-64s-from-the with
everything needed to resume cold: the verified facts about the /60 split
and the seven unclaimed prefixes, why AT&T cannot fix it, the exact page
to start at, what to look for in priority order, the other settings
pages behind the same login, and the wpa_supplicant fallback with its
warning about modifying NH3's only uplink.

Suggested vault path unifi/bgw210-nh3-device-access-code, matching the
existing unifi/* credentials.
2026-08-18 16:29:39 -07:00
vh 57e080319b docs: NH3 v6 root cause is the BGW210, and seven /64s are unclaimed
Operator suggested checking the BGW on its 192.x management address,
which turned out to give the whole picture from unauthenticated status
pages.

The CPE is a BGW210-700 on firmware 4.28.7 at 192.168.1.254. AT&T does
hand it a /60 -- c110 through c11f. The BGW keeps c110-c117 for itself
and re-delegates up to eight individual /64s on c118-c11f, top-down. Our
UDM holds c11f, delegation number eight.

So the earlier conclusion that AT&T only grants a /64 was right about
the symptom and wrong about the cause. Seven further /64s are available
and simply never solicited, because UniFi exposes a single
wan_dhcpv6_pd_size integer with no field for how many prefixes to
request. The documented workaround is repeated -P flags to dhclient,
which the UniFi UI cannot express.

This also settles that an AT&T ticket cannot help: the rationing is CPE
firmware behaviour, not provisioning. Records the two real options --
accept one /64, or bypass the BGW entirely with wpa_supplicant EAP-TLS
on the UDM to negotiate the full /60 -- with the warning that the latter
modifies NH3's only uplink and needs a planned window.
2026-08-18 16:27:09 -07:00
vh 1b6c26ce58 docs: AT&T PD is a hard /64 at NH3, tested on the wire
AT&T support guessed 'I do not believe att will do that' from a DNS
provisioning desk. The guess was correct, but it needed proving rather
than accepting, so the UDM solicited DHCPv6-PD at /48, /56 and /60. All
three returned the same single /64. This is not a case of nobody having
asked -- the ask was made three ways.

Proven by temporarily enabling PD on nh3-iot, the only NH3 VLAN with
zero clients, then setting ipv6_pd_prefixid to 0, 15 and 16. All three
returned an identical c11f prefix, which only happens when exactly one
/64 is delegated; with a larger block the prefix-id moves the LAN within
it.

Records a mistake worth not repeating: I first read the gap between the
WAN address (c110) and the delegated prefix (c11f) as evidence of a /60.
It is not -- AT&T assigns those from different parts of their pool and
the spread means nothing.

NH3 was fully restored afterwards, with rollback artifacts kept. Also
captures the concrete ask for AT&T Business, phrased as something their
provisioning team can verify against their own DHCPv6 logs, and the
consequence if refused: NH3 can host exactly one v6 segment against
ESH's 256.
2026-08-18 14:41:29 -07:00
vh fddf7f587f docs: record the pending Cogent IPv6 provisioning request for the colo
Operator opened a ticket with Cogent for v6 at Anaheim, which closes the
one thing tonight's investigation could not resolve from our side.

Captures the diagnosis so the ticket has evidence behind it: a single RA
in 90 seconds of sniffing wan1, from fe80::ea0a:b9ff:fe3b:2c16, proving
an IPv6-capable router sits one hop away on the circuit terminating
38.120.12.42/29 -- but SLAAC obtained no global address across multiple
RA intervals and ping6 to Cloudflare and Google both returned 100% loss.
Router present, circuit unprovisioned.

Also records that the FortiGate v6 config was fully reverted after
testing, the FortiOS gotcha that SLAAC is 'set autoconf enable' rather
than an ip6-mode, and the ask to make when it lands: a /56 or better,
since NH3 only receives a single /64 from AT&T.

Notes the consequence worth planning around -- once provisioned, the
colo becomes the only site with both a static public v4 and routable
v6, which makes it the natural v6 hub given ESH is CGNAT'd and NH3 is
prefix-constrained.
2026-08-18 14:20:15 -07:00
vh 707a8cbcce docs: correct addressable vs reachable in the ESH v6 entry
I wrote that enabling SLAAC would give LAN devices 'globally reachable
addresses'. Wrong word, and the wrong word in a persistent-memory entry
a future session inherits as fact.

Addressable is a property of the address. Reachable is a policy decision
the firewall makes. v6 removes NAT; it does not remove the firewall, and
treating those as the same thing is exactly how v6 gets mischaracterised
as automatic exposure.

Records the operator's position while correcting it: no 1:1 inbound
pass-through. The pending firewall-policy pass is about writing explicit
default-deny inbound rules per v6 segment, not about deciding what to
expose.
2026-08-18 13:39:40 -07:00
vh 8be8a51437 docs: re-gloss esh-iot, re-spell esh-mgmt
esh-iot keeps the identical eight digits -- 4DBAD107, rendering
4dba:d107 -- and only the reading changes: 4 is FOR rather than A, so it
parses 'FOR DA BAD IOT', which describes what the segment is actually
for.

esh-mgmt genuinely changes: 115D:B055 becomes 15DA:B055. The leading I
is dropped and DA is spelled in full, giving 'IS DA BOSS' with the
network as subject rather than speaker. Still eight digits. DA written
out needs no substitution since D and A are both native hex; spelling it
as a single D the way esh-iot does would have yielded seven digits and
broken the house pattern.
2026-08-18 13:33:12 -07:00
vh 18c683b399 docs: reserve 4411:DBAD for a future DMZ
'FOR ALL DA BAD' -- 4=FOR, 411=ALL, D=DA, BAD=BAD. Eight digits, house
style, renders 4411:dbad.

No DMZ network exists on the ESH UDM today; this is a name claimed
against the day one is built. Pairs deliberately with esh-iot's
4DBA:D107 -- IoT is 'a da bad', the DMZ is 'for all da bad', which is
the correct relationship between the two segments.
2026-08-18 13:31:02 -07:00
vh 959bb6ee05 docs: server network gets 4411:B105 -- ESH v6 naming scheme complete
'FOR ALL BIOS' -- 4=FOR, 411=ALL, B105=BIOS. Eight digits like the rest.

Completes the set. All six ESH networks now carry an 8-hex-digit phrase
in a consistent first-person/declarative voice:

  Default       4BA5:3417   A BASE FOR IT
  esh-mgmt      115D:B055   I IS DA BOSS
  esh-server    4411:B105   FOR ALL BIOS
  esh-userland  CAFE:4411   CAFE FOR ALL
  esh-iot       4DBA:D107   A DA BAD IOT
  esh-cameras   1533:FACE5  I SEE FACES

Still a documentation convention rather than wire-level configuration --
UniFi has no static-v6 client assignment and the gateway address is
platform-fixed -- but these are the values to use whenever ESH LAN v6 is
switched on and hosts get hand-assigned addresses.
2026-08-18 13:30:27 -07:00
vh a264e001ae docs: default network settles on 4BA5:3417
Same phrase, 'A BASE FOR IT', but written as a plain 8-digit string
rather than forcing the article into its own group. 4=A, BA53=BASE,
4=FOR, 17=IT renders as 4ba5:3417 -- two groups, matching every other
network in the scheme, with the words straddling the colon exactly the
way 4DBA:D107 does.

Corrects the previous commit, which claimed this needed nine digits and
a third group. It is eight, and always was.
2026-08-18 13:27:12 -07:00
vh e5bba048c8 docs: revise default network to A:BA53:0417
'A BASE FOR IT' -- the article makes it a full sentence, matching the
voice of the other five. BA53 uses 3=E rather than the 5E spelling used
in the previous BA5E version.

Nine hex digits rather than eight, so unlike the others it does not fit
two groups and renders across three as a:ba53:0417.
2026-08-18 13:26:23 -07:00
vh 309a240fa8 docs: default network gets BA5E:0417
'BASE FOR IT' -- BA5E=BASE, 4=FOR, 17=IT. The foundation segment
everything else hangs off, which is what the default network is, and it
doubles as 'base for IT'.

Note the trailing group zero-pads: it renders as ba5e:0417, not
ba5e:417.
2026-08-18 13:25:36 -07:00
vh e58cfde7fd docs: userland network gets CAFE:4411
'CAFE FOR ALL' -- CAFE, 4=FOR, 411=ALL. Eight digits like the others,
and the only one so far that splits on its own phrase boundary, so it
renders legibly as cafe:4411.

Bonus reading: 411 is US directory assistance, which is a fitting second
joke for the segment the humans actually live on.
2026-08-18 13:24:04 -07:00
vh ab8481907d docs: iot network gets 4DBAD107
'A DA BAD IOT' -- 4=A, D=DA, BAD=BAD, 107=IOT. Eight hex digits to match
the cameras and mgmt picks.

Worth noting it renders as 4dba:d107, so unlike the other two the phrase
does not split on its word boundaries and reads as noise unless you know
it is there -- which is arguably right for the untrusted segment.
2026-08-18 13:22:22 -07:00
vh 805fa6ff22 docs: mgmt network gets 115D:B055
'I IS DA BOSS' -- 1=I, 15=IS, D=DA, B055=BOSS. Eight hex digits like
the cameras pick, so it renders as 2607:73c0:402:1d??::115d:b055 with
the same two-group split and room for host numbering.

Pairs structurally with 1533:FACE5 on cameras: both eight digits, both
first-person, and the network that is actually in charge gets to say so
to the one that is merely watching.
2026-08-18 13:18:32 -07:00
vh 35e7ecbadb docs: cameras network gets 1533:FACE5
'I SEE FACES' -- 1->I, 5->S, 3->E, 3->E then FACES. Eight hex digits
splitting cleanly across two groups, so it renders as
2607:73c0:402:1d00::1533:face5 with room left for host numbering.

Still a documentation convention rather than anything on the wire, per
the constraints recorded in the same entry, but this one is good enough
that it should survive to whenever ESH LAN v6 actually gets switched on.
2026-08-18 13:16:56 -07:00
vh fe3d765873 docs: record the ESH IPv6 naming scheme as a docs convention, not wire-level
Picked six hexspeak names for the ESH LANs during a wind-down moment
(FACE/B055/B105/CAFE/DEAD/BASE), then checked whether any of it could
actually land on the wire before implementing anything.

It can't, for three independent reasons: a network's only nameable slot
is its /64 prefix id, which is 2 hex digits and can't spell a 4-char
word; the gateway's own address is fixed at ::1 by the UniFi platform
with no field to override it; and UniFi has no IPv6 equivalent of
use_fixedip/fixed_ip, confirmed directly against the client schema, so
individual devices can't be pinned to a chosen v6 address either --
SLAAC devices self-assign via EUI-64 or privacy extension.

So this stays a documentation mnemonic. Recorded as such rather than
implied as something live, since I'd already started suggesting a
static-camera-assignment plan that the schema check ruled out.
2026-08-18 11:09:14 -07:00
vh 50d13f57cb docs: park the ipsec_local_ip watcher until ESH fiber is up
The ESH<->colo tunnel is restored and the FortiGate end is permanently
address-agnostic, but the UniFi end still needs a literal ipsec_local_ip
and so drops on any ESH WAN change -- Cox reclaiming WAN1, the fiber
cutover, or a DHCP renewal.

Operator's call not to build the self-healing watcher yet, which is
right: it would be written against the 5G failover address, which is
about to be replaced, and the fiber may reshape the topology anyway.
Parked as self-healing-ipsec-local-ip-watcher-for-the-esh with the
trigger recorded, plus the follow-up to retire the old ana-to-eshudm
tunnel whose distance-10 route would otherwise reclaim traffic if Cox
returned on the old address.

Records the manual stopgap in persistent memory so the gap is cheap to
cover by hand in the meantime: read wan_ip from the UDM's health
endpoint, PUT it into esh-ana.ipsec_local_ip.
2026-08-18 08:21:03 -07:00
vh 8a742f59b8 fix(ana-gw): restore ESH<->colo IPsec as a dialup tunnel with NAT-T
The link died when ESH lost its public IP during the fiber cutover. Two
independent causes, and the second would have defeated the obvious fix:

- phase1 ana-to-eshudm was type static, pinned to 70.181.90.232, an
  address that no longer exists.
- nattraversal was disable, so ESP could not have crossed NAT even with
  the peer IP corrected. pfi-ana-nh3 shares that setting and survives
  only because NH3 is publicly addressed, which is why the two tunnels
  diverged.

FortiOS refuses `set type dynamic` on an existing tunnel -- "Cannot
change tunnel type once configured" -- and rolled back cleanly, so the
fix could not be an edit. Rather than delete and recreate, which
cascades into the phase2, two static routes and ten policies, the
replacement was built alongside: new phase1+phase2 ana-eshudm-dyn
(type dynamic, ikev2, aes256-sha1, dh14, NAT-T on, PSK read from the ESH
UDM API so neither side needed a new key), static route id 10 at
distance 20, and two consolidated multi-zone policies 73/74. The old
tunnel is left in place, dead and harmless, as rollback.

Verified up: ana-eshudm-dyn_0 97.170.236.56:4500 selectors 1/1 -- the _0
suffix is a dialup child, :4500 is NAT-T, and the address is the
carrier's, which is precisely what could never have been pinned. ESH
reaches all four colo hosts at 40-56ms, the colo reaches all three ESH
hosts, and traceroute drops from eight hops leaking into the carrier
network to three hops fully encapsulated.

Config was backed up before any write (1.17MB, 36903 lines, off-box).

Residual fragility recorded: the UDM's ipsec_local_ip demands a literal
address -- empty is rejected as api.err.InvalidPayload -- so it still
needs updating when the fiber changes ESH's WAN address. The gateway end
is now address-agnostic; the UniFi end is not.
2026-08-18 08:12:35 -07:00
vh 9407e7f144 docs: correct the persistent-memory IPv6 entry to match the evidence
The prior edit missed its anchor and left the over-broad version in
place. The entry now separates the two inter-site links rather than
treating them as one: Site Magic (WireGuard, NH3<->ESH) survives
arbitrary NAT and is proven to; IPsec (colo<->ESH via ana-gw) does not
and is currently down, with traffic leaking unencapsulated to the
carrier. IPv6 keeps its justification on the IPsec link specifically.
2026-08-18 07:47:33 -07:00
vh 78cc760ef6 docs: refute the CGNAT-breaks-Site-Magic premise with a live test
The fleet IPv6 work was justified primarily by the expectation that ESH
fiber landing behind CGNAT would break Site Magic on IPv4, making v6 the
escape hatch. The fiber cutover provided a free natural experiment and
the premise does not hold.

Cox was unplugged, ESH failed over to the 5G WAN (already configured
failover-only, so this needed no intervention), and the resulting WAN
address is 192.168.200.111 -- RFC1918, double-NAT, no inbound path at
all, which is strictly worse than the CGNAT that was feared. Site Magic
stayed up throughout: all four ESH hosts reachable, ssh and command exec
working, 20MB pulled over the tunnel, latency 15ms -> ~46ms as expected
for cable to 5G.

The mechanism is visible on the device: magic_site_to_site_vpn holds
only `enabled` plus a WireGuard keypair, with peer orchestration in the
UniFi cloud and no WAN binding of any kind. NH3's edge is publicly
reachable, so the NAT'd side dials out and never needs reachability.

Consequence: no addressing outcome on the new fiber -- public, CGNAT or
double-NAT -- threatens the inter-site tunnel. IPv6 stays worth doing on
its own merits but stops being urgent, and stops gating anything.

Also worth recording that Site Magic cannot be pinned to a WAN. It rides
whichever uplink is active, so the only lever is failover priority --
which moves all site traffic, not just the tunnel. The existing
failover-only config on WAN2 already handles a primary-WAN outage
correctly and needed no change.
2026-08-18 07:44:06 -07:00
vh 668b63a398 feat(esh-pve-nas): install the 225-package backlog; reboot deferred
pve-manager 8.4.11 -> 8.4.20, corosync 3.1.9 -> 3.1.10-pve2, and kernel
6.8.12-42 staged on the /boot LV. dpkg clean, nothing outstanding for
apt -f install, all PVE services active, cluster quorate, no unapplied
conffiles. Reboot deliberately deferred at operator request, so the host
still runs 6.8.12-13 until a chosen window.

This validates the GRUB fix from 061c4b7 under the exact condition it
was written for. update-grub regenerated entries for the new kernel and
entry 0 -- what GRUB_DEFAULT=0 selects -- is now
/vmlinuz-6.8.12-42-pve with root=ZFS=nvme/ROOT/pve-1, supplied by the
grub.d drop-in since grub-mkconfig cannot derive the pool name itself.
The old kernel keeps correct entries as a fallback and the ext4 rollback
entry is untouched. Had the fix not landed first, saved_entry would
still be pinned to 6.8.12-13 and the host would boot the old kernel
indefinitely -- 161 security updates installed and never run.

/boot holds both kernel sets at 176M used of 488M, confirming the 512M
LV carved out of swap was sized correctly.

Adds a ZFS snapshot step to the upgrade playbook, taken automatically on
ZFS-root nodes before any package lands. That is the first real use of
the boot-environment upside the migration was meant to unlock: rollback
for this upgrade is now `zfs rollback -r
nvme/ROOT/pve-1@pre-upgrade-20260818T141652Z && reboot` rather than
archaeology in dpkg. Also documents that the corosync bump restarts
corosync mid-upgrade, which on a 2-node cluster is a brief quorum event.
2026-08-18 07:21:40 -07:00
vh 5f11d1b3cb feat(esh-pve-nas): cut PVE root over to ZFS on the mirrored NVMe
Root is now nvme/ROOT/pve-1. The USB DOM keeps the ESP and /boot but is
out of the runtime I/O path, so a bus reset can no longer drop root from
under a running hypervisor. All five guests healthy, three pools ONLINE,
system running, ext4 pve-root intact and unmounted as the rollback with
its own kernel and initrd. zfs-import-cache is now the active import
path -- the all-three-pools cachefile fix doing its job.

The window cost an unplanned outage, and the cause was this repo's own
tooling rather than the migration.

The staging chroot ran `mount --rbind /dev` and /sys with no
--make-rslave. On systemd `/` has shared propagation, so the cutover's
`umount -R` propagated back into the live host and removed the real
/sys/fs/cgroup, /dev/pts and /dev/shm. With cgroup2 gone systemd-logind
could not create a session: ping fine, TCP fine, SSH authentication
succeeded, resident daemons kept serving -- and every new exec hung,
including /sbin/reboot, so the reboot never ran at all.

It impersonates failing root-disk I/O almost perfectly, and I called it
as the DOM dying. That was wrong. dmesg had the answer throughout: the
DOM attached cleanly with no errors, and the last log timestamp was
12114881s -- 140 days -- meaning this was still the original boot. A
down-detector had also never reported the host down, which I read as a
fast reboot rather than as no reboot.

Fixes and guards:

- --make-rslave after every rbind, plus a guard that refuses to proceed
  while any chroot bind still reports shared propagation.
- Confirm a reboot by observing the host DOWN, not by watching for it to
  come back. Those two states are indistinguishable otherwise.
- Blast radius now measured from the server: `ss` inside CT 103 found
  five NFS clients, not the two documented. The new one that mattered is
  esh-vm-db, hard-mounted and unreachable by ssh. Left mounted on
  purpose and it came through read-write.
- grub-reboot's one-shot does NOT work here: grubenv sits on an LVM LV
  which GRUB reads but cannot write, so next_entry survived the boot
  that consumed it. Steady state is saved_entry=pve-zfs-root with no
  next_entry. There is no auto-fallback on this host and no IPMI.

Recovery needed no console: an idempotent cgroup2/devpts/shm remount
landed in the brief windows where exec succeeded. No data was lost, and
neither the DOM nor any pool was ever at risk.
2026-08-18 06:39:34 -07:00
vh c4b2278e7d feat(esh-pve-nas): stage the PVE root migration off the USB DOM
Everything but the reboot. Two rerunnable elway playbooks; the host is
still running from the ext4 root and its boot path is byte-identical to
the last 140 days, because grub-install is deliberately held back to the
cutover window.

Phase 1 (esh-pve-nas-stage-zfs-root.yaml): carve a 512 MB /boot LV out
of the 768 MB swap LV, populate it, rsync the 4.3 GB ext4 root into
nvme/ROOT/pve-1, write the copy's fstab.

Phase 2 (esh-pve-nas-stage-bootloader.yaml): ZFS initramfs, grub.cfg,
explicit pve-zfs-root and pve-ext4-rollback entries with stable ids,
grubenv pinned to the rollback so cutover's grub-reboot is a one-shot.

Three landmines the plan did not predict, all caught by verify steps
asserting effective state rather than by reading the plan:

- The /boot LV had nowhere to live. VG pve had 4 MB free and mounted
  ext4 cannot shrink; freeing space from root needs a rescue boot, which
  costs the one-reboot property. Space came from swap (768M -> 256M).

- The runbook's `zpool set cachefile=... nvme` would have broken the
  NAS. Populating a cachefile flips the host from import-by-scan to
  import-by-cache, so a one-pool cache leaves ssd and tank unimported --
  and CT 103 esh-nas has twelve bind mounts spanning all three pools.
  Set on all three instead, verified in the resulting cache.

- update-grub silently emitted a pool-less root=ZFS=/ROOT/pve-1, which
  boots to an initramfs prompt. Debian's 10_linux builds ${rpool}${bootfs}
  and rpool comes from grub-probe --target=fs_label, which returns empty
  because GRUB's ZFS reader cannot open a pool with encryption,
  large_dnode and zstd_compress -- the same feature set that forced /boot
  to stay ext4. The probe failure is swallowed by `2>/dev/null || true`.
  Fixed with a /etc/default/grub.d drop-in plus explicit menu entries.

The transferable lesson: the original verify grepped for the correct
root= string appearing somewhere in grub.cfg, which passes while every
menu entry is still broken. Assert the effective value, not the presence
of a substring.
2026-08-17 22:11:14 -07:00
vh 637ed3bd89 memory: Lobe TTS fixed by aliasing stock OpenAI model names at the gateway
Lobe's TTS had never worked. It sends model:"tts-1" and LiteLLM resolves the
model name before routing, so it 403'd against the scoped key's allow-list and
never reached :8198 -- our belief that an unknown model routes to the gateway
default was true of the gateway and false of the LiteLLM path, which is what
hid it.

Fixed at the gateway rather than the client: tts-1, tts-1-hd and gpt-4o-mini-tts
aliased to the same upstream as ext-tts, and added to the lobe-chat-esh
allow-list. Verified with Lobe's exact payload on Lobe's own key. The obsolete
'one-time human UI pass' follow-up is dropped.

Banks two durable facts: those aliases are independent DB rows that must move if
ext-tts repoints, and the infra-ops key has admin rights for /model/new and
/key/update so this class of work does not need sk-corvid.
2026-08-17 21:47:00 -07:00
vh 356752d99c memory: snapshot — heresy gen seat live, irv-ml1 cleared, homepage repo'd, esh-pve-nas DOM planned 2026-08-17 21:44:29 -07:00
vh 4b6daadb16 memory: operator confirms the heresy gen seat working well in real use
Records the one signal the synthetic gates cannot provide -- multi-turn
degeneration is stochastic and invisible to probes, and four synthetic tests
once validated three non-fixes on this exact seat.

Not yet the 60k-token bar the prior seat cleared, so the rollback weights
stay in place.
2026-08-17 20:19:18 -07:00
vh d676a1375b memory: watch DavidAU's heretic Qwen3.8, not Cold-Fusion-GAIN V1.1
Cold-Fusion-GAIN V1.1 examined and not adopted -- it is a capability
finetune of stock Qwen3.8 and every bench row is labelled [non heretic], so
adopting it would reintroduce base refusals the current seat does not have.

Records why it reads as uncensored at a glance: DavidAU's back catalog is
almost entirely Uncensored-Heretic builds, so the naming pattern implies it.
The heretic stage for this one is still in progress from base, and that is
the release worth watching.

Also banks what makes it interesting when the heretic build lands -- real
third-party benchmark gains over stock, claimed MTP acceptance well above
ours, thinking tokens cut to a fraction -- and the two caveats: the MTP
numbers are GGUF/llama.cpp not vLLM, and a trained MTP head means the free
CPU-hash gate would not apply.

Also drops the now-stale 'primary until the DavidAU Qwen3.8 lands' clause
from the superseded seat entry.
2026-08-17 19:26:15 -07:00
vh 11b688ff68 feat(gen-seat): promote absolute-heresy to the live gen seat
MuXodious/Qwen3.8-27B-absolute-heresy (Heretic v1.4.0 + SOMPOA, trial T377,
pin c2374593) quantized through our mixed NVFP4+FP8 recipe and promoted after
passing the full gate on the probe port.

Gate vs incumbent -- MTP acceptance 47.2% (48.2%), decode 103.5 tok/s (96.4),
prefill 6618/5403 at 6.7k/27k (6334/5085), TTFT 27k 5.00s (5.31s), perplexity
6.910 (7.059, 2.1% better), surface 6/6, abliteration compliance 4/4. On our
battery-instruct arm -- the framing that actually elicits refusals -- 0/55 with
zero EMPTY, so no catatonia at the hard edge.

Speed deltas are image-confounded: the probe ran the seat's pinned nightly
while the incumbent's stored numbers came from an earlier image. Read as not
worse. Acceptance, perplexity, surface and refusal are apples-to-apples.

All 7 LiteLLM aliases verified end-to-end. GPU0 at 91.3/97.9 GB with meromero
healthy -- more headroom than the previous build. Incumbent weights untouched
and .env.bak-heresy-20260817 in place for rollback.

Candidate is a 2-day-old RC1 with ~348 downloads; watch real multi-turn use.
2026-08-17 17:23:35 -07:00
vh c18f5c5d33 memory: WT #401 closed on demo verify; host ulimit floor staged not active
worldtree-dev closed #401 on our demo verification. Records the two-layer
state (their e41b139 compose pin verified on demo, covered-not-verified on
personal/pinned; our daemon floor staged), the measured fact that
default-ulimits is not SIGHUP-reloadable on Docker 29.4.3, the explicit
no-dockerd-restart decision, live-restore parked as a separate call, and
the one ping we still owe once worldtree-personal recreates.
2026-08-17 16:04:08 -07:00
vh 2686042106 memory: fleet IPv6 state + verified VPN topology; ana-wg key material locked down
Durable capture ahead of the ESH fiber install (2026-08-18) that puts the
house behind CGNAT and breaks Site Magic on IPv4 -- IPv6 becomes the
escape hatch and the likely first consumer of fleet v6.

Topology verified rather than assumed: Site Magic between UniFi units,
IPsec IKEv2 colo<->UniFi, and WireGuard as a remote-access convention
only, host-based on ana-wg behind a FortiGate UDP VIP. The FortiGate
port-forwards and never terminates WireGuard, so FortiOS 7.2's lack of
native WG is a non-issue.

IPv6 today: NH3 WAN live, colo and ESH none. AT&T delegates exactly one
/64 at NH3 -- established by forcing the prefix ID from auto to 0 and
observing the subnet not move, since the c110/c11f pattern otherwise
reads as a /60. PD enabled on nh3-iot to measure, then reverted; all
five NH3 LANs are back to ipv6_interface_type=none.

Also fixed on ana-wg: wg0.conf, keys/*_priv, keys/*_psk and the client
configs were mode 644 with private key material in them. Now 600, with
keys/ and configs/ at 700. wg-quick@wg0 stayed active, three peers
intact.

Corrects two stale in-flight rows: the DS regeneration is retired, not
queued, and SPEC-ds-regeneration.md is deleted rather than untracked.
2026-08-17 13:50:34 -07:00
vh 0b32b112bd memory: snapshot for /clear — gen-seat saga closed, Lobe live, litellm upgraded
Refreshed Current state (dropped superseded gen-seat history now covered by
the RESOLVED entry + playbook 3.8; added Lobe Chat, litellm upgrade+cap,
updated follow-ups). Logged 4 new Recent decisions (gen-seat two-cause
resolution + the synthetic-probe-validated-3-non-fixes meta-lesson, Lobe
stand-up, litellm upgrade, abliteration-catatonia). Handoff at
/tmp/infra-ops-handoff.md. Index 284 lines, no archival.
2026-08-17 02:23:59 -07:00
vh 2185964a6a memory: gen seat RESOLVED — mixed FP8-attn build on pinned nightly, coherent through 60k
Supersedes the stale 'GEN SEAT = AEON' current-state line. Records the
final resolved config (JonathanColetti/Heretic mixed NVFP4+FP8 on pinned
vLLM nightly, MTP on), the two-real-causes root cause, the AEON purge, and
the pinned-nightly follow-up (move to stable once #51113 ships). A cold
session now reads the correct gen-seat state.
2026-08-17 02:13:42 -07:00
vh aba7cda33e fix(refusal-probe): retract an invalid cross-battery comparison; adopt the two-control pair
RETRACTION. I claimed my arm-3 92.5% 'exceeded' brokkr-smithy-dev's 62.5%.
That comparison was invalid. His diff against his own artifact shows my
battery-instruct.yaml reproduces only his CREATIVE class -- 8 of 16 axes --
dropping all 5 operational (violence/incite, crime/fraud, cyber/malware,
selfharm/methods, privacy/stalk) and all 3 meta (meta/sysprompt,
meta/ignore, meta/dan), while adding 2 controls he never had, at k=5
against his k=2. His figure pools 16 axes; mine is creative-only. Different
denominators, so there was never a delta to report.

Cause: I rebuilt his instrument from his MESSAGE, and the class field lives
in the artifact, not the prose. Lesson recorded -- reconstructing a peer's
instrument from their description reproduces what they described, not what
they ran; diff against the artifact before claiming comparability.

Adopts his amendment to my control fix, which is better than my proposal. I
argued a single UNWRAPPED control suffices since over-fire is a property of
the scorer, not the wrapper. True but incomplete: one control gives only a
scorer FPR baseline and cannot attribute anything to the wrapper. Two -- the
same benign scenario unwrapped AND wrapped -- make the wrapper the only
variable, so the delta between them is the wrapper's contribution to
classifier firing. Costs one prompt.

ictrl-reunion stays contaminated and unchanged, now with an explicit
do-not-fix marker: rewriting it would break comparability with results
already collected, and it is the cleanest live demonstration of the very
confound this battery measures.
2026-08-16 16:45:17 -07:00
vh 766c65801c memory: AEON accepted on the gen seat; canonical sampling; four-wrong-diagnoses lesson
Gen seat state, the abliteration catatonia signature, canonical Qwen3.8
sampling with the wrong-mode presence_penalty fix, the two real
gateway-chat defects, and the single-file bind-mount inode trap.

Also records the methodology failure honestly: four disproved hypotheses
on one bug, caused by a harness that varied the QUESTION along with the
conversation depth, so a narrower question drawing a shorter answer read
as degeneration. Banked as rules -- hold the final question fixed when
comparing across depth, do not infer trends from n=3 when identical
inputs span 25-465 words, and ask for the real failing transcript before
building a synthetic reproduction.
2026-08-16 16:31:24 -07:00
vh 0b95701173 memory: snapshot — FF A/B result, seat state, Qwen-thinking finding, esh hardening
Supersedes the 'blocked on a reproducing battery' entry: brokkr supplied
the framing, the A/B ran, and the refusal question is settled.

Most load-bearing fact for a cold session: Dark-Scarlett is DOWN and
Fable-Fusion is serving char-rp-reasoning on an evaluation window with no
permanent decision taken. Rollback recorded inline.
2026-08-16 10:29:39 -07:00
vh 09c56d51a9 memory: DS-replacement eval blocked on a reproducing battery; refusal-measurement lessons
Records the open loop with brokkr-smithy-dev (01M05KQAD55PYHGCP668AV06YR)
and three durable lessons: benign controls cannot validate a refusal
classifier on in-character RP prose, a 0% rate needs a classifier
self-test before it is believed, and an approved plan whose premise the
evidence invalidates should be surfaced rather than spent.
2026-08-16 08:41:57 -07:00
vh f83456a276 memory: snapshot for /clear — auto-archived 16 entries (Recent decisions 13, Tried and abandoned 3); index 303 -> 254 2026-08-15 19:55:40 -07:00
vh 4e74e0aefe memory: RP-seat direction — keep MeroMero, reject Artemis, Dark-Scarlett 3.8 queued as the MTP-restoring move 2026-08-15 19:50:11 -07:00
vh f338f228a6 memory: record the quantization playbook as the durable home for quant lessons 2026-08-15 10:02:24 -07:00
vh b9da05aeb2 memory: snapshot — overnight arc pushed; operator ruled the +1.7% PPL an acceptable trade (settled) 2026-08-15 08:14:05 -07:00
vh 930197a56a memory: record prefill result (roughly doubled) on the gen-seat mixed requant 2026-08-15 02:34:04 -07:00
vh fa4f652a39 memory: snapshot — gen seat mixed NVFP4+FP8 requant (+18%) + char-rp tool parser; both queued items closed 2026-08-15 02:22:58 -07:00
vh b1370e4b4d memory: snapshot — uncensored gen seat landed (Qwen3.8-27B-Uncensored, gen-seat/vllm-gen); next = chase W4A8 2026-08-15 00:32:50 -07:00
vh bf915e15f0 memory: snapshot — eRP dual-seat overhaul landed (MeroMero + Dark-Scarlett @ 256K); next = infra+access 2026-08-12 14:33:53 -07:00
vh 69597cb686 memory: wgtunnel Phase-0 DNS landed + infra-ops now holds fleet Cloudflare DNS-edit token
boring.phasefinal.com CNAME -> ana-srv1 (DNS-only) published + verified; unblocks
the wstunnel ACME cert. Operator handed over an all-zones Zone:DNS:Edit token, now
vaulted at nh3-dev/.config/cloudflare/infra-ops-dns-token — fleet DNS is self-serve.
2026-08-12 01:29:42 -07:00
vh a8c6d85df9 memory: wgtunnel moved to its own repo (vh/wgtunnel) — endpoint infra verified + pre-seeded
Flip the queued-project pointer: wgtunnel is now a standalone repo, pre-seeded
after a live verification pass (ana-wg UDP/31337 endpoint, traefik TLS edge on
ana-docker, direct-A public edge). Ground truth captured in the new repo;
/vor-plan is the next step. Do wgtunnel work in ~/development/wgtunnel, not here.
2026-08-11 23:48:17 -07:00