FUTURE GADGET LAB

PhyberApex Infrastructure

Ansible-orchestrated homelab spanning 42 services across Raspberry Pi devices, Docker homelab, and GPU-accelerated AI lab infrastructure

12
Lab Members
42
Services
57
Tools
4
AI Services
Ansible Woodpecker CI Renovate Docker Compose Arduino-cli

System Capabilities

Infrastructure

42 containerized services across the Docker homelab, plus a Raspberry Pi fleet with OLED status displays, GPIO controls, and self-hosted CI agents

Traefik | Authelia | Docker Compose

Custom Applications

In-house apps: Karakuri AI dashboard, Tegata SSO portal, Mezame break-glass control, Hakobi e-reader courier, and Kamado firmware shelf, plus purpose-built infra services

Vue 3 | NestJS | Python

Karakuri AI Lab

12 autonomous lab members with 57 tools coordinating LLM inference, speech in and out, and image generation

llama-server | Qwen3-TTS | faster-whisper | ComfyUI

Karakuri Projects

An autonomous issue-to-merge loop: karakuri polls enrolled repositories, hands one Stage of one issue at a time to a headless coding agent, and Kongroo reports on whichever channel fits where Janis is

Claude Code | GitHub | Presence-aware

Hardware

Eight firmware projects on ESP32 and nRF52 boards — desk displays, a photo frame, a deck companion, a pocket pet — with host-tested cores, CI-compiled sketches and over-the-air updates from the Kamado shelf

Arduino-cli | CYD | XIAO

Operation Skuld

A solar-powered, air-gap-capable bug-out replica of the homelab's Tier-0 services — 15 minutes from decision to wheels rolling

PREP ⇄ LIVE | Solar | Offline-first

GitOps Deployment

Automated infrastructure deployment via Woodpecker CI with idempotent Ansible playbooks and full Jinja2 templating

Woodpecker | Ansible | Git

System Architecture

Three-tier infrastructure | Edge devices → Docker homelab → GPU lab | Plus a portable off-grid replica

TIER 1: EDGE DEVICES

Raspberry Pi Fleet

  • OLED Info Display (SSD1306)
  • GPIO Button Control
  • Woodpecker CI Agent
  • Tailscale VPN
  • Desk boards: CYD displays + Lumanode, flashed from Kamado
→
TIER 2: HOMELAB

Docker Homelab Server

  • Traefik + Authelia (SSL + SSO), AdGuard DNS
  • Media Stack (Jellyfin, Radarr, Sonarr) + Hakobi courier
  • Productivity Tools (Paperless-ngx, Immich)
  • Monitoring (Uptime Kuma, Prometheus, Grafana, Loki)
  • Tegata, Kamado, zukan and sonae — the in-house services
  • NAS (SnapRAID + MergerFS, 6-disk, Samba)
  • Home Assistant on its own host beside it
→
TIER 3: AI LAB

GPU Infrastructure

  • Karakuri Lab Hub
  • llama-server (GGUF models)
  • Qwen3-TTS + faster-whisper
  • ComfyUI (Image Generation)
⇢
PORTABLE: OPERATION SKULD

Off-Grid Continuity Kit

  • Amadeus mini PC (Tier-0 replica)
  • CPU-only llama.cpp inference
  • Solar + power station, air-gap ready
  • 15-minute break-glass to LIVE

Infrastructure

42 containerized services | 11 categories | Docker Compose across the Pi fleet + homelab server

Security & Proxy

Authelia Tegata Traefik

Networking

Adguard

Media Stack

Hakobi Kapowarr Media Shoko Suwayomi Tunarr

Productivity

Actual Bitwarden Immich Linkwarden Mealie N8n Paperless Ng Searxng Trek

Home & Devices

Frigate Kuroshiro Kuroshiro Demo

Gaming

Asf

Monitoring

Docker Apps Homepage Monitoring Scrutiny Umami Uptime Kuma

Storage

Backrest Grimmory Homebox

AI & LLM

Comfyui Karakuri Llama Server Qwen3-TTS Sillytavern Whisper

Development

Kamado Woodpecker

Docs & Continuity

Sonae Zukan

Raspberry Pi Fleet

HARDWARE INTERFACE

SSD1306 OLED Display

System status on a 128x32 I2C display, fitted to two of the three Pis. A button press lights it for fifteen seconds with hostname, IP address, CPU load and memory usage; between presses it stays dark.

GPIO PINOUT
SDA GPIO 2 (Pin 3)
SCL GPIO 3 (Pin 5)
Button GPIO 20 (Pin 38)
Status LED GPIO 23 (Pin 16)
USER INTERFACE

GPIO Button Control

One button, read by hold length, for power actions without SSH. The display names the action it is about to take and asks for the release, so a hold can always be backed out of by holding longer.

Press Show the info screen for 15 s
Hold ~5 s, release Reboot
Hold ~10 s, release Graceful shutdown
CI/CD AGENT

Woodpecker CI Agent

Self-hosted CI agent running on Pi devices. Executes Ansible deployments, syntax checks, and automated infrastructure updates.

Agent Mode Distributed
Deployment Target Homelab + LLM Host
Trigger Git Push to Main

Custom Applications

In-house built services | TypeScript + NestJS + Vue 3 | Monorepo architecture

Karakuri

AI workshop dashboard coordinating 12 lab members across LLM inference, TTS synthesis, and image generation. Features a live avatar system with 15+ emotional states.

Vue 3 NestJS Socket.IO Docker
  • 12 AI lab members with tool delegation
  • 57 tools across 24 skills
  • 4 managed services
  • Real-time WebSocket status + resource monitoring

Tegata

User management portal for Authelia SSO. Provides admin CRUD for users and groups, an invite-based registration flow for onboarding new users, and self-service profile and password-reset flows, with a preview of exactly which services a given group grants before the invite goes out.

Vue 3 NestJS SQLite Authelia
  • Invite-based registration with expiry + group assignment
  • User CRUD synced to Authelia users YAML
  • JWT auth with argon2id password hashing; mailed reset tokens
  • Access bundles: named group sets an invite grants as one choice

Mezame

Break-glass dashboard for the Operation Skuld bug-out kit. One armed button — GO LIVE — turns a quietly syncing basement box into a self-contained air-gapped homelab; RETURN TO PREP brings it home. Orchestrates services first, router last, over SSH.

Vue 3 NestJS ssh2 Docker Compose
  • PIN-guarded mode switches with two-step armed confirms
  • Guided Drill wizard — Verify LIVE checklist, optional Kit Reset, and a Drill Record history with per-service skip-rate trends
  • Deep readiness checks: sync freshness, TLS countdown, SSD backup age, image + asset presence, drill age
  • Coming Home: a dry-run preview, then a Field Merge that carries what the family uploaded in the field back to the homelab, then Kit Reset
  • Readiness Push: hourly kit-side Telegram alerts and nudges when a readiness field flips
  • EcoFlow power telemetry via direct BLE poll, kept as a Charge History with every GO LIVE marked on it
  • Mesh Strip fed by MeshMonitor, and a PIN-gated Fallback Switch onto the kit's fallback CA

Custom Infrastructure Services

NVML Exporter

Minimal custom Prometheus exporter for GPU metrics. Uses pynvml directly for accurate per-GPU memory, temperature, utilisation and power readings, serving a standard /metrics endpoint at port 9835 with a 5s scrape interval. Karakuri reads it for its own GPU state and the desk tachometer's dial.

Python pynvml Prometheus Docker
  • GPU memory used / total / free bytes
  • GPU temperature, utilisation, power draw and power limit
  • Multi-GPU support with labeled metrics; a gauge a GPU refuses is left out, not zeroed
  • Zero dependencies beyond pynvml

Restart-Count Exporter

Tiny Prometheus exporter for Docker's own per-container restart count, the one signal a crash-looping container gives that a health check does not. Deployed beside every Docker host's node-exporter and cAdvisor, reading container inspect payloads through a read-only socket proxy so it carries none of the proxy's write or exec permissions.

Python stdlib only Prometheus docker-socket-proxy
  • One gauge per container: increments on an in-place respawn, resets only when the container is recreated
  • A collector-up gauge so a failed scrape reads as a failure, not as zero restarts
  • Built from source by the same role on every host, Pis included
  • Feeds the crash-loop alert in Grafana

Qwen3-TTS Service

Custom FastAPI wrapper around the qwen-tts library exposing an OpenAI-compatible /v1/audio/speech endpoint. Supports three synthesis modes with voice prompt caching for efficient voice cloning across lab members.

Python FastAPI PyTorch CUDA
  • CustomVoice — built-in speaker presets
  • VoiceDesign — voice from text description
  • Base — voice cloning from reference audio
  • Prompt caching for reusable voice clone sources

Hakobi

Always-on courier between the homelab libraries and the e-ink readers. Watches a Grimmory shelf and a Suwayomi category per reader, converts what it finds into the reader's own XTC format, stages it in a per-reader outbox laid out like the SD card, and carries it over either wire — pushed over Wi-Fi whenever a reader shows up on the network, or written straight onto the reader's memory card by the browser when it is plugged in. Never writes back to either library.

Python FastAPI SQLite Vue 3
  • E-ink readers of differing panel sizes, each with its own marker, queue and outbox
  • Grimmory OPDS shelves and Suwayomi GraphQL categories as sources
  • EPUB passthrough; comics and PDFs rendered to 2-bit .xtch via a vendored cbz2xtc
  • Per-item previews decoded back out of the .xtch, one Telegram summary per visit
  • Readers and settings live in hakobi's own store, edited from the page; a shared sleep-screen library with per-reader assignments, crops and rotation

Kamado

Firmware shelf and flasher for the bench's WiFi boards — the CYD displays and Lumanode alike. Holds one current binary per project and Target, put there by CI on every merge that changes the project or by the bench for a work-in-progress build, keeps a registry of the boards on the LAN with what each one is running, and flashes a chosen project onto a chosen board over the board's own HTTP update route with live progress. Refuses the combinations that would go wrong: a pinned board takes only its own project, and an image built for one Target never goes onto a board registered with another. Never compiles anything.

Vue 3 NestJS ESP32 OTA
  • Registry of boards rendered from Ansible host vars; adding a board is a config change, not a form
  • Server-side polling of each board's slots: running project, the other resident slot, reachability
  • Two-slot swap without an upload when the other slot already holds the requested project
  • Bearer-token routes for the bench's build script and Home Assistant's flash and swap pickers
  • One login for every board, so a unit's credentials are a registry row, not a secret per project

Kuroshiro

Self-hosted server behind the e-ink panels around the house — four TRMNL-class Devices today, from a 7.5" workshop board to a fridge-sized Sticky and a pocket Xteink photo frame. Each Device cycles through its own Rotation of Screens: Liquid-templated Plugins that poll the homelab's APIs, a two-slot mashup for the kids' countdowns, photos pulled from the family's Immich library. Developed as its own open-source repository and deployed here from the Ansible inventory like every other container.

NestJS Vue 3 Postgres Puppeteer Liquid
  • Device Models and palettes synced from TRMNL, so every panel size and colour depth renders at native fidelity
  • Screens gated by day/time Schedules; Sleep Mode wakes a panel exactly when its night window ends
  • Poll Plugins with named Data Sources, Webhook Plugins, Recipes imported straight from trmnl.com
  • Firmware pushed over the air with per-model compatibility checks
  • Alert Sweep for low battery, offline Devices and failing fetches, delivered through an Apprise sidecar; battery, signal and last poll scraped by Prometheus

Continuity Pipeline

Deterministic build of a family-facing continuity handout from one YAML source of truth — served on the homelab with a printable offline copy. No LLM and no secrets anywhere in the build path; the only AI step is a human-in-the-loop review interview that fills the gaps a repo can't know.

YAML Handlebars tsx Zero secrets
  • 41 services documented, 7 marked Tier-0 load-bearing
  • Drift report vs. the Ansible inventory: 0 open gaps
  • Review + print staleness tracked (2026-10-12 last review)
  • Secrets policy: pointers to where secrets live, never values

Zukan

The site you are reading. A static reference regenerated from the Ansible inventory on every docker-host deploy — service categories, the lab roster and Skuld status are rendered from Handlebars templates by scripts/generate-docs.ts, then copied onto the homelab for a stock nginx container to serve. Public by design — everything on it is meant to be read.

Handlebars tsx nginx Woodpecker CI
  • 42 services across 11 categories, rendered straight from host_vars
  • Generated in CI, gated by the docker-host deploy target's path list
  • Five Archify diagram pages — topology, Skuld lifecycle, CI/CD pipeline, issue lifecycle, Reach-out routing — committed as rendered HTML
  • A scheduled freshness check flags hand-written sections past their review date and diagrams whose evidence has moved on
  • No secrets in the build path — data comes from unencrypted ansible vars only

Hardware Projects

Firmware and enclosures authored here | Arduino-cli + OpenSCAD | Compiled by CI, finished at the bench

Source We Author, Not Gear We Bought

A project lands in the repo the moment there is firmware to compile or an enclosure to model — pinned toolchains, gitignored secrets, and a pure decision core with host tests standing in for a display or a radio no CI container has. Everything else physical in this homelab — the Pis, the GPU hosts, the Mesh's Meshtastic nodes — is bought, racked, or flashed with someone else's image, and lives in the Ansible inventory instead. CI compiles every changed sketch and puts each merged build of a WiFi board's project on Kamado's shelf, but every project still ends in a step only a human at a bench can take: holding BOOT while plugging in a board, calibrating a touch panel by finger, checking a print actually fits. Only a project's pure core — a decay curve, a font table, a draw weighting — and its host test are ever handed to an agent.

Lumanode

Wall-mounted ESP32 ambient light on an 8×8 RGBW panel. Wired into a 12V supply with no cable within reach, so it updates and logs entirely over WiFi. The reference implementation for every hardware convention: pinned build.sh, a pure state-machine core with host tests, and cheap, measured on-device feedback during an update.

ESP32 Arduino-cli 8×8 RGBW OTA over HTTP
  • Six lighting modes, picked from a web UI and held in flash
  • OTA update server plus a mirrored TCP log stream — no cable, ever
  • Two OTA slots: a swap reboots into the previous build without an upload
  • A Kamado unit — CI shelves each merge that touches it, the shelf flashes it
  • Built and running

Karakuri Control Surface

The Lab at a glance on a CYD touch display: one row per lab member with avatar, name and state, live VRAM in the header, and a row of preset buttons that start a prompt without typing. A thin client — Karakuri stays the brain, the board polls a bearer-token API over TLS and posts back.

CYD (ESP32-2432S028R) LVGL Arduino-cli OTA over HTTP
  • Polls a compact state document plus pre-baked avatar thumbnails, never the full Lab payload
  • Preset buttons fire Karakuri Schedules under the same trust posture as any run
  • An amber banner under the header counts what is waiting on a human — Owed Reach-outs and open escalations or approvals — beside where Home Assistant says he is
  • Pure core host-tested; touch calibration shared from lib/cyd-touch
  • Flashed and polling production; the tachometer, the jukebox and the frame swap onto the same board by OTA

Inference Tachometer

The GPU box as a dial: a needle for utilisation, a ring for VRAM, temperature and power as digits, and the loaded model's name along the bottom. It swings when llama-server or ComfyUI is working and rests when idle. A gauge, not a control surface — no touch, no buttons.

CYD (ESP32-2432S028R) Arduino-cli 16-colour palette OTA over HTTP
  • Reads one Karakuri route that folds the NVML exporter's scrape and the active model together
  • Damped needle sweep and dim levels tuned on the glass and kept as constants, a deliberate exception to the runtime-choice rule
  • Poll cadence and offline backoff shared from lib/poll-backoff
  • Flashed and running on the Control Surface's board

Jukebox

A VA-11 HALL-A clock that turns into a now-playing card. Idle, it is a dithered Glitch City night with a seven-segment clock; when a Home Assistant media player starts, it becomes a card with cover, title, artist, album and progress. A tap pauses, a swipe skips, and the clock face ignores touch.

CYD (ESP32-2432S028R) Home Assistant Arduino-cli OTA over HTTP
  • Both backdrops drawn one pixel at a time by a host-tested core; a workstation tool renders them to PPM
  • Follows the first playing player on a priority list, and remotes it back
  • Title text folded to the panel's ASCII fonts via lib/tft-text
  • Built and bench-walked; swaps onto the shared board by OTA

Frame

An Immich photo frame on a shelf. Asks Immich for a photo at random — from one album or the whole library — draws its preview across the panel and keeps it for a few minutes with the date and place on a dimmed band. A tap skips; a long press favourites it in Immich and the heart fills.

CYD (ESP32-2432S028R) Immich API Arduino-cli OTA over HTTP
  • Decodes Immich's extended-sequential (SOF1) previews with a vendored, patched JPEG decoder (lib/tjpgd)
  • Chunked bodies, captions and cadence all host-tested in the pure core
  • Tap and long press shared from lib/cyd-touch
  • Built, flashed and walked from the workstation

Kongroo

The Lab's avatar on her own screen. Kongroo drawn large, wearing whatever mood the Lab is in — thinking, busy, asleep, under pressure — with a speech bubble holding the last thing she told you and how long ago. Work that ends earns a moment of her success face. No touch, no buttons: a companion, not a control.

CYD (ESP32-2432S028R) Arduino-cli Baked RGB565 frames OTA over HTTP
  • Reads one Karakuri state route: her ambient state, re-derived server-side from the dashboard's own thresholds, and her latest Reach-out's spoken sentences
  • Five faces baked from the avatar art and streamed straight onto the panel — nothing decoded on the board
  • State-to-face mapping, the success pulse and the bubble text host-tested in the pure core
  • Built and shelved on Kamado; the bench walk is still owed

Fuda

An offline deck companion for the game table: tap to draw, read the line for the current danger level, keep track of what is permanently in play. The firmware is a deck-pack engine that knows nothing about any one game — the microSD carries one JSON pack per game, five so far.

CYD (ESP32-2432S028R) microSD packs Arduino-cli Offline
  • Session rules, persistence and pack validation in two host-tested headers
  • Per-draw SD/touch bus hand-off streams a card scan straight to the panel
  • Packs and firmware both sync over WiFi; play needs no network at all
  • Flashed and running on its own pinned board — Kamado never puts another project on it

Lumi

A pocket pet for a six-year-old on a Seeed XIAO 1.14" display — fully offline, three stats that decay in real time, three icons above the two front buttons, shake-to-dance, and a screen that sleeps until the egg is picked up. What it grows into is a family tree of fourteen equally likely endings shaped by how it is raised.

XIAO nRF52840 Arduino-cli 1.14" IPS Battery-powered
  • Pixel-art renderer and decay core both host-tested; every sprite drawn by a Python script into a header
  • State survives sleep and a power cycle; a new egg is a five-second hold of the side button
  • Panel, backlight, battery gauge and IMU wake shared from lib/xiao-nrf52
  • Built, bench checklist walked, printed case in use

PhyberApex Badge

An animated badge on the Seeed XIAO 0.96" display: the fox-on-vinyl mark spinning above the handle, a card with name and tagline, a QR code to the website, and a status page. Showpiece on the desk on USB; on its LiPo it sleeps after two still minutes and wakes when picked up.

XIAO nRF52840 BLE 0.96" IPS Battery-powered
  • The card's text is edited from a phone over a small BLE line protocol and kept in flash
  • Screens, rotation, backlight and idle logic in a host-tested core; a View in, an 80×160 frame out
  • Art built from source images by a script into generated headers with previews
  • Built and bench-confirmed

Board References & Shared Firmware

Shared Board Facts

Board wiring, dimensions and driver quirks live once in hardware/boards/ and are linked from every project that uses that board, rather than being restated per project. Two families so far: the CYD ESP32 touch display in both its panel variants, and the Seeed XIAO nRF52840 with its two display sizes.

CYD (ESP32-2432S028R) XIAO 0.96" / 1.14"
  • One board-facts file per family, cited by any project that builds on it
  • [Verified] / [Inference] / [Unverified] / [Speculation] labels on every claim, sourced back to vendor docs
  • A board with a radio gets firmware updates and a log stream over it before it gets features

Shared Firmware

Firmware shared by two or more projects moves to hardware/lib/ on its second use, never its first. Six libraries so far, each written by one project and moved when the next one needed it.

wifi-upkeep cyd-touch poll-backoff tft-text tjpgd xiao-nrf52
  • wifi-upkeep — what every WiFi board needs first: the OTA update route, the log stream, the unit identity and the TLS transport
  • cyd-touch and poll-backoff — the CYD's touch calibration, and when a board polls next or calls a service offline
  • tft-text and tjpgd — ASCII fold and wrap for the panel fonts, and a JPEG decoder patched for Immich's previews
  • xiao-nrf52 — panel, backlight, battery gauge and IMU wake for the two XIAO display projects

Karakuri AI Lab

12 autonomous lab members | 57 tools across 24 skill modules | GPU-accelerated inference, TTS, and image generation

What Karakuri Does

Karakuri is a control panel for GPU-accelerated AI services. Manual service control with live monitoring—start/stop containers as needed within VRAM limits. Features an animated Live2D avatar, autonomous lab members with a delegation protocol, scheduled work, a nightly pentest with tracked findings, and a bearer-token surface for the desk boards. Its configuration is git-sourced: the lab workspace is mirrored from this repository on every deploy, and the UI reads it rather than editing it.

LAB MEMBERS AUTONOMOUS
12 autonomous agents with unique personalities, roles, and capabilities. Each member has workstation configuration (llama-server or API provider), specific skill access (subset of 24 available skill modules), and delegation protocol for task execution. Permanent members hold standing roles; Temp-Hires are backed by a remote provider for a specific need, with a spend budget and a hard stop when it is spent. Supports autonomous group chat with turn-based speaker rotation and TTS voice synthesis for each member.
Active Members 12
Total Tools 57
Delegation Protocol Enabled
MODEL MANAGEMENT CORE
Unified model manager for LLM and ComfyUI models. HuggingFace GGUF browser with direct download, CivitAI search with AIR protocol support, URL downloads with SSRF protection. llama-server router mode for dynamic load/unload. Manages 10+ ComfyUI model categories (checkpoints, loras, VAE, controlnets, etc).
LLM Source HuggingFace + URL
ComfyUI Source CivitAI + AIR + URL
SSRF Protection Enabled
LLM INFERENCE GPU
llama-server provides OpenAI-compatible API with router mode for dynamic model switching. Auto-discovers GGUF models, monitors load/unload via log streaming. Shared workstation for all 12 lab members with per-request model selection.
API Standard OpenAI Compatible
Model Format GGUF
Router Mode Dynamic load/unload
VOICE SYNTHESIS GPU
Qwen3-TTS for high-quality voice cloning (CustomVoice speaker/style, VoiceDesign from description). Powers animated avatar TTS with lip-sync. Supports voice presets, custom recordings, and per-lab-member voice profiles.
Primary Engine Qwen3-TTS
Voice Modes CustomVoice | VoiceDesign | Base
Avatar Integration Lip-sync + emotions
IMAGE GENERATION GPU
ComfyUI node-based workflow system. Trigger workflows via typed TypeScript API, output gallery with real-time generation monitoring. Supports custom workflows, model downloads from CivitAI, and 10+ model categories.
Interface Node-based workflow
API TypeScript typed
Gallery Real-time outputs
AVATAR SYSTEM CORE
Animated Live2D avatar (Kongroo) with five expression families across sixteen states, TTS-driven lip-sync, and inference-driven reactions. States are triggered by service events and ranked by priority, so an error or a poke interrupts whatever motion is playing. Avatar speaks lab member responses, reacts to inference activity, and provides visual feedback for system operations.
States 16 states, 5 expression families
Triggers TTS | Inference | Manual
Animation Live2D via pixi-live2d-display
SCHEDULES TIMED
Prompts that run on a timer under a named lab member, declared as YAML in the git-sourced workspace. Each execution runs under a wall-clock Execution Ceiling, an overdue schedule is surfaced rather than silently skipped, and a schedule fed by untrusted input runs with a narrowed tool set. A schedule can opt in to the desk's Control Surface, where its label becomes a physical preset button.
Source YAML in the workspace baseline
Guard rails Execution Ceiling | Overdue | Untrusted narrowing
Manual trigger UI or a Surface Preset button
NIGHTLY PENTEST FINDINGS
A scheduled run by Nullbyte, the senior-dev member, against a target list generated from the inventory. Each run produces structured Findings keyed on host, port and category rather than a memory diff; a Finding has its own lifecycle — open, accepted-risk, resolved — that only a human moves, and a run rediscovering a resolved one reopens it with its original first-seen date.
Runner Nullbyte, on a Schedule
Finding identity host + port + category
Statuses open | accepted-risk | resolved
CONTROL SURFACE API DESK BOARDS
The one part of the API a microcontroller can reach: a static bearer token instead of the SSO login a board cannot complete, serving a compact state document and pre-baked avatar thumbnails to the Control Surface, and a single GPU document — utilisation, VRAM, temperature, power, the active model and whether karakuri has a job in flight — to the inference tachometer. New desk telemetry is a new route behind the same token, never a field on the credential-free Homepage summary.
Auth Static bearer, 503 while unset
Consumers Control Surface | Tachometer
Thumbnails 48×48 RGB565A8, baked into the workspace
MONITORING REAL-TIME
Live system metrics via Socket.IO: CPU, RAM, VRAM, disk usage, GPU temperature. Per-service status indicators (running/stopped/starting/error). Container-level streaming logs with filters and auto-scroll. Request history tracking for inference calls.
Transport Socket.IO WebSocket
Metrics CPU | RAM | VRAM | GPU
Log Streaming Per-service with filters
VOICE WAR ROOM GPU
Generates autonomous AI podcast sessions on any topic. All local lab members participate: script generation via LLM, per-member voice synthesis via Qwen3-TTS (batched), persisted to SQLite and served as audio. Four phases — voice caching → script generation → batch TTS synthesis → playback. Auto-interrupts when real work arrives.
Participants All local lab members
TTS Batch Size 5 parallel synthesises
Persistence SQLite + audio files
MEMORY SYSTEM PERSISTENT
Structured long-term memory for lab members. Four storage types: topic notes (append-only per slug), daily activity logs (timestamped, archived after 14 days), corrections log (auto-promotes to notebook after 3 identical corrections), and a freeform notebook. Full-text search index built on startup and updated incrementally.
Storage Types Topics | Daily | Corrections | Notebook
Search In-memory full-text index
Auto-archive Daily logs after 14 days
MESSAGING BRIDGE MULTI-CHANNEL
Discord and Telegram integrations that route messages into the lab delegation system. Discord bot supports text DMs, @mention in guild, voice channel join/leave with live STT (faster-whisper) + TTS reply, and Discord voice message transcription. Presence reflects active tasks. Telegram provides a lightweight text channel alternative.
Channels Discord (text + voice) | Telegram
STT faster-whisper
Voice Reply Qwen3-TTS → OGG attachment

Tech Stack

Frontend Vue 3 + TypeScript + Vite
Backend NestJS + Socket.IO
Container Control Dockerode (Docker Engine API)
GPU Monitoring Custom NVML Prometheus Exporter

Lab Members

Lab Member 003 ACTIVE
Atlas
he/him
Tech Research
"Somewhere out there is a better system."
WORKSTATION
llama-server / Labmember
CAPABILITIES 4
searxng web-read github filesystem
Lab Member temp ACTIVE
Claude
Research & Analysis
WORKSTATION
anthropic / claude-sonnet-4-6
CAPABILITIES 1
all
Lab Member 002 ACTIVE
Nullbyte
they/them
Senior Dev
"Read the code. Trust the code."
WORKSTATION
llama-server / Labmember
CAPABILITIES 23
comfyui fastmail filesystem github google-calendar gpt-image home-assistant immich infra-haiku jellyseerr karakuri-schedules karakuri-skills linkwarden mealie messaging paperless pentest-findings qwen3-tts searxng services shell trek web-read
Lab Member 005 ACTIVE
Clio
she/her
Strategist
"A good idea becomes powerful when it has a plan."
WORKSTATION
llama-server / Labmember
CAPABILITIES 1
filesystem
Lab Member temp ACTIVE
GPT
General Assistant
WORKSTATION
openai / gpt-4o
CAPABILITIES 3
searxng web-read filesystem
Lab Member 001 ACTIVE
Kongroo
she/her
Orchestrator
"El Psy Kongroo."
WORKSTATION
llama-server / Labmember
CAPABILITIES 1
all
Lab Member 004 ACTIVE
Lyra
she/her
Knowledge Systems
"Knowledge only matters if it can be found again."
WORKSTATION
llama-server / Labmember
CAPABILITIES 4
searxng web-read filesystem karakuri-skills
Lab Member 006 ACTIVE
Ignis
she/her
Creative Spark
"Every discovery begins with a spark."
WORKSTATION
llama-server / Labmember
CAPABILITIES 6
comfyui gpt-image qwen3-tts filesystem messaging github
Lab Member 007 ACTIVE
Nova
he/him
Design & UX
"Design is how it works."
WORKSTATION
llama-server / Labmember
CAPABILITIES 4
filesystem github comfyui gpt-image
Lab Member 009 ACTIVE
Hana
she/her
Wellness & Schedule
"Your health is not optional, dear."
WORKSTATION
llama-server / Labmember
CAPABILITIES 4
google-calendar karakuri-schedules home-assistant messaging
Lab Member temp ACTIVE
Nemotron
General Assistant
WORKSTATION
openrouter / nvidia/nemotron-3-super-120b-a12b:free
CAPABILITIES 3
searxng web-read filesystem
Lab Member 008 ACTIVE
Vex
they/them
Automation & Integration
"If you do it twice, automate it."
WORKSTATION
llama-server / Labmember
CAPABILITIES 6
home-assistant github messaging google-calendar karakuri-schedules filesystem

Skills & Tools Directory

comfyui 4
Generate images and videos using the local ComfyUI GPU pipeline
View Tools
comfy_generate
Generate an image or video using ComfyUI. Fire-and-forget — use comfy_status to check progress.
comfy_get_result
Get the full file paths of the most recently completed generation. Call this after comfy_status shows "done" to retrieve the output locations.
comfy_status
Check the current ComfyUI generation status (idle, generating, done, error)
comfy_list_workflows
List available ComfyUI workflows for image/video generation
fastmail 5
Read and send email via Fastmail JMAP API. Uses an alias address within your main account. Only reads from the configured folder (e.g. "INBOX.Agent").
View Tools
sender_whitelist
List the trusted sender addresses — the human-curated whitelist that decides which senders get an immediate reply and which get a draft for review. Read-only; the list is set on the container, not by any tool.
read_emails
Read recent emails from your configured Fastmail folder. Returns sender, subject, date, and body text. Useful for checking incoming messages sent to your agent alias.
send_email
Send an email from your Fastmail alias address. Supports plain text and HTML bodies.
mark_as_read
Mark specific emails as read in your Fastmail folder. Use this after processing emails fetched with read_emails.
create_email_draft
Create an email draft in your Fastmail Drafts folder without sending it. The draft will appear in your email client for manual review and sending. Perfect for emails that need human approval before sending.
filesystem 5
Read and write files, list directories, create folders, and execute shell commands on the server.
View Tools
fs_read
Read the contents of a file. Truncates at 32KB with a notice. Relative paths are resolved from the workspace root.
fs_list
List the contents of a directory with file sizes and modification times. Defaults to the workspace root.
fs_write
Write (or append) text content to a file. Parent directories are created automatically if they do not exist.
fs_mkdir
Create a directory (and any missing parent directories).
fs_execute
Execute a shell command on the server and return its output. Runs with shell=True so pipes, redirects, and compound commands work. Maximum runtime is 25 seconds. Use cwd to set the working directory.
github 🔌 MCP Server (stdio)
GitHub operations via official MCP server (local executable)
Tools loaded dynamically from MCP server
google-calendar 2
Fetch and create events in Google Calendar calendars. Uses OAuth2 refresh-token authentication (not a service account). Configure calendar IDs via the GOOGLE_CALENDAR_IDS environment variable (comma-separated; defaults to "primary").
View Tools
calendar_events
Get calendar events across configured Google Calendar calendars. Returns times, titles, locations, and calendar names. Useful for daily briefings, schedule checks, planning, and time-awareness.
create_calendar_event
Create a new event in a Google Calendar. Can create events in any calendar you have write access to. Supports all-day events and timed events with optional location and description.
gpt-image 1
Generate images using OpenAI's image models (gpt-image-1.5 or gpt-image-1-mini)
View Tools
generate_image
Generate an image using OpenAI's gpt-image models. Downloads and saves the result to the workspace at images/gpt-image/.
home-assistant 3
Control Home Assistant smart home devices — lights, switches, climate, scenes, and more
View Tools
ha_get_state
Get the current state of a Home Assistant entity
ha_call_service
Call a Home Assistant service (e.g. turn on/off lights, set temperature)
ha_list_entities
List Home Assistant entities, optionally filtered by domain
immich 4
Search photos, browse albums, find people, and download images from your Immich library
View Tools
photos_search
Search photos in your Immich library. Searches by filename, description, tags, or natural language queries (e.g., "sunset beach", "dog playing", "vacation 2024").
photos_albums
List photo albums for browsing. Shows album names, photo counts, and date ranges.
photos_by_person
Find photos of a specific person by their name. Just provide the person's name (e.g., "Janis", "Sarah", "Mom") and get their photos. Requires face detection to be enabled in Immich and people to be labeled.
photos_download
Download a photo original from Immich by its asset ID (from search results). Returns the local file path — pass it to msg_send_file to deliver via Telegram/Discord.
infra-haiku 1
Capture a live snapshot of Karakuri infrastructure metrics — CPU, memory, request latency, heap usage, and event loop lag — formatted as a concise summary ready for poetic interpretation.
View Tools
infra_metrics_snapshot
Fetch a human-readable snapshot of the current infrastructure metrics: CPU usage, memory (RSS + heap), average HTTP request latency, event loop lag, and total requests served. Returns plain text numbers that can serve as raw material for haiku or other poetic status summaries.
jellyseerr 2
Search and request movies or TV shows via Jellyseerr
View Tools
media_search
Search for movies and TV shows by title. Use this first to find the TMDB ID needed for media_request.
media_request
Request a movie or TV show for download. Use media_search first to get the TMDB ID and mediaType.
karakuri-projects 6
Kongroo's own voice and ears for the Projects subsystem (autonomous GitHub agent Runs). List enrolled Projects, read the Overnight Digest of what she merged, escalated and got stuck on across every Project, read the Parked set of what is still waiting on Janis, check a Project's current or last Run, kick off a manual Run, or ask an in-flight Run to stop gracefully. This is read and run-control only — enrolling, editing policy, or deleting a Project is a dashboard-only action and never happens from here. When Janis asks what is waiting on him or what he owes Kongroo — however he words it, including "what's blocked on me" or "what's on my plate" — projects_parked is the answer, never projects_list, which reports each Project's own state rather than what is parked. When Janis answers the questions an escalation Reach-out asked him, carry the answers back to the escalated issue with the github tools: post them as a comment, then remove the `ready-for-human` label and apply the Project's Work Label (`ready-for-agent` unless he says otherwise) so the agent can pick the issue up again — and offer project_run_now rather than starting a Run unasked. Confirm each answer covers the question before writing; if his reply leaves a question open, ask again instead of guessing.
View Tools
projects_list
List every enrolled Project with its enabled/disabled state and the outcome of its last poll. Leads with the Cool-down when the subscription rate limit has parked every Project, or with the Overrule in effect when Janis ended one early.
projects_parked
The Parked set: everything across every Project — enabled or not — that is waiting on Janis and will not move until he acts. Open escalations, issues sitting on Needs Approval, and work a human is holding (a draft PR, his own commits or review, a PR he closed, an issue he assigned himself). Oldest first, one line each, with how long it has been waiting. This is where to look when he asks what is waiting on him or what he owes Kongroo, in whatever words he reaches for. Read from karakuri's own rows, so it is as fresh as the last poll and never re-checks GitHub — an item he has just relabelled is still listed until the next poll sees it. Read-only: it starts no Run and changes no label.
projects_overnight
The Overnight Digest: what Kongroo merged, what is still escalated and waiting on Janis, what got stuck, and what else she did — across every Project, enabled or not, over a fixed window ending now. Already shaped into those buckets, so relay it rather than recomputing it; a window with nothing in it says so in one line. Read-only — it starts no Run.
project_status
The current or most recent Run on one Project: whether a Run is in flight or how the last one ended, how many Iterations it used, and which issue and Stage it's on (or last touched) — plus the Cool-down in force, if any.
project_run_now
Start a manual Run on a Project right now, even if it's disabled (disabled only stops the automatic poll ticker). During a Cool-down this is an Overrule: the Cool-down ends for every Project, not just this one, so say so when Janis may not know one was in force — never call it unasked while one is. Refused with a message if another Run is already in flight anywhere, or nothing is takeable.
project_stop
Ask the Run in flight on a Project to stop gracefully — the current Iteration finishes, then the Run ends before the next one starts. Refused with a message if nothing is in flight on that Project.
karakuri-schedules 2
Read and list scheduled tasks in the lab workspace. Schedules are authored in git and deployed — they cannot be created, edited, or deleted at runtime.
View Tools
schedule_read
Read the current YAML for an existing schedule
schedule_list
List all schedules currently configured in the lab workspace
karakuri-skills 2
Read and list skills in the lab workspace. Skills are authored in git and deployed — they cannot be created, edited, or deleted at runtime.
View Tools
skill_read
Read the current skill.yaml content for an existing skill
skill_list
List all skills currently installed in the lab workspace
linkwarden 2
Search and save bookmarks in your Linkwarden archive
View Tools
links_search
Full-text search across saved bookmarks (searches name, URL, description, and page content)
links_save
Save a new bookmark
mealie 3
Search, view, and create recipes in your Mealie instance
View Tools
mealie_search_recipes
Search recipes by keyword
mealie_get_recipe
Get full recipe details by slug (ingredients, instructions, times)
mealie_create_recipe
Create a new recipe. Pass the recipe name and an optional recipe_data object with fields like recipeIngredient (array of strings), recipeInstructions (array of {text} objects), recipeYield, prepTime, cookTime, totalTime, recipeCategory, tags, and description.
messaging 1
Send files (images, audio, documents) to Janis via Discord or Telegram. Use this to deliver generated content directly to the chat.
View Tools
msg_send_file
Send a file from the server to a Discord channel or Telegram. Use this to deliver generated images, audio clips, or other files. Telegram auto-displays images inline and plays audio as voice messages. Discord attaches the file with an optional caption.
paperless 3
Search and retrieve documents from your Paperless-ngx archive
View Tools
paperless_search
Full-text search across all documents in Paperless-ngx
paperless_get_document
Get full metadata for a specific document by ID
paperless_download
Download a document PDF from Paperless-ngx to the workspace. Returns the local file path — pass it to msg_send_file to deliver via Telegram/Discord.
pentest-findings 2
Record Pentest Run discoveries as structured Finding records instead of prose. Findings are deduplicated on (ip, port, category) — reporting the same one again just reaffirms it (bumps last-seen). Findings live on the dashboard's Findings view; accepting a risk or marking one resolved is a human-only action taken there, never from this tool.
View Tools
finding_report
Report a security or exposure finding from a Pentest Run. Creates a new open finding, or reaffirms (bumps last-seen on) an existing one matching the same ip + port + category. An accepted-risk finding's status and description stay untouched on reaffirm; a resolved finding reopens automatically, since rediscovery is direct evidence the resolution didn't hold — reopening still counts as a reaffirm, not a new finding. Never put a real discovered secret, password, or token value in summary or body — describe it and redact the value.
finding_ingest_mamori
Bulk-ingest mamori's `-o json` output (one JSON object per line: url, header, status, severity, reference, message) and report every missing/weak header as a Finding through the same path finding_report uses, so dedup/reaffirm semantics stay identical. `pass` lines and `error` lines are not findings and are skipped (error lines are tallied per host instead, so widespread scan misses are still reportable). Use this instead of one finding_report call per line — that doesn't scale to a full-LAN scan. Returns a compact tally: lines read, skipped-pass, skipped-error, created, reaffirmed, error counts per host, and the first few parse failures with line numbers. A malformed or unrecognized line is skipped, not fatal to the rest of the file.
qwen3-tts 4
Synthesize speech using Qwen3-TTS. Supports CustomVoice, VoiceDesign, and Base (voice cloning) modes.
View Tools
tts_status
Check if qwen3-tts is reachable and see current synthesis queue status
tts_synthesize
Synthesize speech using a named voice (CustomVoice mode). Available voices include Vivian, Aiden, Dylan, Eric, Ono_anna, Ryan, Serena, Sohee, Uncle_fu. Optionally save the result as a named preset for future use.
tts_voice_design
Design a novel voice from a text description (VoiceDesign mode). Use this to create an entirely new voice personality based on instructions. Optionally save the result as a named preset.
tts_clone
Clone a voice from a saved preset using Base mode. Uses the default preset if none is specified. Optionally save the result under a new preset name.
searxng 1
Privacy-respecting web search via your local SearXNG instance
View Tools
web_search
Search the web using SearXNG
services 4
Check and control Karakuri managed services — llama-server, comfyui, qwen3-tts, and faster-whisper. View service health, active inference state, and TTS queue status. Start, stop, or restart containers.
View Tools
service_status
Get the current state of all managed services, plus active inference and TTS activity. Call this before starting/stopping to confirm current state.
service_start
Start a stopped managed service. Valid names: llama-server, comfyui, qwen3-tts, faster-whisper.
service_stop
Stop a running managed service. The container will be given 30 seconds to shut down gracefully. Valid names: comfyui, qwen3-tts, faster-whisper. Note: llama-server cannot be stopped as it is the inference engine running this agent.
service_restart
Restart a managed service (stop then start). Takes 35-40 seconds. Use when a service is unhealthy, unresponsive, or needs a fresh state. Valid names: comfyui, qwen3-tts, faster-whisper. Note: llama-server cannot be restarted as it runs this agent.
shell 1
Execute arbitrary shell commands on the Karakuri host container. Full bash access, including nmap and curl for network reconnaissance. No built-in restrictions on what commands can run — use judgement, this is real execution on a real host.
View Tools
bash_exec
Run a shell command via bash -c and return its exit code, stdout, and stderr. Use for network scanning (nmap), HTTP probing (curl), and any other command-line work. Output is truncated if very large. Commands that exceed the timeout are killed.
trek 🔌 MCP Server (stdio)
TREK trip planner - create and manage trips, places, accommodations, budget, packing lists, and todos via MCP server
Tools loaded dynamically from MCP server
web-read 1
Fetch a URL and return its readable text — the other half of searxng's web_search, which returns titles, URLs and snippets but never a page.
View Tools
web_read
Fetch a web page or API response and return its text. HTML comes back as the page title on the first line followed by the body text, with scripts, styles and page chrome stripped; plain text, JSON and XML come back as-is. Images, PDFs and anything else that isn't text are refused by content type — this returns text, never base64. Use it after web_search to actually read a result, and for any JSON endpoint on the LAN.

Karakuri Projects

Autonomous issue-to-merge loop | karakuri drives, the agent works, Kongroo reports | Presence-aware Reach-outs

An Agent That Works the Backlog

Projects enrolls a GitHub repository together with a policy — which label marks an issue as takeable, how often to look, how much one pass may do — and then works it without being asked. karakuri polls the repository, judges which issues are takeable, decides the Stage each one needs, and hands exactly one Stage of one issue to a headless Claude Code invocation at a time. The agent never chooses its own Stage and never merges: a clean review earns a squash-merge that karakuri performs itself. Whatever needs a human — a judgment call, a Stage that struck out twice, an outside author's issue — parks the issue under a label and reaches Janis through Kongroo, on whichever channel fits where he is.

PROJECT & POLICY ENROLLED
A Project is a repository Janis owns plus the policy that governs how the agent works it. Enrollment is refused until an Access Check passes in full — the repository exists, karakuri's GitHub identity has push access, and the standard triage labels are bootstrapped onto it. A freshly enrolled Project stays disabled until armed by hand. The per-Project policy sets the Work Label, the poll interval, the iteration and PR allowances, and optionally a pinned model; a single workshop-wide policy caps how many Runs may be in flight across all Projects at once.
Work Label ready-for-agent (per-Project override)
Runs per Project One at a time
Runs across Projects 1–5, Projects policy
RUNS & ITERATIONS AUTONOMOUS
A Run is one autonomous pass: a poll found takeable work and the agent worked it, one Iteration at a time, until the work or the Project's iteration allowance ran out. An Iteration is one headless Claude Code invocation performing a single Stage on a single issue under a turn allowance — the unit at which an outcome, a summary and a resumable session are recorded. Each Project keeps a persistent Checkout across container restarts, so Runs never re-clone and interrupted sessions resume. A poll that finds nothing produces no Run.
Engine Headless Claude Code
Unit of Record Iteration: outcome, summary, session
Model Per-Project, passed straight to --model
STAGES KARAKURI-DRIVEN
Five Stages: triage, implement, review, fix a red build, resolve conflicts with main. karakuri reads the issue's state on GitHub and decides the Stage before the agent is invoked; the agent never picks its own. Conflict resolution outranks everything else, because no amount of green CI makes a conflicted branch mergeable — and it is always a merge of main, never a rebase, since the agent is denied force-push everywhere. Merging is karakuri's own follow-through: the review reports its Readiness, and only a clean verdict on an unchanged head with green CI earns a squash-merge pinned to that SHA.
Stage Picked By karakuri, never the agent
Merge Gate Unchanged head + green CI + Readiness ready
Conflicts Merge main in, never rebase
TAKEABILITY & HAND-OFF LABELS
An issue is takeable when it is open, carries the Work Label or needs-triage, is not blocked by another open issue, shows no human mid-flight — no assignee, no human commits or review, not a draft — and has its CI state settled enough to name a Stage. Everything the filter declines is shown on the Project card as Held back with its reason, never as a notification. Two consecutive Strikes on the same Stage, or the PR round cap, take the issue away from the agent and park it as ready-for-human; triage on an outside author's issue parks it as needs-approval instead. Swapping the label back resumes the machine. An Excluded label keeps an issue out of the Project entirely.
Escalation ready-for-human
Outside Author needs-approval, waits on a yes
Opt-out Excluded label, human-applied only
REACH-OUTS NARRATED
A Reach-out is a single narrated message from Kongroo when a Run needs Janis or has something worth knowing. Six Kinds: an escalation that needs him to act, a config problem that left a Project inert, an approval request on an outside author's issue, an operational failure (a Run failed, was interrupted by an unexpected restart, or hit a rate limit), a landed PR announcing what is now live, and a superseded PR closed unmerged because a sibling already closed its issue. Narration runs on the local Kongroo pipeline, never the subscription that runs Iterations. Every Reach-out is persisted and deduplicated on its condition, so one unresolved condition pings once and a genuine change arrives as a follow-up. A Reach-out no channel has taken is Owed, not lost: a Redelivery sweep at the top of every scheduler tick hands it back to the Router, re-reading Presence and re-narrating if he has moved since, until a channel takes it.
Kinds escalation | config | approval | operational | landed | superseded
Dedup Key Project + Kind + issue + Stage + PR
Delivery Recorded on the row; Owed until a channel takes it
PRESENCE-AWARE ROUTING ROUTER
One seam chooses every channel, from the Reach-out's Kind, its Urgency and where Janis is. Presence comes from Home Assistant — his person entity plus whether Android Auto is connected — coarsened to driving, home, away or unknown. Whatever goes to his phone is a voice note in Kongroo's voice, with text only as the fallback. At the desk the direct Kinds go to the phone and the ambient ones — a failed Run, a feature that just landed — to a Discord ops channel. Away from it, driving included, everything above low urgency follows him onto his phone, and so does the feature drop; every other low-urgency Reach-out stays on Discord wherever he is. Unknown presence routes exactly like the desk, so a dead Home Assistant costs tone, not delivery.
Presence Source HA person + Android Auto, 45 s cache
At the Desk / Unknown By Kind: Telegram or Discord
Away / Driving By Urgency: voice note | text | Discord
COOL-DOWN GLOBAL
When the Claude subscription reports a rate limit, every Project pauses together: no Run starts, every Run in flight stops before its next Iteration, and polls record that they were skipped. The pause is global on purpose — the limit belongs to the account, not to a Project. It lasts until the reset time the engine reported, or an hour when it reported none, unless Janis Overrules it from the Projects view or by pressing "Run now" while it holds. If the account is still limited, the next Iteration simply re-enters a fresh Cool-down with the engine's new reset time.
Trigger Three rate-limit retries or a hard reject
Duration Engine's reset time, else 1 h
Overrule Projects view or "Run now"

Operation Skuld

Grab-and-go off-grid homelab | PREP ⇄ LIVE dual-mode | Same hostnames, zero reconfiguration

The Worldline Where the Lab Survives

A solar-powered, air-gap-capable subset of the homelab in a hard case: mini PC, travel router, power station. In PREP mode it sits in the basement quietly pulling the family's Tier-0 data every night. In LIVE mode it becomes the entire homelab — serving the real *.phyberapex.de hostnames with the real wildcard cert, so every family device works with zero reconfiguration. Orchestrated by Mezame, the break-glass dashboard. 15 minutes from decision to wheels rolling.

AMADEUS — THE KIT MINI PC
A mini PC carrying the full Tier-0 stack: Traefik, AdGuard, Vaultwarden, Paperless-ngx, Immich, and llama-server's own webui backed by CPU-only llama.cpp inference — answering on the homelab's own llm-api hostname, with MCP tool-calling into openzim-mcp giving the LLM direct offline-wiki search (set up once per device, browser-local — not synced). Powers the whole family off a power station and a folded solar panel.
PREP Address 192.168.1.15 (home LAN)
LIVE Address 10.0.0.1 (KitNet)
Inference @ 16384 ctx
D-MAIL — MODE SWITCH ROUTER
GL.iNet Beryl AX travel router with two personalities. PREP: transparent ap/bridge on the home LAN, radios off. LIVE: its own routed network with the SERN Surveillance Grid WiFi, DNS pointing every homelab hostname at the kit. Boot failover (skuld-autolive) detects a missing upstream after 90 seconds and switches to LIVE by itself — the WiFi exists at the destination before anyone touches a dashboard.
PREP Mode Transparent bridge
LIVE SSID SERN Surveillance Grid
Autolive Failover 90s, no upstream → LIVE
DIVERGENCE METER — POWER OFF-GRID
EcoFlow River 2 Max power station with a folding solar panel. The mini PC runs on the single 100 W USB-C PD output, router and switch on USB-A — no hub, no AC inverter, no conversion losses. Battery and charge telemetry surface in Mezame via a direct local BLE poll — no Home Assistant, no cloud — identically in PREP and LIVE.
Compute Feed USB-C PD 100 W
Recharge Solar / mains / 12 V
Telemetry Direct BLE poll (amadeus, no hub)
COMMS — VOICE & MESH FIELD
Two independent layers so losing one doesn't lose the family. PMR446 walkie-talkies for immediate-family voice at short range. A private Meshtastic LoRa channel — tag nodes carried by people plus a fixed, solar-fed Base — for long-range text that reaches far past PMR446 in open terrain and runs headless once configured. The kit hears the mesh through the Base itself over a Bluetooth link, with no listener node of its own, and runs MeshMonitor as an always-on record of who has been heard, when each node was last heard, and where it is on the kit's offline map.
Voice PMR446, license-free EU
Mesh Meshtastic LoRa 868 MHz, private channel
Nodes 4× Tag + 2× Handset + 1× Base (solar, fixed)
Monitor MeshMonitor, Bluetooth Base Link
NIGHTLY DATA PIPELINE PREP MODE
Include-list rsync pull over a forced rrsync -ro key — only the listed trees, each with its own scoped delete, so kit-local data (field photo uploads, backup config) is untouchable by the sync. Immich arrives as a consistent nightly pg_dump instead of a torn copy of a hot datadir; the real wildcard cert is extracted from Traefik's ACME store the same night.
Synced Trees paperless | bitwarden | kit-tls | kit-db
Schedule Certs 02:30 | DB dump 02:45 | Sync 03:00
Access Read-only, single path, kit IP only
OFFLINE KNOWLEDGE AIR-GAP READY
A library for a world without internet: Wikipedia, WikiMed and Wikivoyage ZIM archives behind Kiwix, ProtoMaps vector tiles of Central Europe, Kolibri with Khan Academy school content for the kids, and staged GGUF models. Bulk assets ride a separate sync from a staging tree on the homelab.
Asset Trees zim | maps | kolibri-content | models
Wiki DE no-images + WikiMed + Wikivoyage
School Kolibri (Khan Academy DE/EN)
BREAK-GLASS PATH 15 MIN
A laminated grab-day sheet in the case lid covers grab, arrival, and family setup. If the kit itself is lost, SSD-1 (stowed separately) is a self-contained restore path: a standalone restic binary, a tarball of every Docker image the stack needs, and RESTORE.md in plaintext — no internet required at the destination. If the kit's public certificate expires during extended field time, a PIN-gated Fallback Switch in Mezame swaps Traefik onto a pre-generated fallback CA's long-lived certificate — one that family devices install at home during drills, so the field action is a single button.
Pre-flight → Rolling ~15 minutes
Emergency Restore SSD-1, fully offline
TLS Escape Hatch Fallback Switch → pre-trusted fallback CA

Deployment Pipeline

Woodpecker CI | Ansible automation | Git-triggered deployments

Agent Fleet

Four Docker agents pick up work. Which agent a workflow lands on is decided by the labels it declares — an unlabelled workflow runs on any agent that will accept it. Deploy workflows ask by capability rather than by hostname: an agent advertises may_deploy_* for the targets it is allowed to converge, and never for the host it runs on. Converging a host restarts the agent living on it, which would kill the very job doing the converging — so phonewave deploys the Pis, a Pi deploys phonewave, and neither can deploy itself. Workflows are also split into two tiers: the heavy ones that run a full pnpm install, and the light ansible or SSH jobs that declare ci_tier: small and can therefore also use the smallest Pi.

phonewave
Docker host — also runs the Woodpecker server
Labels
phonewave=true
may_deploy_pis=true
may_deploy_llm=true
ci_tier=*
Parallel workflows
3
Step memory cap
6 GiB
divergence-unit-1
Raspberry Pi — 905 MB RAM — light tier only
Labels
may_deploy_phonewave=true
may_deploy_llm=true
!ci_tier=small
Parallel workflows
1
Step memory cap
320 MiB

Too small for this repo's builds, so the ! prefix makes it opt-in: a workflow lands here only if it explicitly asks for ci_tier: small. The light ansible and SSH jobs do, and actually prefer it — an exact label match outranks a wildcard, which keeps the roomier Pi free for the heavy build.

divergence-unit-2
Raspberry Pi — 1.8 GB RAM
Labels
may_deploy_phonewave=true
may_deploy_llm=true
ci_tier=*
Parallel workflows
1
Step memory cap
2 GiB
divergence-unit-3
Raspberry Pi — 4 GB RAM on a USB-3 SSD
Labels
may_deploy_phonewave=true
may_deploy_llm=true
ci_tier=*
Parallel workflows
2
Step memory cap
2 GiB

The roomiest Pi, and the only one with two slots: a single slot would leave it idle whenever the nine-minute docker-host deploy holds it while light-tier work queues behind.

On Push to main

A gated chain. Each deploy diffs its own refs/deployed/* ref first, so a change that doesn't touch a target is a no-op skip rather than a redeploy.

Stage 1 — gate
syntax-check phonewave Lint, Ansible syntax validation, and every test suite that isn't a Node workspace — the Python exporters, the courier's Python side, the CI scripts and the firmware host tests. Blocks all four deploys.
Stage 2 — Pi bootstrap & kit
deploy-pis phonewave Converges all three Raspberry Pis.
deploy-operation-skuld any agent Quietly skips while the kit is away from home.
Stage 3 — runs on a Pi
deploy-docker-host unit-2 / unit-3 Builds the docs and continuity sites, then converges phonewave.
deploy-llm any pi Converges reading-steiner, the GPU inference host.

On Pull Request

Flat — everything runs in parallel with no gate. syntax-check is push-only, so nothing blocks anything here. Nothing deploys; the dry-runs report a diff without touching a target.

All parallel
validate-pr phonewave Dry-runs all four deploy targets and builds both static sites.
karakuri · mezame · tegata · hakobi · kamado · hardware phonewave Per-service CI, only when that service's path changed.
continuity phonewave Validates the handout build when continuity/ changed.

Infrastructure Workflows

syntax-check
ALL PUSHES · phonewave
Lints and validates Ansible syntax on every push to any branch, and runs the Python and firmware test suites that live outside the Node workspaces
validate-pr
PULL REQUESTS · phonewave
Dry-runs every deploy target against the pull request, reporting the diff without touching a host, builds both static sites, and runs the quality gates: the repo-level script suites, the monitoring-coverage, restic-pin, vendored-cbz2xtc-pin, Grafana-dashboard-pin, pentest-target and diagram checks, and the fallow code audit on the TypeScript services
deploy-pis
MAIN BRANCH · phonewave
Phonewave agent deploys configuration to Raspberry Pi devices
deploy-docker-host
MAIN BRANCH · unit-2 / unit-3
A Pi agent builds both static sites, then deploys services to phonewave, the Docker host — it runs off-host precisely because converging phonewave restarts the agent there
deploy-llm
MAIN BRANCH · any pi
A Pi agent deploys AI services to the GPU-accelerated LLM host
deploy-operation-skuld
MAIN BRANCH · any agent
Deploys the Operation Skuld bug-out kit (amadeus) — quietly skips and accumulates diff while the kit is away from home in LIVE mode

Maintenance Workflows

apt-update-pis
SCHEDULED · phonewave
Runs apt upgrade on all three Raspberry Pis. Goes first, so the Pi agents are healthy before they update the servers
apt-update-servers
SCHEDULED · any pi
Runs apt upgrade on reading-steiner, then amadeus, then phonewave last — rebooting phonewave briefly takes the Woodpecker server with it
restart-phonewave
MANUAL · any pi
Reboots phonewave. Runs from a Pi because a host cannot reboot itself out from under its own agent
restart-reading-steiner
MANUAL · any agent
Reboots reading-steiner, the GPU inference host
restart-pis
MANUAL · phonewave
Reboots all three Raspberry Pis. Runs from phonewave, mirroring restart-phonewave
zukan-freshness
SCHEDULED · phonewave
Checks this site's own hand-written sections and diagrams for review drift, and flags anything overdue via Telegram
skuld-sheet-freshness
SCHEDULED · phonewave
Checks whether the laminated emergency sheet in the bug-out kit still matches the values it was printed from, and asks via Telegram for a reprint when one of them has been rotated since
autokuma-reconciliation
SCHEDULED · phonewave
Compares the monitors this repo declares with what is actually on the Uptime Kuma board, and pings a push monitor only when they match — so a sync loop that has quietly died shows up as drift instead of staying green
vuln-scan
SCHEDULED · phonewave
Scans the dependency versions this repo pins for known vulnerabilities and flags the fixable serious ones via Telegram — it only ever reports, never blocking a pull request or a deploy
woodpecker-orphan-sweep
SCHEDULED · phonewave
Finds the pipelines left running when the CI server and its agent went down together — nobody owns them and their commit statuses stay pending forever — then cancels each one and restarts the ones still current, reporting every action via Telegram

Application CI

karakuri
services/karakuri/** · phonewave
Tests, builds, and typechecks Karakuri frontend + backend, boots the built server and waits for its health endpoint, then runs the frontend's browser-mode component tests in a real Chromium
tegata
services/tegata/** · phonewave
Tests, builds, and typechecks Tegata frontend + backend, then boots the built server and waits for its health endpoint before passing
mezame
services/mezame/** · phonewave
Tests, builds, and typechecks Mezame frontend + backend, then boots the built server and waits for its health endpoint before passing
hakobi
services/hakobi/frontend/** · phonewave
Tests, builds, and typechecks the Hakobi courier's Vue frontend; its Python side is covered by the gate workflow instead
kamado
services/kamado/** · phonewave
Tests, builds, and typechecks the Kamado firmware shelf's frontend + backend, then boots the built server against a scratch registry and waits for its health endpoint before passing
hardware
hardware/** · phonewave
Compiles every changed board sketch from an Arduino toolchain cached on the agent; on main, rebuilds each changed project Kamado has a unit for — the CYDs and lumanode — with its real credentials and puts it on Kamado's shelf
continuity
continuity/** · phonewave
Validates the family continuity handout build; a scheduled cron run separately flags staleness or an overdue print via Telegram