For Agents
Reverse-chronological session log. Newest entries at top, grouped by date (
## YYYY-MM-DD). Each bullet: one piece of work, short summary, wikilinks to docs touched. Updated byobsidian-documenteron every project doc write. Read byhistorianat bootstrap.
2026-07-24
- KB semantics phase 2 COMPLETE — fully-local semantic SECTION clustering over the phase-1 vectors + a read-only web explorer (“Szemantika”), NO LLM. Pipeline: scroll all 3252 chunk vectors from Qdrant
manuals(READ-ONLY) → group by(manual_id,page)→ L2-normalized mean-pool → ~1248 SECTION vectors →HDBSCAN(min_cluster_size=2, min_samples=1, euclidean≈cosine)→ representative-heading labels →clusters.json. 194 clusters (post boilerplate filter), 24 cross-SOURCE, 631 noise singletons (noise EXPECTED for a dedup goal). Top clusters are real cross-make overlaps (WINDOW REGULATOR spanning chevy-tracker + suzuki-vitara/5door + workshop; ENGINE COOLING, PARKING BRAKE, THROTTLE BODY). Code/home/levander/kb-vectors/(sectionvecs.py/labels.py/cluster.py/kbclust.py+ tests, CLI build/list/show); explorer/semantics+/api/clustersfolded into the EXISTING knowledgebase Flask app (NO new service) atknowledgebase.taild4189d.ts.net/semantics; 🧭 Szemantika dashboard tile. Ranked by cross-SOURCE span so dedup targets surface first; labels pluggable behindmake_labelfor a futureclaude -prelabel. Phase 3 (claude -pgeneration) still NOT started —clusters.jsonis its retrieval bundle — 2026-07-24-kb-semantics-phase2-complete - ⭐ Gotcha — HDBSCAN’s
min_samplesdefaults tomin_cluster_size, so leaving it default made ALL sections noise (0 clusters). Must setmin_samples=1explicitly formin_cluster_size=2to form clusters — looked like “no structure in the data” but was purely the two knobs being coupled by default — 2026-07-24-kb-semantics-phase2-complete - ⭐ Gotcha — cross-source span was inflated because
manual_idis<folder>/<manual>and the kick-fix archive is ONE source split into ~25 topic-subfolders (each counted as a distinct “manual”). Fix: define span by distinct TOP-LEVEL source folder (source_of = manual_id.split("/",1)[0]) so kick-fix collapses to 1 source while genuinely-separate manuals still count; keepsource_manualsfor display, rank by(span=distinct sources, #manuals, size). Added anis_boilerplate()label stoplist (22 clusters dropped — cover pages “SUZUKI”, “GENERAL DESCRIPTION”/“DIAGNOSIS”). 51% noise is EXPECTED/correct for dedup. Whole pipeline is READ-ONLY scroll → localclusters.json, never upsert/delete — 2026-07-24-kb-semantics-phase2-complete - Updated SESSION-HANDOVER (new Szemantika/semantics service section, phase-2-DONE + phase-3-still-open, the two starred min_samples + span-inflation gotchas in Key gotchas, OPEN/next KB-deferred) and TOPICS
- Live state ~14:00: box UP (booted 13:55 after a physical power-on following a morning mains outage); clevis TPM auto-unlock worked again unattended; all services active (
knowledgebase,home-portal,wifi-usage,intruder-alarm,camwall, both tailnet nodes,kb-qdrant+frigateUp); Qdrantmanualsat 2839 points — SESSION-HANDOVER - Upgraded
telep-routerTailscale 1.80.3 → 1.98.9 — hand-installed a static arm64 build (the opkg feed only carries 1.80.3) via asetsid-detached self-reverting swap script that rolls back to/overlay/ts-backup/if the new binary isn’t online in 45s. Router online, 8 peers, verified over SSH served by the new binary. ⚠️ opkg still RECORDS1.80.3-r1→ a futureopkg upgradecould clobber the new binary. Gotcha: on OpenWrt/busybox usesetsidnotnohup(absent) + a persisted/overlaybackup + auto-revert so a bad swap can’t lock you out of your only remote foothold — Since last handover (2026-07-24) - ⚠️ New observation — a daily ~06:00 mains die-off pattern. The box (and the router with it) appears to lose power around 06:00 most mornings (Jul 19 ~06:59, Jul 23 ~06:36, Jul 24 morning). The router dies too = MAINS-level, distinct from the PSU-load resets (2026-07-24-psu-load-resets). Suspect a scheduled utility relay (Hungarian vezérelt/GEO off-peak tariff) or a timed high-draw appliance browning the shared circuit — NOT confirmed; confirm via
journalctl --list-bootsvs router-reboot regularity — Since last handover (2026-07-24) - Remote-wake (WOL) is NOT possible as configured — after a mains outage the box needs a physical power-button press; WOL never enabled (BIOS+NIC), the router has no etherwake/wakeonlan, and the box’s MAC is unknown after the DHCP lease expires (no static reservation). Better fix than WOL: BIOS
Restore on AC Power Loss → Power Onso the box auto-recovers from the daily blip like the router (one physical BIOS visit) — Since last handover (2026-07-24) - In-flight (check next session):
~/ocr/resume_extras.sh(setsid-detached, sentinels.done/.failed) — body-measurements already OCR’d+seeded into suzuki-vitara; this OCRs the Sidekick parts-catalogue → seedssuzuki-sidekick/parts-catalogue-89-98→ rebuilds the site → vectorizes BOTH new manuals into Qdrant (thread-cappedOMP_NUM_THREADS=4+nice). Re-runbash ~/ocr/resume_extras.shif it died — Since last handover (2026-07-24) - Updated SESSION-HANDOVER with a “Since last handover” delta block (router tailscale upgrade, daily-06:00 mains pattern, WOL, in-flight OCR job) + a live-state callout, added the ⭐ setsid-not-nohup gotcha, the daily-mains and opkg-clobber warnings, and carried the power-hardware + KB-deferred items into OPEN/next
- Expanded the knowledgebase manual library — many OCR’d manuals now under
docs/<folder>/<manual>/: suzuki-vitara (5door-supplement, supplement-61a40/99501, workshop-1988-1998 835pg, wiring-diagrams/99512, body-measurements), chevy-tracker (owners-1995, geo-tracker-repair), suzuki-sidekick (wiring-1996, parts-catalogue-89-98). OCR pipeline marker (~/ocr/venv,marker_single --force_ocr,TORCH_DEVICE=cuda) →seed_import.py→ build; manuals queued as CHAINED nohup scripts (each waits on the prior’s.donesentinel) to serialize the single GPU. Dedup discipline: md5-dedup downloads BEFORE OCR — many were exact dupes / same-content-different-name (workshop ×2, 99501 ×2, parts-catalogue ×2); zips sometimes contain the PDF (sidekick wiring was a zip ofwiring2.pdf) — Library expansion (2026-07-24) - Ingested the kick-fix.com archive — a 112 MB offline archive of the defunct kick-fix.com Suzuki Sidekick/Tracker repair site into
docs/kick-fix/(25 topics, 127 articles, 741 images, 8 text-layer PDFs; 27 image-only PDFs deferred for OCR). Dead-site archive → no ToS/robots issue. Script/home/levander/knowledgebase/kickfix_ingest.py(KB venv + markdownify + beautifulsoup4 + pypdfium2) — 2026-07-24-kickfix-ingest - ⭐ Gotcha — markdownify SILENTLY DROPS
<img>inside<table>cells. Old sites use tables for LAYOUT so nearly all figures were copied to disk but never referenced (11 refs for 122 pages). FIX: before markdownify,.unwrap()the layout containers (table/tbody/tr/td/th/center/font) so text+images flow linearly, rewrite<img src>to the copied basename in place, wrap imgs in own<p>(→ 528 refs); keep a trailing## Figuresonly as a safety net — The two that cost real time - ⭐ Gotcha — “See photo here” TEXT links (
<a href="X.jpg">) are separate from inline<img>, 365/664 broken (targets never copied). FIX: also walk<a href>; if the URL-decoded,../-resolved href is an existing image, copy under basename + rewrite href (keep as text link, don’t inline); leave external http(s)/mailto; drop dead archive-local hrefs (→ 365→0 broken, images 449→741). src/href need URL-decode (%20) + cross-topic../resolution — The two that cost real time - Vectorization phase 1 COMPLETE — built
/home/levander/kb-vectors/(chunker/embed/index/search/kbvec CLI): localBAAI/bge-large-en-v1.5CPU embeddings + Qdrant containerkb-qdrant(127.0.0.1:6333, collectionmanuals, 1024-dim cosine), idempotent per-manual (delete-by-filter + uuid5 ids). 2839 chunks indexed across 33 manual/kick-fix collections; cross-source semantic search blends official manuals + kick-fix community guides. Chose Qdrant over embedded stores for payload filtering + UI + growth; suzuki-forums crawler DROPPED (Tollbit-gated). Foundation for phase 2 (semantic dedup) + 3 (claude -pgeneration) — 2026-07-24-kb-vectorize-complete - ⭐ Root-caused a NEW, distinct power problem: PSU-can’t-sustain-peak-load resets (separate from the overnight mains outages). The box crash-rebooted ~3× in 30 min (04:11/04:20/04:32) under many heavy jobs at once (GPU OCR + CPU embedding + kick-fix conversion + Frigate). NOT thermal (CPU 39°C/GPU 47°C, no MCE), NOT mains — the router stayed up 14h+ straight through (the router-uptime cross-check is the discriminator; mains = router dies too). Dead-stop journal signature. The default 16-thread CPU embedding maxing all cores reset the box in 2–6 min every time. MITIGATION: cap to 4 threads (
OMP_NUM_THREADS=4/torch.set_num_threads(4)) +nice+ serialize heavy jobs → box stayed up 45+ min, finished the index. A UPS does NOT fix this; real fix = a bigger/better PSU — 2026-07-24-psu-load-resets - clevis LUKS TPM2 auto-unlock VALIDATED IN PRODUCTION — the PSU reboot storm above doubled as the reboot test: the box self-recovered the encrypted root through all 3 unattended reboots (
systemd-cryptsetup@nvme1n1p3_crypton boot, root mounted, no passphrase). All services auto-recovered (knowledgebase, kb-qdrant, home/knowledgebase tailscale nodes, Frigate, camwall, wifi-usage, intruder-alarm). Reboot-test status: pending → PASSED in production — Validated in production (2026-07-24 reboot storm) - Added a global dashboard
home.taild4189d.ts.net(owntag:telepnode, Hungarian “Elérhető rendszerek”, tiles Kamerák/Tudásbázis/Top képek/Router); the knowledgebase also became its owntag:telepnode. Same reusable pattern: a dedicated secondtailscaledper identity (--tun=userspace-networking, own--socket/--statedir) — 2026-07-24-global-dashboard - Updated SESSION-HANDOVER (KB expansion + kick-fix + kb-vectors/Qdrant + global-dashboard sections, PSU-load power bullet, LUKS reboot-test PASSED) and added the starred markdownify-table-images, text-link-images, PSU-vs-mains and md5-dedup gotchas
- Set up TPM2 auto-unlock for
telep-mainframe’s root LUKS — the follow-up to the mains-power work so an unattended reboot no longer stalls at the LUKS passphrase prompt (unreachable over SSH, no dropbear-initramfs). Root is LUKS2-on-LVM (/dev/nvme0n1p3, mappernvme1n1p3_crypt, UUIDa3a8e37d-79fa-484b-bc3f-40c56df95337); TPM2/dev/tpmrm0(MSFT0101), Secure Boot OFF. Bound the TPM with clevis (clevis-initramfs+clevis-tpm2,clevis luks bind -d /dev/nvme0n1p3 tpm2 '{}', empty PCRs) → keyslot 1 = TPM, keyslot 0 = passphrase kept as fallback;update-initramfs -uinstalls the clevis unlock hook so the disk auto-unseals at boot. Header backed up first at~/telep/luks-header-nvme0n1p3-20260723.img— 2026-07-24-luks-tpm-autounlock - Gotcha — Debian 13 stock initramfs (
initramfs-tools) does NOT consumesystemd-cryptenrollTPM2 tokens. Triedsystemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=first: it enrolls a valid TPM keyslot, butupdate-initramfswarnsignoring unknown option 'tpm2-device'and the box still prompts at boot (the classic cryptsetup hook can’t read a systemd-tpm2 token). Abandoned that path (wiped the slot with--wipe-slot=tpm2, no passphrase needed) and used clevis instead — [[2026-07-24-luks-tpm-autounlock#why-not-systemd-cryptenroll|Why notsystemd-cryptenroll]] - Gotcha — adding any new LUKS keyslot needs the existing passphrase (an agent can’t do it for the user) and is genuinely slow (argon2 KDF + TPM seal, tens of seconds); an impatient Ctrl-C orphans a
systemd-crypt*process that SURVIVES holding the LUKS header lock AND/dev/tpmrm0and re-broadcasts asystemd-ask-password— silently blocking the laterclevis luks bind. Fix: SIGKILL the orphan, confirmfuser/lsofclear + passphrase slot intact, retry — Hard-won gotchas - Honest incomplete state: reboot test still PENDING (needs console access — a failed unlock hangs with no SSH); auto-unlock only helps once the box BOOTS, and BIOS
Restore on AC Power Lossis still stay-off so a mains cut still needs a button press (2026-07-23-mains-power-shutdowns); empty-PCR + Secure-Boot-off is an accepted tradeoff — protects against bare-drive theft, not whole-box theft. A passphrase-echo hygiene issue occurred → passphrase rotation suggested (no values in the vault) — complementary (honest state) - Added a “Disk encryption / auto-unlock” note to the drives section and the two starred TPM/keyslot gotchas to SESSION-HANDOVER; cross-linked the follow-up from 2026-07-23-mains-power-shutdowns
- Built the knowledgebase — a tailnet-only manual library + OCR ingest portal on
telep-mainframe, live athttps://knowledgebase.taild4189d.ts.netas its own tailnet node (not a port on the mainframe). Renders OCR’d PDF manuals as an mkdocs-material static site (folder-tree browse, full-text search, dark mode, phone-responsive, all imagesloading="lazy") plus a Flask+waitress/uploadportal that runs marker OCR on the GPU, splits the markdown into per-section pages, copies images and atomically rebuilds the site. Two enabled systemd units (knowledgebase.serviceapp on127.0.0.1:8092as levander,tailscaled-knowledgebase.servicethe dedicated node). Code/home/levander/knowledgebase/(26 tests). Seeded with the Suzuki Vitara 5-door supplement (59 pages/1248 imgs) + Chevy Tracker 1995 owners manual (5 pages/352 imgs) — 2026-07-24-knowledgebase - Core pattern — a separate tailnet identity on one host = a second
tailscaledwith--tun=userspace-networking, its own--socket/--statedir, thentailscale --socket=… up --hostname=knowledgebase(one interactive auth) +tailscale --socket=… serve. Givesknowledgebase.<tailnet>.ts.netas its own device without a container or GPU passthrough — the app + OCR stay on the host where the GPU +~/ocrmarker venv live — The two that cost thought (separate-node + TLS) - TLS gotcha —
tailscale serve --httpsfails with “no TailscaleVarRoot” when the second tailscaled is started with only--state=<file>: a TLS handshake error wherecurlgets000even with-kthough TCP/ping succeed. tailscaled has no var root to store the LE cert. Fix: start it with--statedir=<dir>instead (the state file lives inside the statedir, so the node stays authenticated), thentailscale --socket=… cert <name>provisions the cert — The two that cost thought (separate-node + TLS) - Chunked loading = split each manual by its
#h1 headings into many small per-section HTML pages (a 795 KB markdown would be one huge page) + a mkdocson_post_pagehook injectingloading="lazy"into every<img>(verified 1600/1600 lazy) — this fixed “Ulysses chokes on 1248 images”. Browse is the folder tree undermanuals-src/docs/→ mkdocs auto-nav (no hand-maintainednav:); one service serves both the static site and the/uploadportal; mkdocs builds to a temp dir thenos.rename-swapssite/so a broken source never takes down the live site — How it works (the reusable parts) - Ingest reuses the existing
~/ocr/venvmarker pipeline as a subprocess (marker_single,TORCH_DEVICE=cuda) — no duplicate torch/model install; single background worker, GPU-serialised (shared with Frigate); job statesqueued→ocr→splitting→building→done/failed. Path-traversal guard (safe_slug+ realpath containment) before any write,%PDFmagic-byte check, 500 MB cap. Re-ingesting the same folder+name mustrmtreethe destination first or stale orphan pages/images linger as ghost pages in nav/search — Security & robustness - Added the Knowledgebase section and the two starred tailnet-node/TLS gotchas to SESSION-HANDOVER
2026-07-23
- Built the Frigate viewer alert —
frigate-viewer-alert.serviceontelep-mainframe(python3 stdlib,/home/levander/frigate-viewer-alert/, 34 tests, runs as levander not root): followsdocker logs -f --since 0m frigate, parses each nginx access line, reads the real client IP fromX-Forwarded-For(set bytailscale serve), maps100.x→ tailscale device name and Telegrams👁 <device> (100.x) opened Frigate — HH:MM[ · live: <cam>]on a new UI session. Reuses the frigate-notify bot+chat (token/chatid read at runtime from~/nvr/frigate-notify/config.yml, never copied) — viewer pings land beside the person/car alerts — 2026-07-23-frigate-viewer-alert - Core detection technique:
tailscale servewrites the tailnet client IP into the nginxX-Forwarded-Forfield. A real viewer →100.xXFF; internal automation (frigate-notify polling, self-polls) → XFF-, excluded by construction with no allowlist. Both live (/live/jsmpeg/…101) and plain UI (/api/config,/login) requests carry it, so every session is visible off one log stream. “Every UI session without spam” via a per-source-IP 5-min cooldown — an open tab polls constantly so it edge-triggers (first request after a quiet gap = new session; later requests just refresh the timer) — How detection works (the reusable part) - Security gotcha: X-Forwarded-For is the LAST quoted field before
request_time=, not the first. Leftmost-match (re.search) is spoofable — a client injecting a literal"into its User-Agent/Referer forges an earlier"…" request_time=and plants the captured viewer IP. Default nginx escapes quotes so it’s safe in practice, butfindall(...)[-1](the field truly adjacent torequest_time=) removes the dependency on the log-escape setting. Reusable for any nginx-log parsing that trusts a quoted field — [[2026-07-23-frigate-viewer-alert#1-x-forwarded-for-is-the-last-quoted-field-before-request_time|1. X-Forwarded-For is the LAST quoted field beforerequest_time=]] - Ops gotcha: never spawn
tailscale statusper log line. An early version refreshed the name cache on every unknown-IP line; iftailscale statusis momentarily failing or the IP isn’t listed, that’s one blocking ~10s subprocess PER line from an active viewer — a self-DoS. Rate-limit miss-triggered refreshes (≤ once/60s) on top of the periodic refresh. Also: reset thedocker logs -freconnect backoff only after a line is actually read, or an immediate-EOF crashloop never escalates;--since 0mso restarting the daemon doesn’t replay stale alerts — [[2026-07-23-frigate-viewer-alert#2-never-spawn-tailscale-status-per-log-line|2. Never spawntailscale statusper log line]] - Added the Frigate viewer alert bullet under Frigate NVR and gotchas 1+2 to SESSION-HANDOVER
- Root-caused
telep-mainframe’s recurring “randomly turns off overnight” to MAINS POWER loss, not the PC. Recurring unclean shutdowns inlast -x(Jul 23 06:36, 19 06:59, 17 21:28, 16, 14); the box stays dark until manually powered on. Verdict conclusive — the telep-router (separate hardware/PSU) died at the same instant (mainframe journal dead 06:36:10 CEST, router booted 06:36:27, +17s POST), which only a shared-mains event can cause — 2026-07-23-mains-power-shutdowns - Reusable method — the dead-stop signature:
journalctl -b -1ends mid-normal-operation (a routine arp-scan presence check) with NOStoppingsequence, NO panic/stack trace, NO thermal/MCE/Xid line, NO OOM. That absence is the diagnosis: software shutdown always logsStopping, a panic always logs a trace → a clean dead-stop = power physically removed. Confirmed cool (acpitz 27°C, GPU 47°C/128W), no poweroff/suspend timers,atq/wakealarm empty, sleep+suspend masked — Evidence chain (the reusable method) - Clincher method — cross-check an independent always-on device: derive the router’s boot epoch from
date +%sminus/proc/uptimeand compare to the instant the PC’s journal died. Same-timestamp death = mains; only-the-PC = its own PSU. Turns “probably power” into “definitely mains” — Evidence chain (the reusable method) - Explained why only the PC “stays off”: BIOS
Restore on AC Power Loss= stay-off (common default) so the desktop sits dark until someone presses power, while the router auto-powers-on (embedded, no button) so its recovery is invisible. Everylast -xcrash → a boot many hours later, never an auto-restart (inconsistent with panic=reboot) — Why only the PC appears to “stay off” - Recommendations to the user, priority order: (1) ~600–900 VA UPS + NUT — the real fix for unstable mains, logs every sag/transfer and triggers graceful shutdown on long outages; (2) free stopgap — flip BIOS AC-loss to
Power On(physical BIOS access only, not over SSH); (3) rule out an overloaded shared circuit (i9-12900K + RTX 3080 ~500W + 4K TV). ext4 clean and all services recovered so far, but repeated hard cuts risk eventual corruption. Notedfrigate.servicereads inactive only because Frigate is docker compose, not a systemd unit (containers healthy) — Recommendations (priority order) - Added the mains-power bullet under Hardware/host and the three reusable lessons to SESSION-HANDOVER
2026-07-22
- Built the WiFi device usage strip on the camwall —
wifi-usage.serviceontelep-mainframe(python3 stdlib,/home/levander/wifi-usage/, 44 tests): every 5s it polls the router over one persistent SSH connection, diffs per-station byte counters into live Mbps, resolves MACs to names and pushes an ASSosd-overlayinto the running mpv. Strip along the bottom of the cctv pane (y 1880–2140) showing everytelep1device’s ↓/↑ plus a 5G WAN total — 2026-07-22-wifi-usage-strip - Core discovery:
mpvdestroysosd-overlayentries when the IPC client disconnects. Connect→send→close silently does nothing while still replying{"error":"success"}; holding the same socket open with an identical payload made the strip appear. So the IPC connection must be persistent, mpv’s event stream must be drained, and reconnect-on-failure is what gives self-healing (strip returned ~23s after a camwall restart) — 1. The mpv IPC connection must be persistent iwinfo assoclisthas no byte counters;iw dev <ap> station dumpdoes (rx/tx bytes + signal,iwalready on the router) → per-device bandwidth with no extra package, no nftables accounting, no conntrack parsing, no nlbwmon. And the counters are AP-perspective: stationtx bytes= the device’s DOWNLOAD,rx bytes= its upload; the WAN interface is not inverted (verified live: device 35.4 ↑ vs WAN 35.1 ↑) — [[2026-07-22-wifi-usage-strip#2-bytes-come-from-iw-not-iwinfo|2. Bytes come fromiw, notiwinfo]]- Chose
osd-overlayovercamwall-reload:camwall-reload’sloadfilerestarts video playback (fine weekly for TOP KÉPEK, would stutter the live cameras every 5s) and goes through the lavfi chain that previously killed the wall. Also:/run/camwall.sockissrw------- root root→ the daemon must run as root — [[2026-07-22-wifi-usage-strip#4-osd-overlay-never-camwall-reload|4.osd-overlay, nevercamwall-reload]] - Security work on an untrusted input path (DHCP hostnames are attacker-supplied):
subprocess.run(text=True)decodes strictly andUnicodeDecodeErroris aValueErrornot caught byexcept (SubprocessError, OSError)→errors="replace"; ASS escaping stops tag injection (validated vs libass 0.17.3) but names still need a 22-char cap or a 400-char hostname paints across the 4K frame; camera VLAN excluded by construction (phy1-ap1never queried) — Untrusted input: LAN device names - Gotcha: SSH ControlMaster masks credential loss — removing the router key showed no degradation because the multiplexed ControlPersist connection was already authenticated and the key file is never re-read; you must also drop
/run/wifi-usage/router.sockto test it — SSH ControlMaster hides credential loss - Added the WiFi usage strip section and five new gotchas to SESSION-HANDOVER
- Unstalled the FaceKom “VirtualOffice” OpenVPN on the
exit-vpncontainer and wired it as an on-demand Tailscale exit node (exit-vpn/100.98.208.98). The CA was never broken — real causes were the wrong gateway IP (.185vs92.119.122.32, both running OpenVPN on tcp/1194 → foreign CA), wrong crypto (AES-256-CBC/SHA1), and an auth file still carrying the Hungarianjelszó:<TAB>label inside the password. Full tunnel viaredirect-gateway def1, persistent viaopenvpn-client@farm— 2026-07-22-facekom-vpn-exit-node - Discovered FaceKom “VPN-only” hosts (
npm.facekom.net) are public IPs behind an nginx source-IP ACL, not a DNS issue — 403 from home WAN, 200 via VPN, same resolution from every resolver. Also:route-upscripts are impossible under the hardenedopenvpn-client@.service(fork errno=11 kills the tunnel) → separate oneshot unit restartstailscaledinstead — Gotcha: tailscaled goes offline after VPN route changes - Corrected the now-wrong “exit-vpn — STALLED” section and added 6 new gotchas to SESSION-HANDOVER
- Diagnosed an unplayable 1.6 GB SplitCam recording (
20251025075725.mp4) pulled off the mainframe’s Windows drive via filestash:moov atom not found,mdatsize 0. Verdict = unrecoverable — SplitCam crashed and NTFS-preallocated clusters were never flushed, so the mdat is stale deleted-file residue (7.997 bits/byte entropy, zero valid NAL chains, tail all zeros). Only ~69 KB of real H.264 hit disk — mp4-missing-moov-forensics - Reusable trick:
xattr -p com.apple.metadata:kMDItemWhereFroms <file> | xxd -r -p | stringsrecovered the Filestash origin URL, which let the source at/mnt/win/Users/kisapus69/odi/data/SplitCam/be chunk-md5-compared against the local copy (identical — source equally dead, no other copies) — Verifying the local copy wasn’t the problem
2026-07-21
- Remounted the Windows NTFS drive at
/mnt/winafter a reboot and restartedfilebrowser; browsing verified working athttps://telep-mainframe.taild4189d.ts.net:8445— Mount procedure (verified 2026-07-21) - Correction: the NVMe device path is NOT stable across reboots. The docs said
/dev/nvme1n1p2; the Windows NTFS partition is now/dev/nvme0n1p2(1.8Tnvme0n1), andnvme1n1is the 3.6T LUKS+LVM OS disk. Alwayslsblk -fand pick the 1.8Tntfspartition — and any fstab/systemd entry must useUUID=, not a device node — The Windows drive - Gotcha:
filebrowserneeds a restart after the mount. The container binds/mnt/win → /srvRO; started against an empty unmounted dir it holds the stale pre-mount view and/srvlooks empty inside the container while the host mount is fine.docker restart filebrowser. Every reboot = mount, then restart, in that order — The Windows drive - Strengthens the open item on persisting
/mnt/win: a systemd mount unit onUUID=orderedBefore=docker.servicewould eliminate both the reboot gap and the container-restart dance — SESSION-HANDOVER - Drive inventory update:
sdbis now present — 115.5G UDF labelledHBCD_PE_x64(Hiren’s BootCD PE rescue USB stick);sda(447.1G) remains unused (single 16M partition, no filesystem) — Other services on this box
2026-07-18
- Diagnosed & fixed the TV camwall “blue glare”: blocky blue/purple/green chroma on the two live panes (clean on the static JPEG panes). Root cause = a wedged NVENC encoder session on the RTX 3080 corrupting Frigate’s
birdseyerestream (-c:v h264_nvenc, inherited from globalhwaccel_args: preset-nvidia) even at GPU idle — the encode-side sibling of the old NVDEC magenta bug — 2026-07-18-birdseye-nvenc-blue-glare - Reusable isolation method: grab a still at each pipeline layer (TV framebuffer via
x11grab→ birdseye RTSP → source cam RTSP →/api/<cam>/latest.jpg→ps aux | grep birdseye). source clean + latest.jpg clean + birdseye corrupt = theh264_nvencencode is the sole corrupting stage — Isolation method (reusable — this is the valuable part) - Fix:
cd ~/nvr && docker compose restart frigatecleared the wedged session (still on NVENC). Durable option if it recurs: blankhwaccel_args→ birdseye encodes libx264 on the 12900K (YOLO stays on GPU; CUDA ≠ NVENC/NVDEC) — Durable fix if it recurs (not yet applied) - OCR’d a 564-page scanned Suzuki Vitara manual on the mainframe’s RTX 3080 via
marker-pdf(~22.5 min, 794 KB markdown, 1,248 images) — set up a reusable venv at~/ocr/venv. Core gotcha:pip install marker-pdfpulls a cu130 torch that driver 550.163.01 can’t init, sotorch.cuda.is_available()is False and marker silently falls back to CPU (hours instead of minutes). Fix =pip uninstall -y torchthen reinstall from the cu126 index (minor-version compat); the reinstall is a silent no-op without the uninstall. Driver deliberately NOT upgraded — 550 is load-bearing for Frigate — Running it on a CUDA GPU box (telep-mainframe), [[telep-mainframe#gpu-ocr-venv-ocrvenv|GPU OCR venv (~/ocr/venv)]] - Generic ops trap surfaced twice this session:
pgrep -f/pkill -fmatch your own command line. The OCR completion monitor reported RUNNING for 2.5 h after the job finished; apkill -f openvpn...over SSH killed its own shell (exit 143) and silently skipped the remaining steps — shell-gotchas-pgrep-self-match
2026-07-17
- Built and documented the presence-based intruder alarm (
intruder-alarm.service,/home/levander/intruder-alarm.py, python3 stdlib-only) ontelep-mainframe: auto-arms when no trusted phone is on the network, escalates person/car detections to Telegram, sirens the LG TV — 2026-07-17-intruder-alarm - Presence = union of router WiFi association table (SSH
iwinfo assoclistontelep1APs) +arp-scanonenp5s0. KEY INSIGHT: sleeping iOS phones stay WiFi-associated but stop answering ARP, so arp-scan alone false-arms overnight; the assoc table sees them. Failed scans are skipped, never treated as “everyone left” — Presence detection - Gave the mainframe router access it never had (router was Tailscale-SSH-only, ACL-blocked, no port-22 daemon): enabled a LAN-only key-only dropbear (
uciInterface=lan, PasswordAuth off) + dedicated key/home/levander/.ssh/router_alarm. GOTCHA: this rotated the router’s SSH host key (breaks existing known_hosts) — Prerequisite router access - Camera-VLAN gotcha: SSID
telep-ccis APphy1-ap1; querying it made the Tapo camera appear as an enrollable device. Fix: only querytelep1APs + exclude infra MACs (mainframe/printer/router/camera) — Camera-VLAN gotcha — telep-cc leaked an enrollable device - Enrollment via Telegram Trust/Ignore inline buttons (same “Dezsi az őr” bot as frigate-notify, token read from
~/nvr/frigate-notify/config.yml); device names resolved mDNSavahi-resolve→ dnsmasq reverse DNS →eszköz-<last4>, hardened against leakingdigerrors — Device naming & enrollment - State machine: dormant until ≥1 trusted device, arms after ALL trusted absent 10 min, disarms instantly on return, manual
/arm/disarm/auto; intruder escalation🚨 BETŐRŐ!+ snapshot (1×day/2×night); TV siren (2-beep,aplay -D plughw:1,3, NVIDIA HDMI = ALSA card 1 dev 3) on every detection armed or not — State machine
2026-07-16
- Deployed Filestash on
telep-mainframeto browse the box’s Windows NTFS drive in-browser (previews for images/video/PDF/code/markdown/archives); container bound to127.0.0.1:8334, config infilestash_datavolume — filestash - Mounted Windows C: (
/dev/nvme1n1p2, NTFS) read-only at/mnt/winvia in-kernelntfs3(RO because the live Windows install’s fast-startup/hibernation would corrupt a RW mount); passed into the container at/mnt/data. Gap: mount is manual, not in fstab — won’t survive reboot — The Windows drive - Exposed it at
https://telep-mainframe.taild4189d.ts.net:8445viatailscale serve --https=8445, same pattern as top4 picker (:8443) and Frigate (:443); port map now :443/:8443/:8445 — Exposure - First-run wizard (admin password + “Local” backend at /mnt/data) left for the user to finish; OnlyOffice for inline Office docs noted as optional/not-added — First-run setup (unfinished)
2026-07-14
- Built and documented the Frigate NVR end-to-end on bare-metal
telep-mainframe(i9-12900K / RTX 3080 / Debian 13): Frigate 0.17.2 + frigate-notify, ONNX YOLOv9-s detection (~12ms, 6% GPU), events-only recording to/srv/frigate— telep-mainframe - Exposed the Frigate UI at
https://telep-mainframe.taild4189d.ts.netvia Tailscale Serve (real LE cert, tailnet-only, no LAN exposure) — Access - Built the HDMI camera wall: birdseye 3840x1080 → mpv straight to DRM/KMS with no X server; required
nvidia-drm modeset=1or the 3080 exposes zero display connectors — TV wall (HDMI) - Wired Telegram alerting (bot “Dezsi az őr” → supergroup) on person + car via frigate-notify polling the HTTP API every 15s — Alerting
- Big gotcha: the dual-lens Tapo’s
/cam1/and/cam2/RTSP paths are ALIASES FOR THE SAME LENS (proved by PSNR ≈36 vs ≈3.9); caused duplicate events + duplicate alerts and an invisible PTZ lens. Correct map: fixed=/stream1, PTZ=/stream6— are the SAME lens - Recorded eight more gotchas: Secure Boot rejecting the DKMS-signed NVIDIA module, TensorRT detector dead on amd64 in 0.17 (use
onnx), Frigate’s YOLOv9 export broken by torch ≥2.6, Tapo silently truncating long passwords, Frigate’s DB wiped unless the whole/configdir is mounted, birdseye not drawing camera names, birdseye grid geometry, iOS Private Relay breaking MagicDNS — Gotchas - Added the isolated
telep-cccamera VLAN to the router doc (192.168.30.0/24, hidden SSID, WPA2 forced by Tapo, nocams → wan, NTP DNAT’d back) and recorded the router’s outstanding security gaps — Camera VLAN (telep-cc), Security Gaps
2026-07-13
- Initialized the
homelabproject (physical/on-prem infra, separate from cloud-only levandor-infra) — homelab - Surveyed the OpenWrt router end-to-end: RT-AX89X on OpenWrt 24.10.0, LAN/WAN/firewall/DHCP, WPA3-SAE WiFi, Tailscale 1.80.3, 174 stock packages — telep-router
- Fixed hostname drift: device still identified as
OpenWrt(kernel,uname -n, UCI) whiletelep-routerwas only a Tailscale MagicDNS name. Setuci system.@system[0].hostname='telep-router', committed, reloaded — Changes Made (2026-07-13) - Recorded three gotchas: wifi-ifaces declaring
network='lan wan'(inert but a footgun), 8-char WiFi PSK, and double-NAT behind192.168.254.1— Gotchas