← All Customers

{{statusIcon .OverallStatus}} {{if .CustomerName}}{{.CustomerName}}{{else}}{{.CustomerID}}{{end}}

{{if .HasReports}}

Last report: {{timeAgo .Customer.ReceivedAt}} · Controller {{.Customer.ControllerVersion}}

{{else}}

No reports received yet

{{end}}
{{if .Flash}}
{{if eq .Flash "created"}}Configuration created successfully. {{else if eq .Flash "updated"}}Configuration updated. {{else if eq .Flash "password_regenerated"}}Retrieval password regenerated. {{else if eq .Flash "blocked"}}Customer blocked — hidden from Dashboard. {{else if eq .Flash "unblocked"}}Customer unblocked — visible on Dashboard again. {{end}}
{{end}} {{if .IsBlocked}}
This customer is blocked — reports are accepted but not shown on the Dashboard.
{{end}}

Customer Info

{{if .HasConfig}} Edit {{if .IsBlocked}}
{{.CSRFField}}
{{else}}
{{.CSRFField}}
{{end}}
{{.CSRFField}}
{{else}}
{{.CSRFField}}
{{end}}
Customer ID {{.CustomerID}}
Name {{if .CustomerName}}{{.CustomerName}}{{else}}—{{end}}
Domain {{if .Domain}}{{.Domain}}{{else}}—{{end}}
Email {{if .Email}}{{.Email}}{{else}}—{{end}}
Config {{if .HasConfig}} MANAGED {{else}} MANUAL {{end}} {{if .IsBlocked}}BLOCKED{{end}}
{{if .HasConfig}}
Config Created {{timeAgo .Config.CreatedAt}}
{{end}}
{{if .HasReports}}

System

{{with .Report.system}}
Hostname {{index . "hostname"}}
OS {{index . "os"}}
Kernel {{index . "kernel"}}
CPU {{index . "cpu_model"}} ({{index . "cpu_cores"}} cores)
{{end}}
CPU {{formatFloat .Customer.CPUPercent}}%
Memory {{formatFloat .Customer.MemoryPercent}}%

Storage

{{with .Report.storage}}
{{range .}}
{{with index . "label"}}{{.}}{{else}}{{index . "mount"}}{{end}} {{printf "%.0f" (index . "percent")}}%
{{printf "%.1f" (index . "used_gb")}} / {{printf "%.1f" (index . "total_gb")}} GB
{{end}}
{{end}}

Containers ({{.Customer.ContainerRunning}}/{{.Customer.ContainerTotal}})

{{with .Report.containers}} {{$list := index . "list"}} {{if $list}} {{range $list}} {{end}}
Name State CPU Memory
{{index . "name"}} {{index . "state"}} {{printf "%.1f" (index . "cpu_percent")}}% {{printf "%.0f" (index . "memory_mb")}} MB
{{end}} {{end}}

Backup

{{with .Report.backup}}
Enabled {{if index . "enabled"}}Yes{{else}}No{{end}}
Snapshots {{index . "snapshot_count"}}
Repo Size {{index . "repo_size_mb"}} MB
Integrity {{if index . "integrity_ok"}}OK{{else}}Unknown{{end}}
{{end}}
{{if .HasReports}} {{with .Report.geo_restriction}}

Geo-korlátozás

Állapot {{if index . "enabled"}} Aktív {{else}} Inaktív {{end}}
{{if index . "enabled"}}
Engedélyezett országok {{$countries := index . "allowed_countries"}} {{if $countries}} {{range $i, $c := $countries}}{{if $i}}, {{end}}{{$c}}{{end}} {{else}} — {{end}}
{{end}} {{if index . "last_sync"}}
Utolsó szinkron {{index . "last_sync"}}
{{end}} {{if index . "last_sync_error"}}
Szinkron hiba {{index . "last_sync_error"}}
{{end}}
{{$overrides := index . "app_overrides"}} {{if $overrides}}

Alkalmazás felülírások

{{range $app, $override := $overrides}} {{end}}
AlkalmazásEngedélyezett országok
{{$app}} {{$ac := index $override "allowed_countries"}} {{if $ac}}{{range $i, $c := $ac}}{{if $i}}, {{end}}{{$c}}{{end}}{{else}}—{{end}}
{{end}} {{if index . "enabled"}}
{{end}}
{{end}} {{end}}

Health

{{if eq .OverallStatus "disabled"}}

Reporting has been disabled on this node

Enable it in the controller's controller.yaml: hub.enabled: true

{{else if eq .OverallStatus "blocked"}}

Customer is blocked

{{else}} {{with .Report.health}}

Status: {{index . "status"}}

{{$issues := index . "issues"}} {{if $issues}}

Issues

{{end}} {{$warnings := index . "warnings"}} {{if $warnings}}

Warnings

{{end}} {{end}} {{end}}
{{else}} {{if .HasConfig}}

Waiting for First Report

This customer has been configured but no controller report has been received yet.

Use one of the setup commands below to deploy the controller on the customer node.

{{end}} {{end}} {{if .HasConfig}}

Credentials

Retrieval Password
{{.Config.RetrievalPassword}}
{{.CSRFField}}
API Key
{{.Config.APIKey}}
Used by the controller for ongoing hub communication (reports, notifications, backups)

Setup Command

Day-0 host bootstrap. Run on a freshly-PVE-installed Proxmox host as root (create the customer in the hub first). It enrolls the host, installs + verifies the agent, and provisions the guest; the in-guest controller then pulls its own controller.yaml. The retrieval passphrase is entered at the no-echo prompt — never on the command line.

Option 1: Online install (recommended)

Fetches the script over TLS from felhom.eu, then runs it. Download-then-run (not curl | sudo bash) so you can inspect the script before executing it — the right default for a sovereignty product. The passphrase is entered at the no-echo prompt.

curl -fsSL https://felhom.eu/scripts/felhom-host-install.sh -o felhom-host-install.sh \ && sudo bash felhom-host-install.sh --customer-id {{.CustomerID}}

Option 2: Local install (script already on host)

sudo ./felhom-host-install.sh --customer-id {{.CustomerID}}

Option 3: Manual config fetch (debug only)

The same payload the controller pulls itself — for inspection, not normal provisioning.

curl -fsSL https://hub.felhom.eu/api/v1/config/{{.CustomerID}} -H "X-Retrieval-Password: {{.Config.RetrievalPassword}}" -o controller.yaml

YAML Preview

Loading preview...

{{end}} {{if .HasReports}}

Controller Update

Controller version {{.Customer.ControllerVersion}}
{{if .LatestVersion}}
Registry latest v{{.LatestVersion}} {{if .UpdateAvailable}} ● update available {{else}} — up to date {{end}}
{{end}} {{if .ControllerURL}}
Controller URL {{.ControllerURL}}
{{end}}
Effective floor (min. version) {{if .EffectiveFloor}}v{{.EffectiveFloor}} {{if .BelowFloor}}● below floor — will auto-update {{else}}— at/above floor{{end}} {{else}}none (Phase 2 inert){{end}}
Global floor {{if .GlobalFloor}}v{{.GlobalFloor}}{{else}}unset{{end}}
{{.CSRFField}} Boxes below the effective floor auto-update on their next report. Blank clears the override.

Controller updates are agent-driven (the version floor above) and config is delivered by the box pulling it on a config change — the hub never connects into the box. Edit the config via the Edit button (top of page); the controller re-pulls and restarts on its next report.

Events {{if .EventCounts}} {{with mapGet .EventCounts "error"}}{{.}} error{{if gt . 1}}s{{end}}{{end}} {{with mapGet .EventCounts "warning"}}{{.}} warning{{if gt . 1}}s{{end}}{{end}} {{end}} (last 24h)

{{if .Events}}
{{range .Events}} {{end}}
Time Severity Type Message Source
{{.CreatedAt.Format "Jan 02 15:04"}} {{.Severity}} {{.EventType}} {{.Message}} {{.Source}}
{{else}}

No events recorded yet.

{{end}}
{{if .HasAppTelemetry}}

App Telemetry (last 7 days)

{{range .AppTelemetry}} {{end}}
App Memory (current) Memory (avg 7d) Memory (peak 7d) Catalog Limit Errors Warnings
{{if .DisplayName}}{{.DisplayName}}{{else}}{{.AppName}}{{end}} {{formatFloat .MemoryCurrentMB}} MB {{formatFloat .MemoryAvgMB}} MB {{formatFloat .MemoryPeakMB}} MB {{if .CatalogLimit}}{{.CatalogLimit}}{{else}}—{{end}} {{if gt .LogErrors 0}}{{.LogErrors}}{{else}}0{{end}} {{if gt .LogWarnings 0}}{{.LogWarnings}}{{else}}0{{end}}
{{end}} {{if .HasDRRecipe}}

DR Recipe (secret-free reconstruction plan)

The non-secret re-provision plan — guest sizing, drive inventory (durable-id → role → mount → intent), PVE storage defs, PBS coordinates, and app inventory + storage bindings. It complements escrow (keys) and PBS/restic (bytes): it contains no key, password, or token. Use it to rebuild the host/guest/storage scaffolding before the PBS bytes land.

Storage / guest / PBS half (agent) {{if .DRRecipeHasHost}}present{{else}}awaiting host-report{{end}}
Customer / apps half (controller) {{if .DRRecipeHasApps}}present{{else}}awaiting controller report{{end}}
Last updated {{if .DRRecipeUpdatedAt}}{{.DRRecipeUpdatedAt}}{{else}}—{{end}}
Download recipe (JSON)
{{end}}

Notifications

Email {{if .NotifPrefs}}{{if .NotifPrefs.Email}}{{.NotifPrefs.Email}}{{else}}Not set{{end}}{{else}}Not configured{{end}}
{{if .NotifPrefs}}
Events {{if .NotifPrefs.EnabledEvents}}{{joinStrings .NotifPrefs.EnabledEvents ", "}}{{else}}None{{end}}
{{end}}
{{if .RecentNotifications}}

Recent (last 10)

{{range .RecentNotifications}} {{end}}
Time Channel Event Status Message
{{.CreatedAt.Format "Jan 02 15:04"}} {{.Channel}} {{.EventType}} {{.Status}} {{.Message}}
{{end}}
{{if .History}}

Report History (last 24h)

{{len .History}} reports {{range .History}} {{end}}
Time Status CPU Memory
{{.ReceivedAt.Format "Jan 02 15:04"}} {{.HealthStatus}} {{formatFloat .CPUPercent}}% {{formatFloat .MemoryPercent}}%
{{end}} {{end}}
{{if .HasReports}} {{end}}