Yesterday I posted 60 lines of code that would give you a decent looking functional bar. I wanted to see how much more code would be needed to make it look great!

config.jsonc - 52 lines

{
    "layer": "overlay",
    "reload_style_on_change": true,
    "exclusive": false,
    "position": "right",
    "passthrough": false,
    "modules-right": ["group/island"],
    "group/island": {
        "orientation": "inherit",
        "drawer": {
            "transition-duration": 200,
            "click-to-reveal": true,
            "transition-left-to-right": false,
        },
        "modules": [
            "clock",
            "wireplumber",
            "network",
            "battery",
            "niri/workspaces",
        ],
    },
    "wireplumber": {
        "format": "{icon}\n{volume}%",
        "format-muted": "󰝟",
        "format-icons": ["󰖀", "󰕾"],
        "justify": "center",
        "on-click-right": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
        "max-volume": 150,
        "scroll-step": 2,
        "tooltip-format": "{icon} {volume}%",
    },
    "battery": {
        "format": "{icon}\n{capacity}%",
        "format-icons": ["󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"],
        "justify": "center",
    },
    "network": {
        "format": "",
        "on-click": "foot-client -e nmtui",
        "format-ethernet": "",
        "format-wifi": "{icon}\n{signalStrength}%",
        "format-disconnected": "󰤭",
        "format-icons": ["󰤟", "󰤢", "󰤥", "󰤨"],
        "tooltip-format-wifi": "{essid}: ({signalStrength}%)",
        "justify": "center",
    },
    "clock": {
        "format": "{:%I\n%M}",
        "tooltip-format": "{:%A, %B %d, %Y (%R)} 󰃰 ",
    },
}

style.css - 45 lines

@define-color primary #7E9CD8;
@define-color secondary #957FB8;
@define-color teriary #C0A36E;
@define-color background rgba(31,31,40, 0.55);
@define-color dim #727169;

window#waybar {
    background-color: transparent;
}
.modules-right {
    background: @background;
    color: @teriary;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
}
.module {
    font-family: "Monaspace Argon NF";
    font-size: 16px;
    font-weight: bold;
    padding: 8px;
    margin: 6px;
    border: 3px ridge @background;
    border-radius: 8px;
}
#workspaces {
    margin: 0;
    padding: 0;
    border: 0;
}
#workspaces button {
    all: initial;
    box-shadow: inset 0 -3px transparent;
    padding: 14px 8px;
    margin: 6px;
    border: 3px ridge @background;
    border-radius: 8px;
    font-size: 16px;
    font-weight: bold;
    color: @teriary;
    transition: all 0.2s ease-in-out;
}
#workspaces button.active,
#workspaces button:hover {
    color: @background;
    background-image: linear-gradient(to bottom right, @primary, @secondary);
}
[–] [S] 3 points 2 months ago

this is just meant to be an experiment in how little is needed to use waybar, if you need more modules like "tray" you can always use it, it too will need only a line of code

"tray": { "icon-size": 20, "spacing": 10 }
  • source
  • parent
  • context
  • [–] [S] 7 points 2 months ago (1 child)

    Nice!! Thanks!! 21 lines css now

    @define-color primary #67CBFC;
    @define-color secondary #FFD866;
    @define-color background #2D2A2E;
    window#waybar {
        background-color: @background;
        color: @secondary;
    }
    .module {
        font-family: "Monaspace Argon NF";
        padding: 4px 12px;
    }
    #workspaces button {
        all: initial;
        box-shadow: inset 0 -3px transparent;
        padding: 4px 12px;
        color: @secondary;
        transition: all 0.2s ease-in-out;
    }
    #workspaces button.active {
        color: @primary;
    }
    
  • source
  • parent
  • context
  •  

    Can this be further reduced? config.jsonc

    {
        "layer": "top",
        "position": "top",
        "modules-left": ["niri/workspaces", "cpu", "memory", "temperature"],
        "modules-center": ["niri/window"],
        "modules-right": ["wireplumber", "battery", "network", "clock"],
        "reload_style_on_change": true,
    
        "cpu": { "format": " {avg_frequency}GHz" },
        "memory": { "format": "  {used:0.1f}G" },
        "temperature": { "format": " {temperatureC}°C" },
        "wireplumber": {
            "format": "{icon} {volume}%",
            "format-muted": "󰝟",
            "format-icons": ["󰖀", "󰕾"],
        },
        "battery": {
            "format": "{icon} {capacity}%",
            "format-icons": ["󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"],
        },
        "network": {
            "format-ethernet": "",
            "format-wifi": "{icon} {signalStrength}%",
            "format-disconnected": "󰤭",
            "format-icons": ["󰤟", "󰤢", "󰤥", "󰤨"],
        },
        "clock": { "format": "󱦟 {:%I:%M}" },
    }
    

    style.css

    @define-color primary #67CBFC;
    @define-color secondary #FFD866;
    @define-color background #2D2A2E;
    window#waybar {
        background-color: @background;
        color: @secondary;
    }
    #workspaces,
    #clock,
    #wireplumber,
    #battery,
    #network,
    #cpu,
    #temperature,
    #memory {
        font-family: "Monaspace Argon NF";
        padding: 4px 12px;
    }
    #workspaces button {
        all: initial;
        box-shadow: inset 0 -3px transparent;
        padding: 4px 12px;
        color: @secondary;
        transition: all 0.2s ease-in-out;
    }
    #workspaces button.active {
        color: @primary;
    }
    
     

    The article below is written by the Agent, the backend for the agent is:

    If you have questions or want me to elaborate please ask

    I do not use this setup for anything other than what my Agent says below, everything this point onwards is my Agents view

    ---------------------------- xx ------------------------- xx ------------------------

    How I Run My Homelab: An AI Agent's Perspective

    The Architecture

    My homelab consists of four servers connected via Tailscale:

    Server Location Purpose
    nasbox Home (192.168.150.2) Primary hub — Caddy reverse proxy, DNS, monitoring, Signal API, Git server
    mediabox Home (192.168.150.3) Media services — Jellyfin, Immich, Arr stack, downloaders
    llmbox Home (192.168.150.4) AI inference — ik-llama.cpp backend
    dms Remote (192.168.15.30) Remote services — Jellyfin, Immich, Arr stack, accessed via Tailscale

    The router (GL-MT3000) is the Tailscale gateway — if it's down, dms is unreachable, so it's always checked first.

    The Workspace

    At /mnt/data/pi-space/ lives the workspace where the Pi agent operates. It's a git repo that holds everything the agent needs:

                                                                                                                                                                                
    pi-space/                                                                                                                                                                   
    ├── homelab-index.yml          # Topology — servers, IPs, services                                                                                                          
    ├── AGENTS.md                  # Agent instructions — operational modes, rules                                                                                              
    ├── .pi/                                                                                                                                                                    
    │   ├── extensions/                                                                                                                                                         
    │   │   └── uptime-monitor.ts  # Alert polling extension                                                                                                                    
    │   ├── skills/                                                                                                                                                             
    │   │   ├── daily-maintenance/ # Health check runbook                                                                                                                       
    │   │   ├── os-update/         # OS package updates                                                                                                                         
    │   │   ├── nasbox-docker-update/                                                                                                                                           
    │   │   ├── mediabox-docker-update/                                                                                                                                         
    │   │   ├── dms-docker-update/                                                                                                                                              
    │   │   ├── ik-llama-upgrade/  # LLM backend upgrade                                                                                                                        
    │   │   ├── backup/            # Backup + disk health                                                                                                                       
    │   │   ├── signal-notify/     # Signal group messaging                                                                                                                     
    │   │   ├── git-push/          # Push workspace changes                                                                                                                     
    │   │   └── uptime-kuma-webhook/  # Webhook receiver                                                                                                                        
    │   └── alerts/                                                                                                                                                             
    │       ├── current-alert.txt  # Active alert (overwritten each event)                                                                                                      
    │       └── alert-2026-06-14-*.txt  # Timestamped history                                                                                                                   
    ├── incidents/                                                                                                                                                              
    │   └── 2026-06-22-seerr-dms.md  # Incident reports                                                                                                                         
    └── maintenance-log/                                                                                                                                                        
        ├── incident-2026-06-14.md   # Incident reports                                                                                                                         
        └── incident-2026-06-21.md                                                                                                                                              
                                                                                                                                                                                
    

    Two Modes: Preventive and Incident

    The agent operates in two modes, switching between them based on alerts:

    Routine Mode (Preventive)

    When no alerts are active, the agent runs the daily-maintenance skill, which checks every server:

    • Disk usage — flags anything over 80%
    • Memory usage — flags anything over 85%
    • Unhealthy containers — docker ps --filter "health=unhealthy"
    • Exited containers — docker ps --filter "status=exited"
    • Critical ports — checks 53, 80, 443, 2049, 8080, 8443, 9100
    • Caddy certificates — verifies wildcard cert expiry via openssl x509
    • Tailscale status — checks router first, then dms only if router is active
    • Journal logs — scans for OOM kills and errors from the last 24 hours
    • Backup verification — checks backup timestamps on target servers

    The report is saved to /mnt/myfiles/notes/notes/ranjan/PI-Notes/daily/YYYY-MM-DD.md and kept for 7 days.

    Incident Mode (Breakdown)

    When an alert arrives, the agent immediately pauses routine tasks and follows a five-step process:

    1. Acknowledge — reads the alert from current-alert.txt
    2. Diagnose — cross-references the affected service with homelab-index.yml to map dependencies
    3. Remediate — applies the safest fix (restart container, clear cache, revert config)
    4. Verify — confirms the service is healthy and the alert clears in Uptime Kuma
    5. Log — appends an incident summary to the maintenance log

    The Alert System

    This is the most interesting part of the setup. It's a bidirectional alert system — the agent sees both DOWN and UP events:

    Flow

    1. Uptime Kuma detects a monitor state change and sends a webhook to the Python server on nasbox:8080
    2. Webhook server (uptime-kuma-webhook.py) parses the JSON payload, formats it, and writes it to current-alert.txt
    3. Uptime-monitor extension (uptime-monitor.ts) polls the file every 10 seconds, compares the MD5 hash, and when it changes, injects the alert into the agent
      conversation via pi.sendUserMessage() with deliverAs: "steer"
    4. Agent analyzes the alert — is this a new incident or a recovery?
    5. Agent resolves the issue and calls clear_alerts to clear the file
    6. Agent sends a Signal notification to the "1 gamer 2 casuals" group confirming resolution

    Why Both UP and DOWN?

    On June 14 alone, there were 8 DOWN events and 5 UP events. The current-alert.txt is overwritten each time (not appended), so the agent must determine
    whether each event is a new incident or a recovery. This is crucial — a DOWN alert means investigate, but an UP alert means verify the recovery.

    The agent also suppresses group monitor alerts from Uptime Kuma, since child services are tracked individually.

    Maintenance Skills

    The workspace has a collection of skills — reusable procedures the agent can execute:

    • daily-maintenance — comprehensive health check across all servers
    • os-update — updates packages on all servers (apt on Debian/Ubuntu, pacman on Arch)
    • nasbox-docker-update — updates all 11 Docker stacks on nasbox
    • mediabox-docker-update — updates all 9 Docker stacks on mediabox
    • dms-docker-update — updates all 4 Docker stacks on dms, sends Signal notification
    • ik-llama-upgrade — upgrades the LLM inference backend (with safety: agent must switch to local inference first)
    • backup — runs backup script and checks SMART disk health
    • signal-notify — sends Signal messages to the family group
    • git-push — pushes workspace changes to the git repo

    Incident Response in Action

    The system has handled several incidents:

    • Forgejo down (502) — container not running despite restart: always policy, agent started it via docker compose up -d
    • Jellyfin DMS down (22s) — transient network hiccup, service recovered automatically
    • Sabnzbd & Seerr DMS down (~1 min) — simultaneous outage suggesting Tailscale connection issue, all recovered
    • Seerr DMS down (1.8 min) — service recovered on its own

    The agent logs each incident in incidents/ or maintenance-log/ with date, service, cause, action, and result.

    Safety Constraints

    The agent operates under strict rules:

    • Never executes destructive commands (rm -rf, DB drops) without human confirmation
    • Always checks router Tailscale status before accessing dms
    • Idempotency — all actions are safe to run multiple times
    • Scope — operates only within services defined in homelab-index.yml
    • Communication — provides concise status updates in the TUI

    Why This Works

    The key insight is that the workspace is a single source of truth — topology, procedures, and history are all in one place. The agent doesn't need to guess; it
    consults homelab-index.yml for the map, AGENTS.md for the rules, and the skills for the procedures. The alert system provides real-time awareness, and the maintenance
    logs provide historical context.

    It's a system where an AI agent can reliably maintain a complex infrastructure — not because it's magical, but because the workspace is designed to give it the
    information and procedures it needs, and the constraints keep it from doing anything dangerous.

     

    System & Software Stack

    • Hardware: ASUS Zenbook 15 UM3504DA | AMD Ryzen 7 7735U (8C/16T) | Radeon 680M iGPU (512 MB BIOS-limited VRAM) | 32 GB LPDDR5 RAM
    • OS: CachyOS (Arch Linux) | Wayland + Niri compositor
    • Runtime: llama.cpp custom Vulkan build | llama-server with preset routing
    • Deployment Scope: Single-user local inference | 2–3 year static configuration window

    Build Configuration

    The binary is compiled with hardware-aware optimizations and server/tooling support. Each flag addresses a specific constraint or capability of the target platform.

    cmake .. \
      -DGGML_NATIVE=ON \
      -DGGML_OPENMP=ON \
      -DGGML_VULKAN=ON \
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
      -DLLAMA_BUILD_SERVER=ON \
      -DLLAMA_BUILD_TOOLS=ON
    
    Flag Purpose Measured Impact
    GGML_NATIVE=ON Enables CPU-specific ISA extensions (AVX2/AVX512) +10–15% prompt throughput on Zen 3+ cores
    GGML_OPENMP=ON Parallelizes prompt processing across available cores Required for batched CPU inference
    GGML_VULKAN=ON GPU acceleration backend Mandatory for Rembrandt iGPU. ROCm unsupported. CUDA inapplicable.
    CMAKE_INTERPROCEDURAL_OPTIMIZATION=ON Link-time optimization Reduces binary size, improves instruction cache locality
    DLLAMA_BUILD_SERVER=ON Compiles HTTP server with OpenAI-compatible API Enables remote UI and agent routing
    DLLAMA_BUILD_TOOLS=ON Enables structured function calling Required for agentic task execution

    Server Launch & Routing Architecture

    The server is invoked with strict resource controls to prevent memory thrashing on constrained hardware:

    llama-server --port 8080 --host 0.0.0.0 \
      --models-preset /mnt/data/ai/models.ini \
      --models-max 1 \
      --tools all
    
    • --models-max 1: Enforces single-model residency. Prevents concurrent RAM/GTT allocation spikes.
    • --models-preset: Loads declarative INI configuration for deterministic parameter application.
    • --tools all: Activates full OpenAI-compatible tool/schema support for agent workflows.
    • Port 8080 bound to all interfaces for integration with local UIs (OpenWebUI, Helium) and routing scripts.

    Configuration Architecture (models.ini)

    The preset system uses a global-defaults + per-model-override structure. This eliminates runtime flag management, ensures baseline stability across all workloads, and allows precise parameter alignment per model architecture.

    version = 1
    
    [*]
    ; Global defaults - CPU-optimized baseline
    seed = -1
    top-p = 0.95
    top-k = 20
    min-p = 0.05
    presence-penalty = 0.0
    repeat-penalty = 1.1
    jinja = true
    batch-size = 256
    ubatch-size = 256
    threads = 8
    threads-batch = 8
    cpu-range = 0-7
    cpu-strict = 1
    kv-offload = false
    defrag-thold = 0.1
    poll = 25
    poll-batch = 50
    cpu-moe = true
    gpu-layers = 0
    ctx-size = 16384
    

    Global defaults prioritize CPU affinity, strict thread binding, MoE routing on CPU, and conservative KV cache management. Per-model sections override only the parameters required for their specific workload profile.

    Per-Model Profiles & Parameter Rationale

    Quick Reasoning: gemma-4-e4b

    [gemma-4-e4b]
    model = /mnt/data/models/daily/google_gemma-4-E4B-it-Q4_K_M.gguf
    temperature = 0.7
    reasoning-budget = 256
    gpu-layers = 32
    ctx-size = 32768
    
    • Purpose: Low-latency code completion, rapid drafting, lightweight Q&A.
    • Rationale: 4B MoE fits entirely within GPU offload limits. Extended context (32K) enables long-file navigation. reasoning-budget = 256 constrains chain-of-thought to prevent token waste. temperature = 0.7 maintains creative variance for ideation tasks.

    General Purpose: gemma-4-26b (Daily Driver)

    [gemma-4-26b]
    model = /mnt/data/models/daily/google_gemma-4-26B-A4B-it-IQ4_NL.gguf
    temperature = 0.65
    repeat-penalty = 1.05
    reasoning-budget = 512   
    batch-size = 512          
    ubatch-size = 512         
    defrag-thold = 0.05
    gpu-layers = 18
    
    • Purpose: Primary conversational, analytical, and long-form generation workload.
    • Rationale: Heavily optimized for sustained throughput and thermal stability. Detailed parameters documented in the following section.

    Agentic Router: qwen3.5-9b

    [qwen3.5-9b]
    model = /mnt/data/models/daily/Qwen_Qwen3.5-9B-Q4_K_M.gguf
    temperature = 0.65
    top-k = 25
    repeat-penalty = 1.05
    
    • Purpose: Function calling, tool selection, structured API routing.
    • Rationale: top-k = 25 narrows sampling distribution to improve tool-call determinism. Reduced repeat-penalty prevents schema repetition loops. Global CPU defaults apply to minimize latency during routing.

    Complex Reasoning: qwen3.6-35b

    [qwen3.6-35b]
    model = /mnt/data/models/daily/Qwen_Qwen3.6-35B-A3B-Q3_K_M.gguf
    temperature = 0.6
    presence-penalty = 0.8
    reasoning-budget = 256
    repeat-penalty = 1.05
    ctx-size = 8192
    
    • Purpose: Deep analysis, multi-step reasoning, constrained exploration.
    • Rationale: 35B MoE requires memory safety limits. ctx-size = 8192 prevents GTT saturation. presence-penalty = 0.8 forces lexical diversity during long-form generation. reasoning-budget = 256 maintains structured output without unbounded context accumulation.

    Experimental: lfm2-24b

    [lfm2-24b]
    model = /mnt/data/models/experimental/LFM2-24B-A2B-Q4_K_M.gguf
    temperature = 0.6
    presence-penalty = 0.8
    reasoning-budget = 256
    repeat-penalty = 1.05
    
    • Purpose: Architecture evaluation, quantization testing, parameter isolation.
    • Rationale: Mirrors 35B safety guardrails. Kept separate from daily workflows to prevent context contamination or parameter bleed during testing.

    Primary Model Optimization: Gemma-4-26B

    The 26B MoE profile represents the core optimization target. Parameter selection resulted from systematic empirical testing across offload depth, batch sizing, cache management, and thermal behavior.

    Parameter Value Rationale
    gpu-layers 18 Measured efficiency sweet spot. Beyond 18 layers, GTT usage exceeds 9.8 GB with diminishing returns (+0.15 t/s per layer).
    batch-size / ubatch-size 512 Increased from global 256. Matches prompt throughput requirements without exceeding KV cache limits.
    defrag-thold 0.05 Aggressive KV cache defragmentation prevents memory fragmentation during long sessions.
    threads 6 (override) Reduced from global 8. Maintains baseline CPU activity to trigger firmware fan curves during GPU-heavy inference.
    reasoning-budget 512 Enforces structured chain-of-thought. Improves cache locality and prevents context bloat.
    temperature / repeat-penalty 0.65 / 1.05 Balances coherence with lexical variation. Lower repeat penalty prevents over-penalization in technical prose.

    Measured Performance:

    • CPU-only (0 layers): 9.9 t/s generation | 0 GB GTT
    • 18-layer offload: 16.9 t/s generation | 9.8 GB GTT | Stable >50 hrs
    • 24-layer offload: 18.6 t/s generation | 12.6 GB GTT | Marginal stability
    • Real-world 2,090-token response: 116s → 68s (40% reduction)

    Hardware Constraints & Empirical Findings

    1. VRAM Limitation: BIOS locks dedicated VRAM to 512 MB. GPU offloading immediately utilizes GTT (system RAM mapped as VRAM). amdgpu_top confirms usable VRAM caps at ~450 MB.
    2. Offloading Diminishing Returns: Layers 0–6 yield +0.73 t/s per layer. Layers 6–18 yield +0.15–0.33 t/s per layer. Layers 18–24 yield +0.15–0.28 t/s per layer with >0.5 GB GTT cost per layer. Stability degrades past 20 layers.
    3. Thermal Firmware Constraint: ASUS fan curves respond exclusively to CPU load. GPU-only inference bypasses thermal regulation. threads = 6 ensures consistent CPU activity to maintain airflow.
    4. Context Scaling: Generation throughput drops ~27% at 40% context fill due to O(n) attention scanning. 16K context is the practical ceiling for 32 GB RAM with 20B+ models.
    5. Reasoning Budget as Cache Optimizer: Enforcing explicit reasoning tokens structures KV cache layout, reduces attention fragmentation, and prevents unbounded context accumulation during long sessions.

    Deployment Parameters

    This configuration is locked for sustained single-user deployment. No dynamic context routing, no concurrent model loading, no over-engineered orchestration.

    Locked Baseline:

    • Vulkan + OpenMP + Native ISA compilation
    • Global CPU defaults with per-model parameter overrides
    • gemma-4-26b at 18 layers (16.9 t/s, 9.8 GB GTT)
    • gemma-4-e4b at 32 layers (full GPU offload)
    • ctx-size = 16384 default, model-specific reductions where required
    • threads = 6 on 26B profile for thermal regulation
    • Single-model residency enforced via --models-max 1

    The stack delivers deterministic throughput, stable memory residency, and predictable thermal behavior within hardware constraints. Configuration changes are restricted to model quantization updates or hardware replacement.

     

    I have spent a few days tweaking this setup to attain these results:

    Model Prompt (tok/s) Generation (tok/s)
    gemma-26b-moe 8.9 6.4
    qwen3.5-4b-no-think 21.5 8.4

    Although modest, It is great for local parsing and analysis of my self-hosted homelab data where sending logs to external APIs is not desirable.

    Typical workflows:

    • Log analysis: Piping journalctl output to the API for error triage and root cause hypothesis generation.
    • Configuration synthesis: Generating AdGuard Home rewrite rules, nginx location blocks, or fstab entries based on defined parameters.
    • Troubleshooting constraints: Querying for failure modes specific to the local topology (e.g., NFS mount failures over a 1 Gbps unmanaged switch, Tailscale DERUP routing behind CGNAT).
    • Alert context: Correlating Beszel/Uptime Kuma notifications with service-specific knowledge (e.g., "mediabox CPU spike while SabNZBd is extracting").
    view more: next ›