Overview

HomelabDashboard is a native Android application built as the primary management interface for a self-hosted AI infrastructure monitoring agent. It replaced a Telegram bot as the main interaction point and handles everything from live container monitoring to push notification delivery with inline action controls.

Built with Kotlin and Jetpack Compose on a Samsung S21, the app communicates with a Flask REST API running on a Proxmox LXC container via a Cloudflare tunnel. No VPN, no direct Proxmox exposure. The backend is described separately at Homelab AI Monitoring Agent .

This was built from scratch across a series of development sessions using AI-assisted development throughout. The architectural decisions, debugging, and infrastructure integration were hands-on throughout.


Why Native Android

Cross-platform frameworks were evaluated and rejected early. The deciding factors were push notifications and action controls.

Firebase Cloud Messaging delivers alerts to the notification shade with ACK and Snooze action buttons that post directly back to the agent API without opening the app. This required a native broadcast receiver. A web dashboard cannot do this reliably. A cross-platform framework adds a layer of abstraction that complicates FCM integration without providing meaningful benefit for a single-device personal tool.

Jetpack Compose was chosen over the legacy View system because it is the current standard for Android UI development. The declarative approach suited the data-driven nature of the dashboard - infrastructure state changes, UI reflects it.


Screens

Dashboard

The landing screen. Shows agent connectivity status, a colour-coded grid of all 25+ containers sorted by VMID, VPS health, PBS backup status, and current AWS Bedrock spend for the month. Pull to refresh throughout.

Tapping any container opens a detail sheet with CPU, memory, and disk progress bars and start/stop/reboot action buttons. Every destructive action triggers a Proxmox snapshot attempt before execution, with a graceful fallback for containers that cannot be snapshotted due to bind mounts. Optimistic state updates mean the UI reflects the action immediately while the API call completes in the background.

Costs

Full AWS Bedrock cost breakdown by day and model tier. A scrollable monthly history strip sits at the top, showing cumulative spend per month as the history accumulates. The current month typically sits under $0.03 USD.

Services

A launcher for 22 self-hosted applications, each displaying live container status sourced from the metrics API. Applications are shown in a two-column grid with their service icon and an online/offline indicator. Tapping opens the service URL in the browser. Useful for confirming a service is actually running before investigating a playback or access issue remotely.

Ask / Simulate

Two modes accessible via a toggle at the top of the screen.

Ask mode sends a natural language question to the agent’s /ask endpoint. The agent queries current metrics, baseline deviation data, and recent alert state, then routes the question through AWS Bedrock Nova Micro to produce a plain-language answer grounded in actual infrastructure state.

Simulate mode sends a what-if query to the /simulate endpoint. The question is converted to a structured scenario by Bedrock Micro, run through the full alert and action pipeline without touching live infrastructure, and returned as a structured report. The screen shows a four-number summary strip, colour-coded alerts by severity, proposed actions with their safety gate labels and snapshot flags, and optionally a full AI narrative. Six preset scenarios cover the most common failure modes and are available as one-tap chips.

Alerts

Full alert history in reverse chronological order. Open alerts show severity colour coding and inline ACK and Snooze controls that post directly to the agent API. Resolved alerts display a resolved chip with timestamp. Snoozed alerts show the snooze expiry.


Push Notifications

Firebase Cloud Messaging delivers alerts generated by the agent’s alert_monitor.py service. The notification payload includes the container name, alert type, severity, and metric value.

Notifications include two action buttons - Acknowledge and Snooze 1h - handled by AlertActionReceiver.kt, a broadcast receiver that fires without launching the app UI. On action, it posts to the agent’s acknowledge or snooze endpoints and dismisses the notification. The full interaction from alert detection to acknowledged takes under ten seconds.


Key Technical Decisions

OkHttp over Ktor or Retrofit - OkHttp is direct, well-understood, and the API surface is simple enough that a networking framework adds more abstraction than value. All API calls are suspend functions running on the IO dispatcher.

Optimistic state updates via mutableStateMapOf - Container start/stop/reboot actions update the UI immediately rather than waiting for the API round trip. A runningOverrides map holds the optimistic state and is cleared on the next full refresh.

No local database - All state lives in the agent’s SQLite database on CT900. The app is a display and control surface, not a data store. This keeps the app simple and ensures the phone and any other client always see the same state.

Single activity, Compose navigation - Bottom navigation bar with five tabs, all managed in a single AppShell composable. No fragments. Navigation state is simple enough that a full navigation library was not warranted.


Outcomes

  • Full native Android app built from scratch in Kotlin/Jetpack Compose
  • Push notifications with inline action controls working end to end
  • Sub-second UI response on container actions via optimistic updates
  • Simulation engine queryable from the phone with natural language input
  • Clean APK distributed directly to device, no Play Store required

Technologies

Kotlin - Jetpack Compose - Firebase Cloud Messaging - OkHttp - Material 3 - AWS Bedrock (via agent API) - Cloudflare Tunnels - Proxmox VE (via agent API)


Code

Published at github.com/mrapierre alongside the agent it connects to. The google-services.json Firebase configuration file is excluded - instructions for substituting your own are in the README.