cf6fec8d87
gates / gates (push) Successful in 6m17s
Needs attention, Waiting for you (one card per kernel/Docker/Proxmox set the button may approve), a 7-column Boxes table whose rows open to every old value, and a closed Details (release ids, cancelled approvals, ring-0 counts, floors, crash guard and root files, Approve now - which now asks first). Same data, buttons, routes and CSRF field. No deploy. Screenshots in audits/hub-system-page-2026-10-10/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
1645 lines
70 KiB
HTML
1645 lines
70 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>System — Felhom Hub</title>
|
||
<style>/* Felhom Hub — Dark theme */
|
||
/* Felhom Hub — design system v2 (TASK-D4). Canonical tokens:
|
||
documentation/design/design-system.md */
|
||
|
||
@font-face {
|
||
font-family: 'Plus Jakarta Sans';
|
||
font-style: normal;
|
||
font-weight: 400 800;
|
||
font-display: swap;
|
||
src: url('file:///mnt/5_hdd/felhom.eu/git/felhom.eu/hub/internal/web/static/fonts/pjs-latin.woff2') format('woff2');
|
||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||
}
|
||
@font-face {
|
||
font-family: 'Plus Jakarta Sans';
|
||
font-style: normal;
|
||
font-weight: 400 800;
|
||
font-display: swap;
|
||
src: url('file:///mnt/5_hdd/felhom.eu/git/felhom.eu/hub/internal/web/static/fonts/pjs-latin-ext.woff2') format('woff2');
|
||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||
}
|
||
@font-face {
|
||
font-family: 'JetBrains Mono';
|
||
font-style: normal;
|
||
font-weight: 400 500;
|
||
font-display: swap;
|
||
src: url('file:///mnt/5_hdd/felhom.eu/git/felhom.eu/hub/internal/web/static/fonts/jbm-latin.woff2') format('woff2');
|
||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||
}
|
||
@font-face {
|
||
font-family: 'JetBrains Mono';
|
||
font-style: normal;
|
||
font-weight: 400 500;
|
||
font-display: swap;
|
||
src: url('file:///mnt/5_hdd/felhom.eu/git/felhom.eu/hub/internal/web/static/fonts/jbm-latin-ext.woff2') format('woff2');
|
||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||
}
|
||
|
||
:root {
|
||
--bg-0: #0A1220; --bg-1: #0F1B2E; --bg-2: #16263F;
|
||
--line: #22344F; --line-soft: #1A2A42;
|
||
--text-1: #EDF2F9; --text-2: #94A6BF; --text-3: #5E7392;
|
||
--blue: #0083D8; --blue-bright: #2EA8F5; --blue-dim: rgba(0,131,216,.13);
|
||
--warn: #E0A93E; --warn-dim: rgba(224,169,62,.12);
|
||
--crit: #E5534B; --crit-dim: rgba(229,83,75,.12);
|
||
--radius: 2px;
|
||
--font-ui: 'Plus Jakarta Sans', -apple-system, sans-serif;
|
||
--font-data: 'JetBrains Mono', monospace;
|
||
}
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
font-family: var(--font-ui);
|
||
background: var(--bg-0);
|
||
color: var(--text-1);
|
||
line-height: 1.6;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 2rem 1.5rem;
|
||
}
|
||
|
||
/* Header */
|
||
header {
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
header h1 {
|
||
font-size: 1.75rem;
|
||
font-weight: 700;
|
||
color: var(--text-1);
|
||
}
|
||
|
||
.subtitle {
|
||
color: var(--text-2);
|
||
font-size: 0.9rem;
|
||
margin-top: 0.25rem;
|
||
}
|
||
|
||
.back-link {
|
||
color: var(--blue-bright);
|
||
text-decoration: none;
|
||
font-size: 0.9rem;
|
||
display: inline-block;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.back-link:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
/* Dashboard table */
|
||
.dashboard-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
background: var(--bg-1);
|
||
border-radius: var(--radius);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.dashboard-table th {
|
||
text-align: left;
|
||
padding: 0.75rem 1rem;
|
||
background: var(--bg-1);
|
||
color: var(--text-2);
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
border-bottom: 1px solid var(--line);
|
||
}
|
||
|
||
.dashboard-table td {
|
||
padding: 0.75rem 1rem;
|
||
border-bottom: 1px solid var(--line);
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.dashboard-table tbody tr {
|
||
cursor: pointer;
|
||
transition: background 0.15s;
|
||
}
|
||
|
||
.dashboard-table tbody tr:hover {
|
||
background: var(--bg-2);
|
||
}
|
||
|
||
.dashboard-table tbody tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.customer-name {
|
||
font-weight: 600;
|
||
}
|
||
|
||
.status-dot {
|
||
display: inline-block;
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
margin-right: 0.35rem;
|
||
vertical-align: baseline;
|
||
background: var(--text-3);
|
||
}
|
||
.status-dot-nominal { background: var(--blue); }
|
||
.status-dot-warn { background: var(--warn); }
|
||
.status-dot-crit { background: var(--crit); }
|
||
.status-dot-neutral { background: transparent; border: 1px solid var(--text-3); }
|
||
|
||
/* Status badges */
|
||
.status-badge {
|
||
display: inline-block;
|
||
white-space: nowrap;
|
||
padding: 0.15rem 0.5rem;
|
||
border-radius: var(--radius);
|
||
font-size: 0.75rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
/* Status = STATE -> tag semantics (outline + dot; design-system addendum). Keyed by the
|
||
raw status value the templates emit; exception-color: ok is blue, pending/disabled quiet. */
|
||
.status-badge { border: 1px solid var(--line); background: transparent; color: var(--text-2); }
|
||
.status-badge::before {
|
||
content: '';
|
||
display: inline-block;
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
margin-right: 0.35rem;
|
||
background: var(--text-3);
|
||
vertical-align: 0.05em;
|
||
}
|
||
.status-badge-ok { border-color: rgba(0,131,216,.45); color: var(--blue-bright); }
|
||
.status-badge-ok::before { background: var(--blue); }
|
||
.status-badge-warn, .status-badge-blocked, .status-badge-stale { border-color: var(--warn); color: var(--warn); background: var(--warn-dim); }
|
||
.status-badge-warn::before, .status-badge-blocked::before, .status-badge-stale::before { background: var(--warn); }
|
||
.status-badge-down, .status-badge-fail { border-color: var(--crit); color: var(--crit); background: var(--crit-dim); }
|
||
.status-badge-down::before, .status-badge-fail::before { background: var(--crit); }
|
||
.status-badge-disabled, .status-badge-pending { color: var(--text-3); }
|
||
.status-badge-disabled::before, .status-badge-pending::before { background: transparent; border: 1px solid var(--text-3); }
|
||
|
||
/* Config badges */
|
||
.config-badge {
|
||
display: inline-block;
|
||
padding: 0.15em 0.5em;
|
||
border-radius: var(--radius);
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
letter-spacing: 0.03em;
|
||
}
|
||
.config-badge-managed { background: var(--blue-dim); color: var(--blue-bright); }
|
||
.config-badge-manual { background: var(--bg-2); color: var(--text-2); }
|
||
.config-badge-blocked { background: var(--crit-dim); color: var(--crit); }
|
||
|
||
/* Blocked banner */
|
||
.flash-blocked {
|
||
background: var(--crit-dim);
|
||
border: 1px solid rgba(229,83,75,.3);
|
||
color: var(--crit);
|
||
padding: 0.75rem 1rem;
|
||
border-radius: var(--radius);
|
||
margin-bottom: 1rem;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
/* Blocked row in tables */
|
||
.row-blocked { opacity: 0.5; }
|
||
|
||
/* Cards */
|
||
.card {
|
||
background: var(--bg-1);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
padding: 1.25rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.card h2 {
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
margin-bottom: 1rem;
|
||
color: var(--text-1);
|
||
}
|
||
|
||
.card h3 {
|
||
font-size: 0.9rem;
|
||
font-weight: 600;
|
||
margin-top: 0.75rem;
|
||
margin-bottom: 0.5rem;
|
||
color: var(--text-2);
|
||
}
|
||
|
||
/* Info grid */
|
||
.info-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.info-item .label {
|
||
display: block;
|
||
font-size: 0.75rem;
|
||
color: var(--text-3);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
.info-item .value {
|
||
font-size: 0.9rem;
|
||
color: var(--text-1);
|
||
}
|
||
|
||
/* Metrics */
|
||
.metrics-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||
gap: 1rem;
|
||
margin-top: 0.75rem;
|
||
}
|
||
|
||
.metric {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.25rem;
|
||
}
|
||
|
||
.metric-label {
|
||
font-size: 0.8rem;
|
||
color: var(--text-2);
|
||
}
|
||
|
||
.metric-value {
|
||
font-size: 1.25rem;
|
||
font-weight: 700;
|
||
font-family: var(--font-data);
|
||
}
|
||
|
||
.metric-detail {
|
||
font-size: 0.8rem;
|
||
color: var(--text-3);
|
||
}
|
||
|
||
.bar {
|
||
height: 6px;
|
||
background: var(--line);
|
||
border-radius: var(--radius);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bar-fill {
|
||
height: 100%;
|
||
background: var(--blue-bright);
|
||
border-radius: var(--radius);
|
||
transition: width 0.3s ease;
|
||
}
|
||
/* Fill bands (v0.64.0, R-5) — exception color: neutral when nominal, amber ≥ warn, red ≥ crit; no green. */
|
||
.bar-fill.bar-ok { background: var(--text-3); }
|
||
.bar-fill.bar-warning { background: var(--warn); }
|
||
.bar-fill.bar-critical { background: var(--crit); }
|
||
|
||
/* Offsite dashboard gauges (v0.65.0, R-5) — two side-by-side tiles (Restic, PBS DR). */
|
||
.offsite-gauges { display: flex; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 1rem; }
|
||
.offsite-gauges .offsite-tile { margin-bottom: 0; }
|
||
|
||
/* Offsite pool dashboard tile (v0.64.0, R-5) — compact, band-colored, links to /offsite. */
|
||
.offsite-tile {
|
||
display: inline-flex; align-items: baseline; gap: 0.5rem;
|
||
padding: 0.4rem 0.8rem; margin-bottom: 1rem;
|
||
border: 1px solid var(--line); border-radius: var(--radius);
|
||
text-decoration: none; font-size: 0.85rem;
|
||
}
|
||
.offsite-tile-label { color: var(--text-3); text-transform: uppercase; font-size: 0.72rem; letter-spacing: 0.03em; }
|
||
.offsite-tile-val { color: var(--text-1); font-weight: 600; }
|
||
.offsite-tile-stale { color: var(--warn); font-size: 0.72rem; }
|
||
.offsite-tile-warning { border-color: var(--warn); }
|
||
.offsite-tile-warning .offsite-tile-val { color: var(--warn); }
|
||
.offsite-tile-critical { border-color: var(--crit); }
|
||
.offsite-tile-critical .offsite-tile-val { color: var(--crit); }
|
||
|
||
/* Container table */
|
||
.container-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.container-table th {
|
||
text-align: left;
|
||
padding: 0.5rem 0.75rem;
|
||
color: var(--text-3);
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
border-bottom: 1px solid var(--line);
|
||
}
|
||
|
||
.container-table td {
|
||
padding: 0.4rem 0.75rem;
|
||
border-bottom: 1px solid rgba(51, 65, 85, 0.5);
|
||
}
|
||
|
||
.container-state {
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.container-state-running { color: var(--blue-bright); }
|
||
.container-state-stopped, .container-state-exited { color: var(--crit); }
|
||
.container-state-unhealthy { color: var(--warn); }
|
||
|
||
/* Health */
|
||
.health-status {
|
||
font-size: 1.1rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.health-status-ok { color: var(--blue-bright); }
|
||
.health-status-warn { color: var(--warn); }
|
||
.health-status-fail { color: var(--crit); }
|
||
|
||
.issue-list, .warning-list {
|
||
list-style: none;
|
||
padding-left: 0;
|
||
}
|
||
|
||
.issue-list li::before { content: "● "; color: var(--crit); }
|
||
.warning-list li::before { content: "● "; color: var(--warn); }
|
||
|
||
.issue, .warning {
|
||
padding: 0.25rem 0;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
/* History */
|
||
.history-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 0.85rem;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.history-table th {
|
||
text-align: left;
|
||
padding: 0.4rem 0.5rem;
|
||
color: var(--text-3);
|
||
font-size: 0.75rem;
|
||
border-bottom: 1px solid var(--line);
|
||
}
|
||
|
||
.history-table td {
|
||
padding: 0.3rem 0.5rem;
|
||
border-bottom: 1px solid rgba(51, 65, 85, 0.3);
|
||
}
|
||
|
||
details summary {
|
||
cursor: pointer;
|
||
color: var(--blue-bright);
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
/* Empty state */
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 4rem 2rem;
|
||
color: var(--text-2);
|
||
}
|
||
|
||
.empty-state .hint {
|
||
margin-top: 0.5rem;
|
||
font-size: 0.85rem;
|
||
color: var(--text-3);
|
||
}
|
||
|
||
.empty-state code {
|
||
background: var(--bg-2);
|
||
padding: 0.15rem 0.4rem;
|
||
border-radius: var(--radius);
|
||
font-family: var(--font-data);
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
/* Footer */
|
||
footer {
|
||
margin-top: 2rem;
|
||
padding-top: 1rem;
|
||
border-top: 1px solid var(--line);
|
||
text-align: center;
|
||
font-size: 0.8rem;
|
||
color: var(--text-3);
|
||
}
|
||
|
||
footer a {
|
||
color: var(--blue-bright);
|
||
text-decoration: none;
|
||
}
|
||
|
||
/* Code */
|
||
code {
|
||
font-family: var(--font-data);
|
||
font-size: 0.85em;
|
||
color: var(--blue-bright);
|
||
}
|
||
|
||
/* Navigation */
|
||
.nav-links {
|
||
display: flex;
|
||
gap: 1.5rem;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.nav-link {
|
||
color: var(--text-2);
|
||
text-decoration: none;
|
||
font-size: 0.9rem;
|
||
padding-bottom: 2px;
|
||
border-bottom: 2px solid transparent;
|
||
transition: color 0.15s, border-color 0.15s;
|
||
}
|
||
|
||
.nav-link:hover {
|
||
color: var(--text-1);
|
||
}
|
||
|
||
.nav-link.active {
|
||
color: var(--blue-bright);
|
||
border-bottom-color: var(--blue-bright);
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Buttons */
|
||
.btn {
|
||
display: inline-block;
|
||
padding: 0.5rem 1rem;
|
||
background: var(--blue-bright);
|
||
color: var(--bg-0);
|
||
border: none;
|
||
border-radius: var(--radius);
|
||
font-size: 0.85rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
text-decoration: none;
|
||
transition: opacity 0.15s;
|
||
}
|
||
|
||
.btn:hover { opacity: 0.85; }
|
||
|
||
.btn-outline {
|
||
background: transparent;
|
||
color: var(--text-2);
|
||
border: 1px solid var(--line);
|
||
}
|
||
|
||
.btn-outline:hover {
|
||
color: var(--text-1);
|
||
border-color: var(--text-2);
|
||
}
|
||
|
||
.btn-danger {
|
||
background: var(--crit);
|
||
color: #fff;
|
||
}
|
||
|
||
.btn-sm {
|
||
padding: 0.3rem 0.6rem;
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
/* Forms */
|
||
.config-form .form-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||
gap: 1rem;
|
||
}
|
||
|
||
.form-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.3rem;
|
||
}
|
||
|
||
.form-group label {
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
color: var(--text-2);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.03em;
|
||
}
|
||
|
||
.form-group input[type="text"],
|
||
.form-group input[type="email"] {
|
||
background: var(--bg-0);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
padding: 0.5rem 0.75rem;
|
||
color: var(--text-1);
|
||
font-size: 0.9rem;
|
||
font-family: inherit;
|
||
}
|
||
|
||
.form-group input:focus {
|
||
outline: none;
|
||
border-color: var(--blue-bright);
|
||
}
|
||
|
||
.form-group input[readonly] {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.form-hint {
|
||
font-size: 0.75rem;
|
||
color: var(--text-3);
|
||
}
|
||
|
||
/* Install-command generator (GL-7) */
|
||
.gen-controls {
|
||
background: var(--bg-0);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
padding: 1rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
.gen-radios {
|
||
display: flex;
|
||
gap: 1.25rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
.gen-radio, .gen-check {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.4rem;
|
||
font-size: 0.85rem;
|
||
color: var(--text-1);
|
||
text-transform: none;
|
||
letter-spacing: normal;
|
||
font-weight: 400;
|
||
cursor: pointer;
|
||
}
|
||
.gen-radio input, .gen-check input {
|
||
accent-color: var(--blue-bright);
|
||
cursor: pointer;
|
||
}
|
||
.gen-checks {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
margin-top: 0.85rem;
|
||
padding-top: 0.85rem;
|
||
border-top: 1px solid var(--line-soft);
|
||
}
|
||
.gen-check code, .gen-radio code { color: var(--blue-bright); font-size: 0.8rem; }
|
||
.gen-controls input[type="number"] {
|
||
background: var(--bg-1);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
padding: 0.5rem 0.75rem;
|
||
color: var(--text-1);
|
||
font-size: 0.9rem;
|
||
font-family: inherit;
|
||
}
|
||
.gen-controls input[type="number"]:focus,
|
||
.gen-controls input[type="text"]:focus { outline: none; border-color: var(--blue-bright); }
|
||
.gen-msg {
|
||
color: #E5534B;
|
||
font-size: 0.82rem;
|
||
margin: 0 0 0.75rem;
|
||
padding: 0.5rem 0.75rem;
|
||
background: rgba(229,83,75,.10);
|
||
border: 1px solid rgba(229,83,75,.35);
|
||
border-radius: var(--radius);
|
||
}
|
||
|
||
/* Credentials */
|
||
.credential-row {
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.credential-box {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
background: var(--bg-0);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
padding: 0.5rem 0.75rem;
|
||
margin-top: 0.25rem;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.credential-box code {
|
||
flex: 1;
|
||
font-size: 0.8rem;
|
||
word-break: break-all;
|
||
color: var(--text-1);
|
||
}
|
||
|
||
.copy-btn {
|
||
background: transparent;
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
color: var(--text-2);
|
||
cursor: pointer;
|
||
padding: 0.2rem 0.5rem;
|
||
font-size: 0.85rem;
|
||
flex-shrink: 0;
|
||
transition: color 0.15s, border-color 0.15s;
|
||
}
|
||
|
||
.copy-btn:hover {
|
||
color: var(--blue-bright);
|
||
border-color: var(--blue-bright);
|
||
}
|
||
|
||
/* Flash messages */
|
||
.flash {
|
||
padding: 0.75rem 1rem;
|
||
border-radius: var(--radius);
|
||
margin-bottom: 1rem;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.flash-success {
|
||
background: var(--blue-dim);
|
||
color: var(--blue-bright);
|
||
border: 1px solid rgba(74, 222, 128, 0.3);
|
||
}
|
||
|
||
.flash-error {
|
||
background: var(--crit-dim);
|
||
color: var(--crit);
|
||
border: 1px solid rgba(229,83,75,.3);
|
||
}
|
||
|
||
/* v0.67.0 — the deviation tier between success and error: a state that is not a failure but is
|
||
also not nominal, and that the operator must act on (post-RESET staleness R-37, enabled-but-
|
||
unprovisioned offsite R-36). Amber per the exception-color principle: it appears ONLY on
|
||
deviation, never on a healthy page. */
|
||
.flash-warn {
|
||
background: var(--warn-dim);
|
||
color: var(--warn);
|
||
border: 1px solid rgba(224,169,62,.3);
|
||
}
|
||
.flash-warn strong { color: var(--text-1); }
|
||
.flash-warn code {
|
||
font-family: var(--font-data);
|
||
font-size: .85em;
|
||
}
|
||
|
||
/* YAML Preview */
|
||
.yaml-preview {
|
||
background: var(--bg-0);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
padding: 1rem;
|
||
max-height: 500px;
|
||
overflow: auto;
|
||
}
|
||
|
||
.yaml-preview pre {
|
||
font-family: var(--font-data);
|
||
font-size: 0.8rem;
|
||
color: var(--text-2);
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
margin: 0;
|
||
}
|
||
|
||
/* Muted text */
|
||
.text-muted {
|
||
color: var(--text-3);
|
||
}
|
||
|
||
/* Severity badges */
|
||
.severity-badge {
|
||
display: inline-block;
|
||
padding: 0.15em 0.5em;
|
||
border-radius: var(--radius);
|
||
font-size: 0.8em;
|
||
font-weight: 600;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.severity-error {
|
||
background: rgba(239, 68, 68, 0.15);
|
||
color: #ef4444;
|
||
}
|
||
|
||
.severity-warning {
|
||
background: rgba(245, 158, 11, 0.15);
|
||
color: #f59e0b;
|
||
}
|
||
|
||
.severity-info {
|
||
background: rgba(59, 130, 246, 0.15);
|
||
color: #3b82f6;
|
||
}
|
||
|
||
.severity-critical {
|
||
background: var(--crit-dim);
|
||
color: var(--crit);
|
||
}
|
||
|
||
.severity-ok {
|
||
background: var(--bg-2);
|
||
color: var(--text-2);
|
||
}
|
||
|
||
/* Event filter buttons */
|
||
.event-filter {
|
||
font-size: 0.8em;
|
||
padding: 0.2em 0.6em;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.event-filter.active {
|
||
background: var(--blue-bright);
|
||
color: #fff;
|
||
border-color: var(--blue-bright);
|
||
}
|
||
|
||
/* Notification channel badges */
|
||
.status-badge-operator {
|
||
background: rgba(139, 92, 246, 0.15);
|
||
color: #8b5cf6;
|
||
}
|
||
|
||
.status-badge-customer {
|
||
background: rgba(59, 130, 246, 0.15);
|
||
color: #3b82f6;
|
||
}
|
||
|
||
/* Config diff table */
|
||
.diff-changed td { color: #f59e0b; }
|
||
.diff-hub_only td { color: #3b82f6; }
|
||
.diff-controller_only td { color: #fb923c; }
|
||
|
||
/* Badge styles */
|
||
.badge {
|
||
display: inline-block;
|
||
padding: 0.15rem 0.5rem;
|
||
border-radius: var(--radius);
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
font-family: var(--font-data);
|
||
}
|
||
.badge-error {
|
||
background: var(--crit-dim);
|
||
color: var(--crit);
|
||
}
|
||
.badge-warn {
|
||
background: rgba(250, 204, 21, 0.2);
|
||
color: var(--warn);
|
||
}
|
||
.badge-neutral {
|
||
background: var(--bg-2);
|
||
color: var(--text-3);
|
||
}
|
||
/* v0.51.0: badge-ok was referenced by the DR-recipe rows but never defined (fell back to the
|
||
bare .badge). Blue = healthy, per the status-badge-ok convention. */
|
||
.badge-ok {
|
||
background: rgba(0, 131, 216, 0.15);
|
||
color: var(--blue-bright);
|
||
}
|
||
|
||
/* Summary cards row */
|
||
.summary-cards {
|
||
display: flex;
|
||
gap: 1rem;
|
||
margin-bottom: 1.5rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
.summary-card {
|
||
flex: 1;
|
||
min-width: 160px;
|
||
background: var(--bg-1);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
padding: 1rem 1.25rem;
|
||
}
|
||
.summary-card .card-number {
|
||
font-size: 2rem;
|
||
font-weight: 700;
|
||
color: var(--text-1);
|
||
font-family: var(--font-data);
|
||
line-height: 1.1;
|
||
}
|
||
.summary-card .card-label {
|
||
font-size: 0.8rem;
|
||
color: var(--text-3);
|
||
margin-top: 0.25rem;
|
||
}
|
||
|
||
/* Chart container */
|
||
.chart-container {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 280px;
|
||
margin: 1rem 0;
|
||
}
|
||
|
||
/* Period selector button group */
|
||
.period-selector {
|
||
display: flex;
|
||
gap: 0.25rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
.period-btn {
|
||
padding: 0.3rem 0.75rem;
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
background: var(--bg-1);
|
||
color: var(--text-2);
|
||
font-size: 0.85rem;
|
||
text-decoration: none;
|
||
cursor: pointer;
|
||
transition: background 0.15s, color 0.15s;
|
||
}
|
||
.period-btn:hover, .period-btn.active {
|
||
background: var(--blue-bright);
|
||
color: var(--bg-0);
|
||
border-color: var(--blue-bright);
|
||
}
|
||
|
||
/* Accuracy dot */
|
||
.accuracy-dot {
|
||
display: inline-block;
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
margin-left: 0.25rem;
|
||
}
|
||
.accuracy-ok { background: var(--blue-bright); }
|
||
.accuracy-warn { background: var(--warn); }
|
||
.accuracy-danger { background: var(--crit); }
|
||
|
||
/* Memory color classes */
|
||
.mem-ok { color: var(--blue-bright); }
|
||
.mem-warn { color: var(--warn); }
|
||
.mem-danger { color: var(--crit); }
|
||
|
||
/* Data table (apps page) */
|
||
.data-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 0.9rem;
|
||
}
|
||
.data-table th {
|
||
text-align: left;
|
||
padding: 0.6rem 0.75rem;
|
||
font-weight: 600;
|
||
color: var(--text-2);
|
||
font-size: 0.8rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
border-bottom: 1px solid var(--line);
|
||
white-space: nowrap;
|
||
}
|
||
.data-table td {
|
||
padding: 0.6rem 0.75rem;
|
||
border-bottom: 1px solid rgba(100,116,139,0.15);
|
||
color: var(--text-1);
|
||
font-family: var(--font-data);
|
||
font-size: 0.875rem;
|
||
}
|
||
/* Plain links only — :not(.btn) keeps <a class="btn"> buttons on the .btn palette
|
||
(blue-bright link text on a blue-bright button background is invisible). */
|
||
.data-table td a:not(.btn) {
|
||
color: var(--blue-bright);
|
||
text-decoration: none;
|
||
font-family: var(--font-sans, sans-serif);
|
||
}
|
||
.data-table td a:not(.btn):hover { text-decoration: underline; }
|
||
.data-table th a {
|
||
color: var(--text-2);
|
||
text-decoration: none;
|
||
}
|
||
.data-table th a:hover { color: var(--text-1); }
|
||
.data-table tr:hover td { background: rgba(96,165,250,0.04); }
|
||
.data-table input[type="checkbox"] {
|
||
width: 1rem;
|
||
height: 1rem;
|
||
cursor: pointer;
|
||
accent-color: var(--blue-bright);
|
||
}
|
||
|
||
/* Responsive */
|
||
@media (max-width: 768px) {
|
||
.container { padding: 1rem; }
|
||
.dashboard-table { font-size: 0.8rem; }
|
||
.dashboard-table th, .dashboard-table td { padding: 0.5rem; }
|
||
.info-grid { grid-template-columns: 1fr 1fr; }
|
||
.metrics-grid { grid-template-columns: 1fr; }
|
||
}
|
||
|
||
|
||
/* Row tint — exception rule: only warn/crit rows carry a wash; ok/pending stay quiet. */
|
||
.dashboard-table tbody tr.status-warn { background: var(--warn-dim); }
|
||
.dashboard-table tbody tr.status-down { background: var(--crit-dim); }
|
||
|
||
/* Two-tone brand heading */
|
||
header h1 span { color: var(--blue-bright); }
|
||
|
||
/* Inline icon (Lucide sprite) */
|
||
.ico { width: 16px; height: 16px; flex-shrink: 0; vertical-align: -0.18em; }
|
||
|
||
/* Hairline table rows (v2): soft dividers, header underline stays --line. */
|
||
.dashboard-table td, .data-table td { border-bottom: 1px solid var(--line-soft); }
|
||
|
||
:focus-visible { outline: 2px solid var(--blue-bright); outline-offset: 1px; }
|
||
|
||
/* ── Customer page tabs (v0.47.0) ──────────────────────────────────────────
|
||
Hash-based client-side tabs. Graceful degradation is load-bearing: panels
|
||
are hidden ONLY under body.js-tabs (added by JS on DOMContentLoaded) — with
|
||
JS off every panel stays visible and the page reads top-to-bottom. */
|
||
.summary-strip {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 40;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
flex-wrap: wrap;
|
||
padding: 0.5rem 0.75rem;
|
||
margin: 0.75rem 0 0;
|
||
background: var(--bg-0);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
font-size: 0.85rem;
|
||
}
|
||
.summary-strip .strip-name { font-weight: 600; color: var(--text-1); }
|
||
.summary-strip .strip-item { color: var(--text-2); white-space: nowrap; }
|
||
.summary-strip .strip-item code { font-size: 0.85em; }
|
||
|
||
.tab-nav {
|
||
display: flex;
|
||
gap: 0.25rem;
|
||
margin: 0.75rem 0 1rem;
|
||
border-bottom: 1px solid var(--line);
|
||
overflow-x: auto;
|
||
white-space: nowrap;
|
||
}
|
||
.tab-nav a {
|
||
color: var(--text-2);
|
||
text-decoration: none;
|
||
font-size: 0.9rem;
|
||
padding: 0.45rem 0.75rem;
|
||
border-bottom: 2px solid transparent;
|
||
flex-shrink: 0;
|
||
}
|
||
.tab-nav a:hover { color: var(--text-1); }
|
||
.tab-nav a.active {
|
||
color: var(--blue-bright);
|
||
border-bottom-color: var(--blue-bright);
|
||
font-weight: 600;
|
||
}
|
||
.tab-badge {
|
||
display: inline-block;
|
||
min-width: 1.2em;
|
||
margin-left: 0.35em;
|
||
padding: 0 0.35em;
|
||
background: var(--crit);
|
||
color: #fff;
|
||
border-radius: var(--radius);
|
||
font-size: 0.75em;
|
||
font-weight: 600;
|
||
text-align: center;
|
||
}
|
||
body.js-tabs .tab-panel:not(.tab-panel-active) { display: none; }
|
||
|
||
/* Inline two-step confirm (take-two F-16) — the in-place replacement for native confirm() */
|
||
.inline-confirm { display: inline-flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
|
||
.inline-confirm-q { font-size: .8rem; color: var(--warn); }
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after { animation: none !important; transition: none !important; }
|
||
}
|
||
|
||
/* MAIL-HOLD release (internal/mailhold): the button and its one-line explanation sit on one row under the banner. */
|
||
.mail-hold-release {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
flex-wrap: wrap;
|
||
margin-bottom: 1rem;
|
||
}
|
||
.mail-hold-release .text-muted { font-size: 0.8rem; }
|
||
#mail-hold a { color: var(--text-1); }
|
||
</style>
|
||
<style>
|
||
.c-warn { color: var(--warn); font-weight: 600; }
|
||
.c-bad { color: var(--crit); font-weight: 700; }
|
||
.sys-sec { margin-bottom: 1.5rem; }
|
||
.sys-sec > h3 { margin: 0 0 0.15rem; font-size: 1.05rem; color: var(--text-1); }
|
||
.att a, .bx-row a, .bx-more a { color: var(--blue-bright); text-decoration: none; }
|
||
.sys-sec > .why { margin: 0 0 0.8rem; color: var(--text-2); font-size: 0.85em; }
|
||
.term { text-decoration: underline dotted; cursor: help; }
|
||
.sys form { display: inline; }
|
||
.rel-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 0.75rem; }
|
||
|
||
.att { list-style: none; margin: 0; padding: 0; }
|
||
.att li { padding: 0.45rem 0; border-top: 1px solid var(--line); }
|
||
.att li:first-child { border-top: 0; }
|
||
.att .rs { display: block; margin: 0.15rem 0 0 1.4rem; font-size: 0.88em; }
|
||
.att .rs span { font-weight: normal; }
|
||
.mark { display: inline-block; min-width: 3.4rem; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.03em; }
|
||
.mark::before { content: "● "; }
|
||
.mark.c-warn::before { content: "▲ "; }
|
||
.mark.c-bad::before { content: "✕ "; }
|
||
.ok-line::before { content: "● "; color: var(--blue-bright); }
|
||
|
||
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: 0.75rem; }
|
||
.wcard { border: 1px solid var(--warn); border-radius: var(--radius); padding: 0.8rem 1rem; }
|
||
.wcard h4 { margin: 0 0 0.3rem; font-size: 1.05em; }
|
||
.wcard p { margin: 0.25rem 0; font-size: 0.9em; }
|
||
.wcard form { margin-top: 0.5rem; }
|
||
.testing { margin: 0.8rem 0 0; padding-left: 1.1rem; font-size: 0.88em; }
|
||
|
||
.boxes { font-size: 0.9em; }
|
||
.bx-row { display: grid; grid-template-columns: 1.7fr 0.5fr 0.7fr 1.2fr 1.1fr 1.3fr 1.2fr; gap: 0.6rem; align-items: start;
|
||
padding: 0.55rem 0.8rem; }
|
||
.bx-head { color: var(--text-2); font-size: 0.85em; border-bottom: 1px solid var(--line); }
|
||
details.bx { border-bottom: 1px solid var(--line); }
|
||
details.bx > summary { list-style: none; cursor: pointer; color: var(--text-1); font-size: inherit; }
|
||
details.bx > summary::-webkit-details-marker { display: none; }
|
||
details.bx > summary:hover { background: rgba(127,127,127,0.07); }
|
||
details.bx > summary .name::before { content: "▸ "; color: var(--text-2); }
|
||
details.bx[open] > summary .name::before { content: "▾ "; }
|
||
.bx-row > div { min-width: 0; overflow-wrap: anywhere; }
|
||
.bx-row .sub { display: block; color: var(--text-2); font-size: 0.88em; font-weight: normal; }
|
||
.bx-more { padding: 0.4rem 0.8rem 1rem 1.8rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: 0.4rem 1.5rem; }
|
||
.bx-more h5 { margin: 0.6rem 0 0.3rem; font-size: 0.85em; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-2); }
|
||
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 0.15rem 0.8rem; margin: 0; font-size: 0.92em; }
|
||
.kv dt { color: var(--text-2); }
|
||
.kv dd { margin: 0; overflow-wrap: anywhere; }
|
||
.kv form { display: inline; margin-left: 0.3rem; }
|
||
|
||
details.more > summary { cursor: pointer; font-weight: 600; font-size: 1.05rem; }
|
||
details.more h3 { margin-top: 1.3rem; }
|
||
.tbl-wrap { overflow-x: auto; }
|
||
.sys td, .sys th { font-size: 0.85em; vertical-align: top; }
|
||
@media (max-width: 760px) {
|
||
.nav-links { flex-wrap: wrap; gap: 0.3rem 1rem; }
|
||
.bx-head { display: none; }
|
||
.bx-row { grid-template-columns: 1fr 1fr; }
|
||
.bx-row > div:first-child { grid-column: 1 / -1; }
|
||
.bx-row > div[data-label]::before { content: attr(data-label); display: block; color: var(--text-2); font-size: 0.78em; font-weight: normal; }
|
||
.bx-more { padding-left: 0.8rem; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
||
<symbol id="i-triangle-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /> <path d="M12 9v4" /> <path d="M12 17h.01" /></symbol>
|
||
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></symbol>
|
||
<symbol id="i-server" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2" /> <rect width="20" height="8" x="2" y="14" rx="2" ry="2" /> <line x1="6" x2="6.01" y1="6" y2="6" /> <line x1="6" x2="6.01" y1="18" y2="18" /></symbol>
|
||
<symbol id="i-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></symbol>
|
||
<symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18" /> <path d="m6 6 12 12" /></symbol>
|
||
<symbol id="i-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M12 16v-4" /> <path d="M12 8h.01" /></symbol>
|
||
<symbol id="i-hard-drive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 16h.01" /> <path d="M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> <path d="M21.946 12.013H2.054" /> <path d="M6 16h.01" /></symbol>
|
||
<symbol id="i-cpu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20v2" /> <path d="M12 2v2" /> <path d="M17 20v2" /> <path d="M17 2v2" /> <path d="M2 12h2" /> <path d="M2 17h2" /> <path d="M2 7h2" /> <path d="M20 12h2" /> <path d="M20 17h2" /> <path d="M20 7h2" /> <path d="M7 20v2" /> <path d="M7 2v2" /> <rect x="4" y="4" width="16" height="16" rx="2" /> <rect x="8" y="8" width="8" height="8" rx="1" /></symbol>
|
||
<symbol id="i-clock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M12 6v6l4 2" /></symbol>
|
||
<symbol id="i-boxes" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z" /> <path d="m7 16.5-4.74-2.85" /> <path d="m7 16.5 5-3" /> <path d="M7 16.5v5.17" /> <path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z" /> <path d="m17 16.5-5-3" /> <path d="m17 16.5 4.74-2.85" /> <path d="M17 16.5v5.17" /> <path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z" /> <path d="M12 8 7.26 5.15" /> <path d="m12 8 4.74-2.85" /> <path d="M12 13.5V8" /></symbol>
|
||
<symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /> <path d="M16 3.128a4 4 0 0 1 0 7.744" /> <path d="M22 21v-2a4 4 0 0 0-3-3.87" /> <circle cx="9" cy="7" r="4" /></symbol>
|
||
</svg>
|
||
|
||
<script>
|
||
|
||
|
||
function felhomConfirm(el,question,onYes){
|
||
if(!el||el.dataset.fcOpen)return;
|
||
el.dataset.fcOpen='1';
|
||
var wrap=document.createElement('span');wrap.className='inline-confirm';
|
||
var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question;
|
||
var yes=document.createElement('button');yes.type='button';yes.className='btn btn-sm btn-danger';yes.textContent='Igen';
|
||
var no=document.createElement('button');no.type='button';no.className='btn btn-sm btn-outline';no.textContent='Mégse';
|
||
wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no);
|
||
el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling);
|
||
function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;}
|
||
no.addEventListener('click',close);
|
||
yes.addEventListener('click',function(){close();onYes();});
|
||
}
|
||
document.addEventListener('click',function(e){
|
||
var btn=e.target.closest?e.target.closest('[data-confirm]'):null;
|
||
if(!btn)return;
|
||
e.preventDefault();
|
||
felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){
|
||
var form=btn.closest('form');
|
||
if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();}
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<div class="container">
|
||
<header>
|
||
<h1>Felhom <span>Hub</span></h1>
|
||
<nav class="nav-links">
|
||
<a href="/" class="nav-link">Dashboard</a>
|
||
<a href="/configs" class="nav-link">Customers</a>
|
||
<a href="/apps" class="nav-link">Apps</a>
|
||
<a href="/hosts" class="nav-link">Hosts</a>
|
||
<a href="/system" class="nav-link active">System</a>
|
||
<a href="/offsite" class="nav-link">Offsite</a>
|
||
<a href="/configuration" class="nav-link">Configuration</a>
|
||
</nav>
|
||
</header>
|
||
|
||
|
||
<h2 style="margin-bottom: 1rem;">System — versions and OS updates</h2>
|
||
|
||
|
||
|
||
|
||
<section class="card sys-sec" id="attention">
|
||
<h3>Needs attention</h3>
|
||
<p class="why">One line per box that is amber or red, and why. Amber: worth a look. Red: an operator alarm fires (`08` §6.3).</p>
|
||
|
||
|
||
<ul class="att">
|
||
|
||
<li><span class="mark c-warn" title="worth a look">look</span>
|
||
<a href="/hosts/tester1-0f1e2d"><strong>tester1-0f1e2d</strong></a> <span class="text-muted">Tester 1</span>
|
||
<span class="rs"><span class="c-warn" title="1 minor release behind — sign an agent_update for this box">agent behind: 0.155.0 → 0.156.0 (since 2026-10-08)</span></span></li>
|
||
|
||
<li><span class="mark c-bad" title="an operator alarm fires for this box">alarm</span>
|
||
<a href="/hosts/tester2-9a8b7c"><strong>tester2-9a8b7c</strong></a> <span class="text-muted">Tester 2</span>
|
||
<span class="rs"><span class="c-bad" title="15 minor releases behind — sign an agent_update for this box">agent behind: 0.141.0 → 0.156.0 (since 2026-10-01)</span> · <span class="c-warn">no versions reported (agent older than v0.142.0)</span> · <span class="c-warn">crash guard not installed</span></span></li>
|
||
|
||
</ul>
|
||
|
||
|
||
</section>
|
||
|
||
<section class="card sys-sec" id="waiting">
|
||
<h3>Waiting for you</h3>
|
||
<p class="why">Update sets that only you approve: the kernel, the Docker engine and the Proxmox packages. Guest and host Debian updates approve themselves after 24 h and one night on the <span class="term" title="Ring 0 = the demo boxes. They install every new fix first; the other boxes (ring 1) install only what is approved.">ring-0</span> boxes.</p>
|
||
<p class="ok-line" style="margin: 0;">Nothing waits for your approval.</p>
|
||
|
||
<ul class="testing">
|
||
<li><strong>Docker engine 29.8.3-1</strong> — still being tested: <span class="text-muted">demo-hp-d4e5f6 has 1 of 2 healthy night Docker step(s) with this set</span></li>
|
||
</ul>
|
||
|
||
</section>
|
||
|
||
|
||
<section class="card sys-sec boxes" id="boxes" style="padding-left: 0; padding-right: 0;">
|
||
<h3 style="padding: 0 1rem;">Boxes</h3>
|
||
<p class="why" style="padding: 0 1rem;">Click a box to see every value it reports, and its ring and update switches. "unknown": the box could not read the value — never a guess.</p>
|
||
<div class="bx-row bx-head"><div>Box</div><div>Ring</div><div>Health</div><div>Controller · agent</div><div>OS updates</div><div>Kernel</div><div title="The newest OS update run">Last night</div></div>
|
||
|
||
<details class="bx" id="box-demo-felhom-a1b2c3">
|
||
<summary class="bx-row">
|
||
<div class="name"><a href="/hosts/demo-felhom-a1b2c3">demo-felhom-a1b2c3</a><span class="sub">Demo N100</span></div>
|
||
<div data-label="Ring"><span class="term" title="Ring 0: a demo box — it installs every new fix first">0</span></div>
|
||
<div data-label="Health"><span class="mark" title="nothing amber or red">fine</span></div>
|
||
<div data-label="Controller · agent"><span>0.232.0</span><span class="sub" title="current (vouched 0.156.0)">agent 0.156.0</span></div>
|
||
<div data-label="OS updates"><span title="runs the newest approved guest and host releases">on · up to date</span></div>
|
||
<div data-label="Kernel"><span>7.0.14-23-pve</span></div>
|
||
<div data-label="Last night"><span title="12 h ago · applied · 41 s — last successful leg: 12 h ago">ok · 12 h ago</span></div>
|
||
</summary>
|
||
<div class="bx-more sys">
|
||
<div>
|
||
|
||
<h5>Ring and updates</h5>
|
||
<dl class="kv">
|
||
<dt>Ring</dt><dd>ring 0
|
||
<form method="POST" action="/os/ring/demo-felhom-a1b2c3">
|
||
<input type="hidden" name="_csrf" value="csrf-fixture-token"><input type="hidden" name="return" value="/system">
|
||
<input type="hidden" name="ring" value="1"><button type="submit" class="btn btn-sm btn-outline" data-confirm="Make demo-felhom-a1b2c3 a normal (ring 1) box? It then installs only approved releases.">→ normal</button>
|
||
|
||
</form></dd>
|
||
<dt>OS updates</dt><dd>updates <strong>ON</strong>
|
||
<form method="POST" action="/os/enabled/demo-felhom-a1b2c3">
|
||
<input type="hidden" name="_csrf" value="csrf-fixture-token"><input type="hidden" name="return" value="/system">
|
||
<input type="hidden" name="on" value="0"><button type="submit" class="btn btn-sm btn-outline" data-confirm="Switch OS updates OFF for demo-felhom-a1b2c3? It keeps reporting and installs nothing.">switch off</button>
|
||
|
||
</form></dd>
|
||
<dt>Tunnel</dt><dd>running</dd>
|
||
<dt>Controller</dt><dd>0.232.0</dd>
|
||
<dt>Agent</dt><dd title="current (vouched 0.156.0)">0.156.0</dd>
|
||
<dt>Last OS leg</dt><dd title="last successful leg: 12 h ago">12 h ago · applied · 41 s</dd>
|
||
</dl>
|
||
<h5>Docker engine</h5>
|
||
<dl class="kv">
|
||
<dt>Docker</dt><dd>29.8.2</dd>
|
||
<dt>containerd</dt><dd>2.3.6-1~debian.13~trixie</dd>
|
||
<dt>live-restore</dt><dd>on</dd>
|
||
<dt>Docker release</dt><dd>os-docker-20261001-031500</dd>
|
||
</dl>
|
||
</div>
|
||
<div>
|
||
<h5>Host</h5>
|
||
<dl class="kv">
|
||
<dt>Proxmox</dt><dd>9.0.11</dd>
|
||
<dt>Kernel (running)</dt><dd>7.0.14-23-pve</dd>
|
||
<dt>Kernel (next boot)</dt><dd>7.0.14-23-pve</dd>
|
||
<dt title="The kernel GRUB boots normally (R-836). Amber: a one-shot flag names another kernel for the next boot only.">Kernel (default)</dt><dd>7.0.14-23-pve</dd>
|
||
<dt title="The kernel lane (R-836): the newest step, the kernel the box is due, and whether its household was told for tonight (no mail, no step).">Kernel step</dt><dd>7.0.14-23-pve applied 30 h ago</dd>
|
||
<dt>Debian</dt><dd>13.7</dd>
|
||
<dt>Felhom release</dt><dd>os-host-20261009-031500</dd>
|
||
<dt>Pending</dt><dd>0</dd>
|
||
<dt>Not covered</dt><dd>0</dd>
|
||
<dt>Held</dt><dd>none</dd>
|
||
<dt>Reboot needed</dt><dd>no</dd>
|
||
<dt>kernel.panic</dt><dd>10 s</dd>
|
||
<dt>Oops</dt><dd>no</dd>
|
||
<dt>Crash restarts 24 h</dt><dd>0</dd>
|
||
<dt>Crash guard</dt><dd>armed</dd>
|
||
<dt title="The root-owned config bundle: sudoers, wrappers, units (R-840)">Root files</dt><dd>0.155.0</dd>
|
||
</dl>
|
||
</div>
|
||
<div>
|
||
<h5>Guest</h5>
|
||
<dl class="kv">
|
||
<dt>Guest Debian</dt><dd>13.7</dd>
|
||
<dt>Felhom release</dt><dd>os-guest-20261009-031500</dd>
|
||
<dt>Pending</dt><dd>0</dd>
|
||
<dt>Restart needed</dt><dd>0</dd>
|
||
<dt title="The newest pct fstrim of the guest's disks, and what it freed (R-444). The boxes trim weekly.">Last disk trim</dt><dd title="schedule: weekly">2 days ago · 3.0 GiB</dd>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<details class="bx" id="box-demo-hp-d4e5f6">
|
||
<summary class="bx-row">
|
||
<div class="name"><a href="/hosts/demo-hp-d4e5f6">demo-hp-d4e5f6</a><span class="sub">Demo HP</span></div>
|
||
<div data-label="Ring"><span class="term" title="Ring 0: a demo box — it installs every new fix first">0</span></div>
|
||
<div data-label="Health"><span class="mark" title="nothing amber or red">fine</span></div>
|
||
<div data-label="Controller · agent"><span>0.232.0</span><span class="sub" title="current (vouched 0.156.0)">agent 0.156.0</span></div>
|
||
<div data-label="OS updates"><span title="runs the newest approved guest and host releases">on · up to date</span></div>
|
||
<div data-label="Kernel"><span>7.0.14-23-pve</span></div>
|
||
<div data-label="Last night"><span title="12 h ago · applied · 41 s — last successful leg: 12 h ago">ok · 12 h ago</span></div>
|
||
</summary>
|
||
<div class="bx-more sys">
|
||
<div>
|
||
|
||
<h5>Ring and updates</h5>
|
||
<dl class="kv">
|
||
<dt>Ring</dt><dd>ring 0
|
||
<form method="POST" action="/os/ring/demo-hp-d4e5f6">
|
||
<input type="hidden" name="_csrf" value="csrf-fixture-token"><input type="hidden" name="return" value="/system">
|
||
<input type="hidden" name="ring" value="1"><button type="submit" class="btn btn-sm btn-outline" data-confirm="Make demo-hp-d4e5f6 a normal (ring 1) box? It then installs only approved releases.">→ normal</button>
|
||
|
||
</form></dd>
|
||
<dt>OS updates</dt><dd>updates <strong>ON</strong>
|
||
<form method="POST" action="/os/enabled/demo-hp-d4e5f6">
|
||
<input type="hidden" name="_csrf" value="csrf-fixture-token"><input type="hidden" name="return" value="/system">
|
||
<input type="hidden" name="on" value="0"><button type="submit" class="btn btn-sm btn-outline" data-confirm="Switch OS updates OFF for demo-hp-d4e5f6? It keeps reporting and installs nothing.">switch off</button>
|
||
|
||
</form></dd>
|
||
<dt>Tunnel</dt><dd>running</dd>
|
||
<dt>Controller</dt><dd>0.232.0</dd>
|
||
<dt>Agent</dt><dd title="current (vouched 0.156.0)">0.156.0</dd>
|
||
<dt>Last OS leg</dt><dd title="last successful leg: 12 h ago">12 h ago · applied · 41 s</dd>
|
||
</dl>
|
||
<h5>Docker engine</h5>
|
||
<dl class="kv">
|
||
<dt>Docker</dt><dd>29.8.2</dd>
|
||
<dt>containerd</dt><dd>2.3.6-1~debian.13~trixie</dd>
|
||
<dt>live-restore</dt><dd>on</dd>
|
||
<dt>Docker release</dt><dd>os-docker-20261001-031500</dd>
|
||
</dl>
|
||
</div>
|
||
<div>
|
||
<h5>Host</h5>
|
||
<dl class="kv">
|
||
<dt>Proxmox</dt><dd>9.0.11</dd>
|
||
<dt>Kernel (running)</dt><dd>7.0.14-23-pve</dd>
|
||
<dt>Kernel (next boot)</dt><dd>7.0.14-23-pve</dd>
|
||
<dt title="The kernel GRUB boots normally (R-836). Amber: a one-shot flag names another kernel for the next boot only.">Kernel (default)</dt><dd>7.0.14-23-pve</dd>
|
||
<dt title="The kernel lane (R-836): the newest step, the kernel the box is due, and whether its household was told for tonight (no mail, no step).">Kernel step</dt><dd>7.0.14-23-pve applied 30 h ago</dd>
|
||
<dt>Debian</dt><dd>13.7</dd>
|
||
<dt>Felhom release</dt><dd>os-host-20261009-031500</dd>
|
||
<dt>Pending</dt><dd>0</dd>
|
||
<dt>Not covered</dt><dd>0</dd>
|
||
<dt>Held</dt><dd>none</dd>
|
||
<dt>Reboot needed</dt><dd>no</dd>
|
||
<dt>kernel.panic</dt><dd>10 s</dd>
|
||
<dt>Oops</dt><dd>no</dd>
|
||
<dt>Crash restarts 24 h</dt><dd>0</dd>
|
||
<dt>Crash guard</dt><dd>armed</dd>
|
||
<dt title="The root-owned config bundle: sudoers, wrappers, units (R-840)">Root files</dt><dd>0.155.0</dd>
|
||
</dl>
|
||
</div>
|
||
<div>
|
||
<h5>Guest</h5>
|
||
<dl class="kv">
|
||
<dt>Guest Debian</dt><dd>13.7</dd>
|
||
<dt>Felhom release</dt><dd>os-guest-20261009-031500</dd>
|
||
<dt>Pending</dt><dd>0</dd>
|
||
<dt>Restart needed</dt><dd>0</dd>
|
||
<dt title="The newest pct fstrim of the guest's disks, and what it freed (R-444). The boxes trim weekly.">Last disk trim</dt><dd title="schedule: weekly">3 days ago · 3.0 GiB</dd>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<details class="bx" id="box-tester1-0f1e2d">
|
||
<summary class="bx-row">
|
||
<div class="name"><a href="/hosts/tester1-0f1e2d">tester1-0f1e2d</a><span class="sub">Tester 1</span></div>
|
||
<div data-label="Ring"><span class="term" title="Ring 1: a normal box — it installs only approved releases">1</span></div>
|
||
<div data-label="Health"><span class="mark c-warn" title="worth a look">look</span></div>
|
||
<div data-label="Controller · agent"><span>0.231.0</span><span class="sub c-warn" title="1 minor release behind — sign an agent_update for this box">agent 0.155.0 → 0.156.0 (since 2026-10-08)</span></div>
|
||
<div data-label="OS updates"><span title="not on the newest approved release yet — a box takes it at its next night run">on · guest behind</span></div>
|
||
<div data-label="Kernel"><span>7.0.14-20-pve</span></div>
|
||
<div data-label="Last night"><span title="12 h ago · nothing · 41 s — last successful leg: 36 h ago">ok · 12 h ago</span></div>
|
||
</summary>
|
||
<div class="bx-more sys">
|
||
<div>
|
||
|
||
<h5>Ring and updates</h5>
|
||
<dl class="kv">
|
||
<dt>Ring</dt><dd>ring 1
|
||
<form method="POST" action="/os/ring/tester1-0f1e2d">
|
||
<input type="hidden" name="_csrf" value="csrf-fixture-token"><input type="hidden" name="return" value="/system">
|
||
<input type="hidden" name="ring" value="0"><button type="submit" class="btn btn-sm btn-outline" data-confirm="Make tester1-0f1e2d a DEMO (ring 0) box? It then installs every new fix first and takes unsigned Docker steps if its root-owned ring-0 mark allows.">→ demo</button>
|
||
</form></dd>
|
||
<dt>OS updates</dt><dd>updates <strong>ON</strong>
|
||
<form method="POST" action="/os/enabled/tester1-0f1e2d">
|
||
<input type="hidden" name="_csrf" value="csrf-fixture-token"><input type="hidden" name="return" value="/system">
|
||
<input type="hidden" name="on" value="0"><button type="submit" class="btn btn-sm btn-outline" data-confirm="Switch OS updates OFF for tester1-0f1e2d? It keeps reporting and installs nothing.">switch off</button>
|
||
|
||
</form></dd>
|
||
<dt>Tunnel</dt><dd>running</dd>
|
||
<dt>Controller</dt><dd>0.231.0</dd>
|
||
<dt>Agent</dt><dd class="c-warn" title="1 minor release behind — sign an agent_update for this box">0.155.0 → 0.156.0 (since 2026-10-08)</dd>
|
||
<dt>Last OS leg</dt><dd title="last successful leg: 36 h ago">12 h ago · nothing · 41 s</dd>
|
||
</dl>
|
||
<h5>Docker engine</h5>
|
||
<dl class="kv">
|
||
<dt>Docker</dt><dd>29.8.2</dd>
|
||
<dt>containerd</dt><dd>2.3.6-1~debian.13~trixie</dd>
|
||
<dt>live-restore</dt><dd>on</dd>
|
||
<dt>Docker release</dt><dd>—</dd>
|
||
</dl>
|
||
</div>
|
||
<div>
|
||
<h5>Host</h5>
|
||
<dl class="kv">
|
||
<dt>Proxmox</dt><dd>9.0.11</dd>
|
||
<dt>Kernel (running)</dt><dd>7.0.14-20-pve</dd>
|
||
<dt>Kernel (next boot)</dt><dd>7.0.14-20-pve</dd>
|
||
<dt title="The kernel GRUB boots normally (R-836). Amber: a one-shot flag names another kernel for the next boot only.">Kernel (default)</dt><dd>7.0.14-20-pve</dd>
|
||
<dt title="The kernel lane (R-836): the newest step, the kernel the box is due, and whether its household was told for tonight (no mail, no step).">Kernel step</dt><dd>due 7.0.14-23-pve — not told yet (mails 09–20 h)</dd>
|
||
<dt>Debian</dt><dd>13.7</dd>
|
||
<dt>Felhom release</dt><dd>os-host-20261009-031500</dd>
|
||
<dt>Pending</dt><dd>0</dd>
|
||
<dt>Not covered</dt><dd>0</dd>
|
||
<dt>Held</dt><dd>none</dd>
|
||
<dt>Reboot needed</dt><dd>no</dd>
|
||
<dt>kernel.panic</dt><dd>10 s</dd>
|
||
<dt>Oops</dt><dd>no</dd>
|
||
<dt>Crash restarts 24 h</dt><dd>0</dd>
|
||
<dt>Crash guard</dt><dd>armed</dd>
|
||
<dt title="The root-owned config bundle: sudoers, wrappers, units (R-840)">Root files</dt><dd>0.155.0</dd>
|
||
</dl>
|
||
</div>
|
||
<div>
|
||
<h5>Guest</h5>
|
||
<dl class="kv">
|
||
<dt>Guest Debian</dt><dd>13.7</dd>
|
||
<dt>Felhom release</dt><dd>os-guest-20261008-031500</dd>
|
||
<dt>Pending</dt><dd>0</dd>
|
||
<dt>Restart needed</dt><dd>0</dd>
|
||
<dt title="The newest pct fstrim of the guest's disks, and what it freed (R-444). The boxes trim weekly.">Last disk trim</dt><dd title="schedule: weekly">4 days ago · 3.0 GiB</dd>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
<details class="bx" id="box-tester2-9a8b7c">
|
||
<summary class="bx-row">
|
||
<div class="name"><a href="/hosts/tester2-9a8b7c">tester2-9a8b7c</a><span class="sub">Tester 2</span></div>
|
||
<div data-label="Ring"><span class="term" title="Ring 1: a normal box — it installs only approved releases">1</span></div>
|
||
<div data-label="Health"><span class="mark c-bad" title="an operator alarm fires for this box">alarm</span></div>
|
||
<div data-label="Controller · agent"><span>0.229.0</span><span class="sub c-bad" title="15 minor releases behind — sign an agent_update for this box">agent 0.141.0 → 0.156.0 (since 2026-10-01)</span></div>
|
||
<div data-label="OS updates"><span title="runs the newest approved guest and host releases">on · up to date</span></div>
|
||
<div data-label="Kernel"><span class="c-warn">unknown</span></div>
|
||
<div data-label="Last night"><span title="12 h ago · nothing · 41 s — last successful leg: 12 h ago">ok · 12 h ago</span></div>
|
||
</summary>
|
||
<div class="bx-more sys">
|
||
<div>
|
||
<p class="c-warn" style="font-weight: normal; margin: 0.6rem 0 0;">no versions reported (agent older than v0.142.0)</p>
|
||
<h5>Ring and updates</h5>
|
||
<dl class="kv">
|
||
<dt>Ring</dt><dd>ring 1
|
||
<form method="POST" action="/os/ring/tester2-9a8b7c">
|
||
<input type="hidden" name="_csrf" value="csrf-fixture-token"><input type="hidden" name="return" value="/system">
|
||
<input type="hidden" name="ring" value="0"><button type="submit" class="btn btn-sm btn-outline" data-confirm="Make tester2-9a8b7c a DEMO (ring 0) box? It then installs every new fix first and takes unsigned Docker steps if its root-owned ring-0 mark allows.">→ demo</button>
|
||
</form></dd>
|
||
<dt>OS updates</dt><dd>updates <strong>ON</strong>
|
||
<form method="POST" action="/os/enabled/tester2-9a8b7c">
|
||
<input type="hidden" name="_csrf" value="csrf-fixture-token"><input type="hidden" name="return" value="/system">
|
||
<input type="hidden" name="on" value="0"><button type="submit" class="btn btn-sm btn-outline" data-confirm="Switch OS updates OFF for tester2-9a8b7c? It keeps reporting and installs nothing.">switch off</button>
|
||
|
||
</form></dd>
|
||
<dt>Tunnel</dt><dd>running</dd>
|
||
<dt>Controller</dt><dd>0.229.0</dd>
|
||
<dt>Agent</dt><dd class="c-bad" title="15 minor releases behind — sign an agent_update for this box">0.141.0 → 0.156.0 (since 2026-10-01)</dd>
|
||
<dt>Last OS leg</dt><dd title="last successful leg: 12 h ago">12 h ago · nothing · 41 s</dd>
|
||
</dl>
|
||
<h5>Docker engine</h5>
|
||
<dl class="kv">
|
||
<dt>Docker</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt>containerd</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt>live-restore</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt>Docker release</dt><dd>—</dd>
|
||
</dl>
|
||
</div>
|
||
<div>
|
||
<h5>Host</h5>
|
||
<dl class="kv">
|
||
<dt>Proxmox</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt>Kernel (running)</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt>Kernel (next boot)</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt title="The kernel GRUB boots normally (R-836). Amber: a one-shot flag names another kernel for the next boot only.">Kernel (default)</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt title="The kernel lane (R-836): the newest step, the kernel the box is due, and whether its household was told for tonight (no mail, no step).">Kernel step</dt><dd title="the box reports no kernel lane (agent older than v0.152.0)">—</dd>
|
||
<dt>Debian</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt>Felhom release</dt><dd>os-host-20261009-031500</dd>
|
||
<dt>Pending</dt><dd>0</dd>
|
||
<dt>Not covered</dt><dd>0</dd>
|
||
<dt>Held</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt>Reboot needed</dt><dd>no</dd>
|
||
<dt>kernel.panic</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt>Oops</dt><dd>no</dd>
|
||
<dt>Crash restarts 24 h</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt>Crash guard</dt><dd class="c-warn">not installed</dd>
|
||
<dt title="The root-owned config bundle: sudoers, wrappers, units (R-840)">Root files</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
</dl>
|
||
</div>
|
||
<div>
|
||
<h5>Guest</h5>
|
||
<dl class="kv">
|
||
<dt>Guest Debian</dt><dd class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</dd>
|
||
<dt>Felhom release</dt><dd>os-guest-20261009-031500</dd>
|
||
<dt>Pending</dt><dd>0</dd>
|
||
<dt>Restart needed</dt><dd>0</dd>
|
||
<dt title="The newest pct fstrim of the guest's disks, and what it freed (R-444). The boxes trim weekly.">Last disk trim</dt><dd title="the agent reports no disk trim (an agent without the weekly trim)">—</dd>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</details>
|
||
|
||
</section>
|
||
|
||
|
||
<details class="card more" id="details">
|
||
<summary>Details</summary>
|
||
<p class="why text-muted" style="font-size: 0.85em;">Release ids, cancelled approvals, what ring 0 runs, the version floors, and every box's crash guard and root files.</p>
|
||
|
||
<h3>Approved releases</h3>
|
||
<p class="text-muted" style="font-size: 0.85em; margin-top: 0;">The newest approved set of each layer. A <span class="term" title="Approved while a TEST wait override was active. It is cancelled when the hub starts without the override (`11` §5.3.1).">TEST approval</span> is amber.</p>
|
||
<div class="rel-grid">
|
||
|
||
<div><strong>guest</strong>: <code>os-guest-20261009-031500</code><br>
|
||
<span class="text-muted">214 packages · 2026-10-09 09:17 UTC · by auto</span>
|
||
</div>
|
||
|
||
<div><strong>host</strong>: <code>os-host-20261009-031500</code><br>
|
||
<span class="text-muted">389 packages · 2026-10-09 09:17 UTC · by auto</span>
|
||
</div>
|
||
|
||
<div><strong>docker</strong>: <code>os-docker-20261001-031500</code><br>
|
||
<span class="text-muted">4 packages · 2026-10-09 09:17 UTC · by auto</span>
|
||
</div>
|
||
|
||
<div><strong>pve</strong>: <code>os-pve-20261007-090000</code><br>
|
||
<span class="text-muted">31 packages · 2026-10-09 09:17 UTC · by auto</span>
|
||
</div>
|
||
|
||
<div><strong>kernel</strong>: <code>os-kernel-20261010-091200</code><br>
|
||
<span class="text-muted">2 packages · 2026-10-09 09:17 UTC · by operator</span>
|
||
</div>
|
||
|
||
</div>
|
||
<form method="POST" action="/os/approve-now" style="margin-top: 0.8rem;">
|
||
<input type="hidden" name="_csrf" value="csrf-fixture-token"><input type="hidden" name="return" value="/system">
|
||
<button type="submit" class="btn btn-sm btn-danger" data-confirm="Approve the guest and host sets now, without the usual 24 h and one night? Every ring-1 box installs them at its next night run.">Approve now (guest + host)</button>
|
||
<span class="text-muted" style="font-size: 0.85em;">An urgent fix only — normally the hub approves after 24 h and one night.</span>
|
||
</form>
|
||
|
||
<h3>Cancelled approvals (last 7 days)</h3>
|
||
<div class="rel-grid">
|
||
|
||
<div><strong>guest</strong>: <code>os-guest-20261006-100000</code><br>
|
||
<span class="c-warn">cancelled 2026-10-07 08:00:00 UTC — a TEST approval</span><br>
|
||
<span class="text-muted">no further box installs it; boxes that installed it keep it</span></div>
|
||
|
||
<div><strong>host</strong>: <code>os-host-20261006-110000</code><br>
|
||
<span class="c-warn">cancelled 2026-10-07 08:00:00 UTC — a TEST approval</span><br>
|
||
<span class="text-muted">no further box installs it; boxes that installed it keep it</span></div>
|
||
|
||
<div><strong>docker</strong>: <code>os-docker-20261006-120000</code><br>
|
||
<span class="c-warn">cancelled 2026-10-07 08:00:00 UTC — a TEST approval</span><br>
|
||
<span class="text-muted">no further box installs it; boxes that installed it keep it</span></div>
|
||
|
||
<div><strong>pve</strong>: <code>os-pve-20261006-130000</code><br>
|
||
<span class="c-warn">cancelled 2026-10-07 08:00:00 UTC — a TEST approval</span><br>
|
||
<span class="text-muted">no further box installs it; boxes that installed it keep it</span></div>
|
||
|
||
</div>
|
||
|
||
|
||
<h3>What ring 0 runs now</h3>
|
||
<div class="rel-grid">
|
||
|
||
<div><strong>guest</strong>:
|
||
214 packages, first seen 2026-10-09 03:17 UTC<br>
|
||
<span class="text-muted">approved as os-guest-20261009-031500</span>
|
||
|
||
</div>
|
||
|
||
<div><strong>host</strong>:
|
||
389 packages, first seen 2026-10-09 03:17 UTC<br>
|
||
<span class="text-muted">approved as os-host-20261009-031500</span>
|
||
|
||
</div>
|
||
|
||
<div><strong>docker</strong>:
|
||
4 packages, first seen 2026-10-09 19:17 UTC<br>
|
||
<span class="text-muted">demo-hp-d4e5f6 has 1 of 2 healthy night Docker step(s) with this set</span>
|
||
|
||
</div>
|
||
|
||
<div><strong>pve</strong>:
|
||
31 packages, first seen 2026-10-06 15:17 UTC<br>
|
||
<span class="text-muted">approved as os-pve-20261007-090000</span>
|
||
|
||
</div>
|
||
|
||
<div><strong>kernel</strong>:
|
||
2 packages, first seen 2026-10-09 13:17 UTC<br>
|
||
<span class="text-muted">approved as os-kernel-20261010-091200</span>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<h3 id="version-floors">Version floors</h3>
|
||
<p>Global controller floor: <strong>0.229.0</strong> · vouched agent: <strong>0.156.0</strong></p>
|
||
<p class="text-muted" style="font-size: 0.85em; margin-top: -0.5rem;"><span class="term" title="A floor is the lowest controller version a box must run; the hub moves a box below it up.">What is a floor?</span> · <span class="term" title="The agent version the operator checked and signed off for the fleet. Agents update only by a per-box signed job.">What is a vouched agent?</span></p>
|
||
|
||
<div class="tbl-wrap">
|
||
<table class="data-table">
|
||
<thead><tr><th>Customer</th><th>Own floor</th><th>Set</th><th>Global floor moves it?</th></tr></thead>
|
||
<tbody>
|
||
|
||
<tr>
|
||
<td><a href="/customers/c-tester1">c-tester1</a><br><span class="text-muted">Tester 1</span></td>
|
||
<td>0.231.0</td>
|
||
<td class="" title="">5 days ago (2026-10-05)</td>
|
||
<td>no — its own floor applies (at or above the global)</td>
|
||
</tr>
|
||
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
|
||
|
||
<h3>Crash guard and root files</h3>
|
||
<div class="tbl-wrap">
|
||
<table class="data-table sys">
|
||
<thead><tr><th>Box</th><th>kernel.panic</th><th>Oops</th><th>Crash restarts 24 h</th><th>Crash guard</th><th title="The root-owned config bundle: sudoers, wrappers, units (R-840)">Root files</th></tr></thead>
|
||
<tbody>
|
||
|
||
<tr><td><a href="/hosts/demo-felhom-a1b2c3">demo-felhom-a1b2c3</a></td><td>10 s</td><td>no</td><td>0</td><td>armed</td><td>0.155.0</td></tr>
|
||
|
||
<tr><td><a href="/hosts/demo-hp-d4e5f6">demo-hp-d4e5f6</a></td><td>10 s</td><td>no</td><td>0</td><td>armed</td><td>0.155.0</td></tr>
|
||
|
||
<tr><td><a href="/hosts/tester1-0f1e2d">tester1-0f1e2d</a></td><td>10 s</td><td>no</td><td>0</td><td>armed</td><td>0.155.0</td></tr>
|
||
|
||
<tr><td><a href="/hosts/tester2-9a8b7c">tester2-9a8b7c</a></td><td class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</td><td>no</td><td class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</td><td class="c-warn">not installed</td><td class="c-warn" title="the box could not read it (agent older than v0.142.0, or the guest is down)">unknown</td></tr>
|
||
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
</details>
|
||
|
||
<footer style="margin-top: 2rem; color: var(--text-muted); font-size: 0.8rem; text-align: center;">
|
||
Felhom Hub <span style="font-family: var(--font-mono)">test</span>
|
||
</footer>
|
||
</div>
|
||
<script>
|
||
|
||
(function(){var h=location.hash&&document.getElementById(location.hash.slice(1));if(!h)return;var d=h.closest('details');while(d){d.open=true;d=d.parentElement&&d.parentElement.closest('details');}h.scrollIntoView();})();
|
||
</script>
|
||
</body>
|
||
</html>
|
||
|
||
|