v1.2.0 New Features , UI Updates and More
N.O.R.A — v1.2.0 Release Notes
🆕 New Features
API Polling Engine (AP-01 → AP-04)
A full end-to-end API polling system that lets NORA actively query your apps instead of waiting for webhooks.
- Digest Registry — new database table (
digest_registry) with a startup reconciler that reads all app profiles and upserts their digest categories and widgets. Entries can be toggled active/inactive; active entries cannot be deleted. A new Digest Registry tab in Settings exposes the registry with a filter-by-profile dropdown and a SlidePanel detail drawer. - App Profile Schema extensions — profiles now support an
api_pollingblock (list of endpoints withpath,name,label,target,value_type) and adigest.widgetssection withsource: api | webhook. Validation runs at load time with log-and-skip on failure — no startup crashes. - API Poller scanner —
RunAPIPollingregistered as aGlobalDiscoveryJob(hourly cadence). Supports four auth types,count/string/boolean/listvalue types, JSONPath and length targets. Snapshots written to a newapp_metric_snapshotstable (UNIQUE(app_id, metric_name)). - App Detail metric cards — the App Detail page now renders per-category digest stats as individual metric cards, sourced from both webhook events and API poll snapshots. Cards appear even when the app has zero events yet.
- API URL field — apps can now store a separate internal
api_urldistinct from the publicbase_url. The poller usesapi_urlwhen present, falls back tobase_url. Add App and App Settings UI both expose the field.
Relationship Graph & Infrastructure Topology
component_linkstable — canonical parent-child relationship store covering all entity types (Proxmox → VM, VM → Docker Engine → Container → App → Monitor). Backfilled from all previous inline FK columns on first startup./relationshipspage — app-centric table showing each app's infra link chain;+ Linkdropdown to create a new relationship;Unlinkto remove one. All writes go through thecomponent_linksAPI (GET/POST/DELETE /api/v1/links).- Network Topology page — interactive ReactFlow graph showing all four entity tiers (infra, containers, apps, monitors) as distinct styled nodes. Color-coded edges per tier (blue → teal → violet → green). Tree layout replaces flat BFS — parents are centered above children. Topology replaced the embedded map tab on the Infrastructure page.
- Auto-linking — containers are automatically linked to apps when a discovered container's image matches the app's registered image field. Traefik route → app links are synced automatically when discovery resolves a route to an app via container cross-reference.
Container Management
- Infrastructure Containers tab — all discovered containers from every Docker Engine and Portainer endpoint in one flat table (
/infrastructure?view=containers). Columns: status dot, name, source badge (Docker / Portainer), truncated image, update status, last seen, linked app, settings gear. - Container detail panel — SlidePanel to inspect a container: ports, networks, volumes, labels, restart policy, Docker-native created timestamp. On-demand only, no continuous polling.
- Portainer + Docker Engine detail pages — replaced the inline container card grids with stat cards (running/stopped/images/volumes/networks/dangling images/unused volumes) and a link through to the Containers tab. Inline app linking removed from both pages; now managed exclusively via the Relationships page.
- Container enrichment fields —
ports,labels,volumes,networks,restart_policy,docker_created_atnow fully populated by the Docker and Portainer scanners and returned by the API (previously collected in DB but silently dropped from the response struct).
SlidePanel Shared Drawer Component
All modal-backdrop overlays replaced with a unified SlidePanel component — right-side panel on desktop (480px), bottom sheet on mobile. Features: React portal, focus trap, Escape key, body scroll lock, 200ms entry / 150ms exit animation. Migrated across: Add/Edit Infra Component, Add Check, Edit Check, App Settings, Add App wizard (3-step), Event detail expand. All dropdowns (templates, apps, Traefik integrations) now sort alphabetically.
Instance & Tech Stack Info
Settings Instance tab overhauled:
- Displays NORA version, Go runtime version, live SQLite version (queried at runtime)
- Tech Stack card — Backend dependencies read from the compiled binary via
debug.ReadBuildInfo()(SQLite, chi, jwt, webpush-go, gosnmp, Docker SDK, sqlx). Frontend deps imported frompackage.jsonat build time (React, React Router, Vite, TypeScript). Always reflects what's actually running — no manual updates. - GitHub repo and Wiki links in-panel.
- Settings tabs reordered: Apps → Notifications → Notify Rules → Digest Registry → Jobs → Users → Instance.
🏗️ Infrastructure & Schema Improvements
Database Sprawl Cleanup (Migrations 043–049)
Seven migrations eliminated redundant tables that had been accumulating since earlier refactors:
| Migration | What happened |
|---|---|
| 043 | Drop legacy traefik_routes table |
| 044 | Drop traefik_services; services now derived from discovered_routes |
| 045 | Drop infrastructure_integrations, traefik_certs, traefik_component_certs; strip ssl_source/integration_id from monitor_checks |
| 046 | Drop traefik_overview; add traefik_meta column to infra components |
| 047 | Consolidate snmp_meta / synology_meta / traefik_meta → single meta column |
| 048 | Migrate docker_engines rows into infrastructure_components (same IDs, all links preserved), drop table; topology rewritten to treat Docker Engines as uniform infra nodes |
| 049 | Add servers_json column to discovered_routes (JSON map of backend server URLs → UP|DOWN) |
Proxmox VM Type System
- Dropped
bare_metal,lxc, andvirtual_hostcomponent types; addedvm_linux,vm_windows,vm_other. - LXC containers removed entirely from the Proxmox scanner.
- VM OS type (
ostypefrom Proxmox API) now determines the component type at discovery time (Linux variants →vm_linux, Windows variants →vm_windows, unknown →vm_other).
Route Data Enrichment
Eleven previously missing fields now returned by route list endpoints (GET /routes, GET /infrastructure/{id}/routes): router_status, provider, entry_points, has_tls_resolver, cert_resolver_name, service_status, service_type, servers_total, servers_up, servers_down, servers_json.
Stale BackendService Field Removed
BackendService field removed from DiscoveredRoute struct, routeSelectCols, and all test fixtures. It was superseded by ServiceName in an earlier migration but was still being SELECTed on every route query.
🐛 Bug Fixes
| Issue | Fix |
|---|---|
| Traefik discovery source_type | Events from Traefik discovery were logged as "physical_host". Fixed to "traefik", consistent with every other scanner. |
| Portainer "Discover Now" button | ScanOneComponent had no portainer_api case — hit default and returned an error. Added the case, wired to PortainerEnrichmentWorker. Portainer discovery now also upserts containers to discovered_containers instead of just counting them. |
Portainer metrics source_type |
Portainer container metrics were written with source_type = "portainer_container" which violated the DB CHECK constraint. Fixed to "docker_container". |
| Monitor check auto-filling app template URL | The monitor: block in app profiles was auto-creating and overwriting user URL checks on every save/startup via SyncAllProfileChecks. Removed the entire monitor sync system — monitor checks are now fully user-managed. The monitor: block stripped from all 25 app profiles. |
| API Poller — taint/security (CodeQL) | API key was string-concatenated into rawURL, creating a CodeQL taint path to log call sites. Fixed by building requests with clean URLs and setting the key via q.Encode(). All error log calls now log entry.Path instead of the full URL. |
| Secure cookie breaking HTTP dev environments | Secure: true on session cookies silently dropped cookies on plain-HTTP dev setups. Replaced with isSecureRequest(r) helper (checks r.TLS != nil or X-Forwarded-Proto: https) on all three cookie writes (Login, Logout, TOTP Verify). |
| SSL monitor check | Cleaned up SSL check result parsing and days-remaining display. |
| Proxmox detail page | Proxmox detail page not rendering correctly — fixed as part of the VM type system and detail page refactor. |
| Traefik chain walker | Chain walker was following parent links through Traefik nodes into VMs/Proxmox. Added early-break on parentType == "traefik" and "traefik_route". Manual Traefik links with no discovered routes now inject a synthetic ManualLink: true entry so Traefik still appears in the chain. |
🎨 UI / Dashboard
Dashboard Rework
- New section order: Summary Bar → Events → Monitor Checks → Apps → Infrastructure
- Events + Monitor Checks side-by-side in a shared 2-column row; each a 2×2 grid at matched heights. Event cards redesigned to match check rollup structure (type label → big count → event count; no icon). Both card sets use identical padding and top-border accent stripe so rows align pixel-perfectly.
- Unified app cards — Apps and Services split removed. New compact card: icon left, name + status dot, last event text, optional
checks X/Ypill. Backend computeschecks_up/checks_totalper app from enabled linked monitor checks. - Sparklines removed from Summary Bar cards.
- Responsive: summary bar 2-column on mobile, events + checks stack vertically, app cards in 2-column grid.
Infrastructure Page
- Container list gains column headers on desktop.
- Image update indicator changed from amber dot → "Update" amber pill.
- Navigate button redesigned as a pill with accent border, solid hover fill and glow.
Digest Report (Email)
- Per-app enrichment: linked monitor checks, Traefik routes (parsed via
routeDomain()helper), container counts pre-computed per app section. - Compact one-row-per-app summary table replaces expanded per-app widget blocks.
- Infrastructure and container sections use consistent column-header table layout in both HTML and plain-text templates.
📦 Migrations Summary
| # | Description |
|---|---|
| 031 | digest_registry table |
| 032 | profile_source column on digest_registry |
| 033 | app_metric_snapshots table |
| 034 | Component type system rebuild (add vm_linux/vm_windows/vm_other, remove bare_metal/lxc/virtual_host) |
| 035 | component_links table + backfill from inline FK columns |
| 036 | Drop old inline FK columns (parent_id, docker_engine_id, host_component_id) |
| 037 | learned_containers / discovered_containers enrichment fields |
| 038 | first_seen_at on traefik_services |
| 041 | Backfill traefik → route, route → app, monitor → app into component_links |
| 043–049 | Database sprawl cleanup (see table above) |
| 051 | Container env vars |