Tighten control plane flow and status presentation
This commit is contained in:
+16
-24
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<div class="control-state">
|
<div class="control-state">
|
||||||
<span class="status-dot"></span>
|
<span class="status-dot"></span>
|
||||||
<span>LAB CONFIGURATION / VERIFIED OCT 2026</span>
|
<span>LIVE TELEMETRY / CONFIG VERIFIED OCT 2026</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav aria-label="Control plane navigation">
|
<nav aria-label="Control plane navigation">
|
||||||
@@ -150,14 +150,10 @@
|
|||||||
|
|
||||||
<aside class="activity-panel">
|
<aside class="activity-panel">
|
||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<span>OPERATIONAL CAPABILITIES</span>
|
<span>PLATFORM CONTROLS</span>
|
||||||
<small>DOCUMENTED</small>
|
<small>DOCUMENTED</small>
|
||||||
</div>
|
</div>
|
||||||
<ol class="event-list">
|
<ol class="event-list">
|
||||||
<li>
|
|
||||||
<span class="event-ok">DEPLOY</span>
|
|
||||||
<p>Automated deployment through Woodpecker CI</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<span class="event-info">AUTH</span>
|
<span class="event-info">AUTH</span>
|
||||||
<p>OIDC authentication through Authentik</p>
|
<p>OIDC authentication through Authentik</p>
|
||||||
@@ -166,10 +162,6 @@
|
|||||||
<span class="event-ok">BACKUP</span>
|
<span class="event-ok">BACKUP</span>
|
||||||
<p>Persistent data backup and documented recovery procedures</p>
|
<p>Persistent data backup and documented recovery procedures</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<span class="event-warn">SEC</span>
|
|
||||||
<p>CrowdSec threat detection and decision evidence</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<span class="event-info">TLS</span>
|
<span class="event-info">TLS</span>
|
||||||
<p>Certificate and HTTPS management through the reverse proxy</p>
|
<p>Certificate and HTTPS management through the reverse proxy</p>
|
||||||
@@ -248,7 +240,7 @@
|
|||||||
<a href="assets/images/portfolio-grafana-logs.png" target="_blank">
|
<a href="assets/images/portfolio-grafana-logs.png" target="_blank">
|
||||||
<img src="assets/images/portfolio-grafana-logs.png" alt="Centralized logs snapshot in Grafana" />
|
<img src="assets/images/portfolio-grafana-logs.png" alt="Centralized logs snapshot in Grafana" />
|
||||||
</a>
|
</a>
|
||||||
<p class="evidence-note">CAPTURED OCT 2026 · LOGGING EVIDENCE SNAPSHOT · VALUES SHOWN ARE FROM CAPTURE TIME</p>
|
<p class="evidence-note">OCT 2026 · LOGGING EVIDENCE</p>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
<div class="coverage-matrix">
|
<div class="coverage-matrix">
|
||||||
@@ -282,15 +274,15 @@
|
|||||||
<div class="service-layout">
|
<div class="service-layout">
|
||||||
<div class="service-table" role="table" aria-label="Service inventory">
|
<div class="service-table" role="table" aria-label="Service inventory">
|
||||||
<div class="service-row service-head" role="row">
|
<div class="service-row service-head" role="row">
|
||||||
<span>Service</span><span>Role</span><span>Access</span><span>State</span>
|
<span>Service</span><span>Role</span><span>Access</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="service-row"><strong>Gitea</strong><span>Source control</span><span>SSO</span><i>CONFIGURED</i></div>
|
<div class="service-row"><strong>Gitea</strong><span>Source control</span><span>SSO</span></div>
|
||||||
<div class="service-row"><strong>Wiki.js</strong><span>Documentation</span><span>SSO</span><i>CONFIGURED</i></div>
|
<div class="service-row"><strong>Wiki.js</strong><span>Documentation</span><span>SSO</span></div>
|
||||||
<div class="service-row"><strong>Dashy</strong><span>Service portal</span><span>SSO</span><i>CONFIGURED</i></div>
|
<div class="service-row"><strong>Dashy</strong><span>Service portal</span><span>SSO</span></div>
|
||||||
<div class="service-row"><strong>Vaultwarden</strong><span>Secrets</span><span>DIRECT</span><i>CONFIGURED</i></div>
|
<div class="service-row"><strong>Vaultwarden</strong><span>Secrets</span><span>DIRECT</span></div>
|
||||||
<div class="service-row"><strong>Stirling PDF</strong><span>Document tools</span><span>SSO</span><i>CONFIGURED</i></div>
|
<div class="service-row"><strong>Stirling PDF</strong><span>Document tools</span><span>SSO</span></div>
|
||||||
<div class="service-row"><strong>IT Tools</strong><span>Utilities</span><span>SSO</span><i>CONFIGURED</i></div>
|
<div class="service-row"><strong>IT Tools</strong><span>Utilities</span><span>SSO</span></div>
|
||||||
<div class="service-row"><strong>Uptime Kuma</strong><span>Availability</span><span>SSO</span><i>CONFIGURED</i></div>
|
<div class="service-row"><strong>Uptime Kuma</strong><span>Availability</span><span>SSO</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="portal-shot live-service-panel" aria-label="Live service health">
|
<div class="portal-shot live-service-panel" aria-label="Live service health">
|
||||||
@@ -299,7 +291,7 @@
|
|||||||
<small>SANITIZED ENDPOINT CHECKS</small>
|
<small>SANITIZED ENDPOINT CHECKS</small>
|
||||||
</figcaption>
|
</figcaption>
|
||||||
<div class="service-health-grid">
|
<div class="service-health-grid">
|
||||||
<div><span>GITEA</span><strong id="health-gitea">CHECKING</strong><small id="gitea-version">VERSION --</small></div>
|
<div><span>GITEA</span><strong id="health-gitea">CHECKING</strong><small>SOURCE CONTROL</small></div>
|
||||||
<div><span>WOODPECKER</span><strong id="health-woodpecker">CHECKING</strong><small>CI / DELIVERY</small></div>
|
<div><span>WOODPECKER</span><strong id="health-woodpecker">CHECKING</strong><small>CI / DELIVERY</small></div>
|
||||||
<div><span>UPTIME KUMA</span><strong id="health-kuma">CHECKING</strong><small>AVAILABILITY</small></div>
|
<div><span>UPTIME KUMA</span><strong id="health-kuma">CHECKING</strong><small>AVAILABILITY</small></div>
|
||||||
<div><span>PROMETHEUS</span><strong id="health-prometheus">CHECKING</strong><small>METRICS</small></div>
|
<div><span>PROMETHEUS</span><strong id="health-prometheus">CHECKING</strong><small>METRICS</small></div>
|
||||||
@@ -336,7 +328,7 @@
|
|||||||
<div class="delivery-state">
|
<div class="delivery-state">
|
||||||
<span class="delivery-kicker">SERVICE STATE</span>
|
<span class="delivery-kicker">SERVICE STATE</span>
|
||||||
<strong id="pipeline-gitea-state">CHECKING</strong>
|
<strong id="pipeline-gitea-state">CHECKING</strong>
|
||||||
<small id="pipeline-gitea-version">VERSION --</small>
|
<small>REPOSITORY HOST</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="delivery-meta">
|
<div class="delivery-meta">
|
||||||
<span>ROLE</span><strong>REPOSITORY HOST</strong>
|
<span>ROLE</span><strong>REPOSITORY HOST</strong>
|
||||||
@@ -392,7 +384,7 @@
|
|||||||
<a href="assets/images/portfolio-crowdsec-decisions.png" target="_blank">
|
<a href="assets/images/portfolio-crowdsec-decisions.png" target="_blank">
|
||||||
<img src="assets/images/portfolio-crowdsec-decisions.png" alt="CrowdSec firewall decision evidence snapshot" />
|
<img src="assets/images/portfolio-crowdsec-decisions.png" alt="CrowdSec firewall decision evidence snapshot" />
|
||||||
</a>
|
</a>
|
||||||
<p class="evidence-note">CAPTURED OCT 2026 · SECURITY EVIDENCE SNAPSHOT · NOT A LIVE DECISION FEED</p>
|
<p class="evidence-note">OCT 2026 · DECISION-HISTORY EVIDENCE</p>
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -418,7 +410,7 @@
|
|||||||
<a href="assets/images/portfolio-wiki-disaster-recovery.png" target="_blank">
|
<a href="assets/images/portfolio-wiki-disaster-recovery.png" target="_blank">
|
||||||
<img src="assets/images/portfolio-wiki-disaster-recovery.png" alt="ByteGeist disaster recovery runbook documentation snapshot" />
|
<img src="assets/images/portfolio-wiki-disaster-recovery.png" alt="ByteGeist disaster recovery runbook documentation snapshot" />
|
||||||
</a>
|
</a>
|
||||||
<p class="evidence-note">CAPTURED OCT 2026 · DOCUMENTED RECOVERY PROCEDURE</p>
|
<p class="evidence-note">OCT 2026 · RUNBOOK CAPTURE</p>
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -429,7 +421,7 @@
|
|||||||
<img src="assets/images/bytegeist-logo.png" alt="" />
|
<img src="assets/images/bytegeist-logo.png" alt="" />
|
||||||
<div><strong>BYTEGEIST</strong><span>KEITH CASKO / INFRASTRUCTURE LAB</span></div>
|
<div><strong>BYTEGEIST</strong><span>KEITH CASKO / INFRASTRUCTURE LAB</span></div>
|
||||||
</div>
|
</div>
|
||||||
<p>PLAIN HTML / CSS / JAVASCRIPT</p>
|
<p>HTML / CSS / JAVASCRIPT · PYTHON STATUS API · NGINX</p>
|
||||||
<p>VERIFICATION: <span class="status-text">OCT 2026</span></p>
|
<p>VERIFICATION: <span class="status-text">OCT 2026</span></p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -19,9 +19,7 @@ const fields = {
|
|||||||
healthWoodpecker: document.querySelector("#health-woodpecker"),
|
healthWoodpecker: document.querySelector("#health-woodpecker"),
|
||||||
healthKuma: document.querySelector("#health-kuma"),
|
healthKuma: document.querySelector("#health-kuma"),
|
||||||
healthPrometheus: document.querySelector("#health-prometheus"),
|
healthPrometheus: document.querySelector("#health-prometheus"),
|
||||||
giteaVersion: document.querySelector("#gitea-version"),
|
|
||||||
pipelineGiteaState: document.querySelector("#pipeline-gitea-state"),
|
pipelineGiteaState: document.querySelector("#pipeline-gitea-state"),
|
||||||
pipelineGiteaVersion: document.querySelector("#pipeline-gitea-version"),
|
|
||||||
pipelineWoodpeckerState: document.querySelector("#pipeline-woodpecker-state"),
|
pipelineWoodpeckerState: document.querySelector("#pipeline-woodpecker-state"),
|
||||||
deployedCommit: document.querySelector("#deployed-commit"),
|
deployedCommit: document.querySelector("#deployed-commit"),
|
||||||
};
|
};
|
||||||
@@ -31,7 +29,7 @@ const telemetryHistory = {
|
|||||||
memory: [],
|
memory: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
let lastSuccessfulUpdate = null;
|
let lastSampledAt = null;
|
||||||
let requestInFlight = false;
|
let requestInFlight = false;
|
||||||
|
|
||||||
function updateClock() {
|
function updateClock() {
|
||||||
@@ -80,8 +78,6 @@ function setUnavailable() {
|
|||||||
element.classList.add("status-down");
|
element.classList.add("status-down");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fields.giteaVersion.textContent = "VERSION --";
|
|
||||||
fields.pipelineGiteaVersion.textContent = "VERSION --";
|
|
||||||
fields.deployedCommit.textContent = "-------";
|
fields.deployedCommit.textContent = "-------";
|
||||||
refreshElement.textContent = "TELEMETRY UNAVAILABLE";
|
refreshElement.textContent = "TELEMETRY UNAVAILABLE";
|
||||||
refreshElement.classList.add("telemetry-error");
|
refreshElement.classList.add("telemetry-error");
|
||||||
@@ -104,10 +100,6 @@ function pushHistory(series, value) {
|
|||||||
if (series.length > 24) series.shift();
|
if (series.length > 24) series.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
function serviceLabel(service) {
|
|
||||||
return service && service.up ? "ONLINE" : "UNAVAILABLE";
|
|
||||||
}
|
|
||||||
|
|
||||||
function setServiceState(element, service) {
|
function setServiceState(element, service) {
|
||||||
if (!element) return;
|
if (!element) return;
|
||||||
const up = Boolean(service && service.up);
|
const up = Boolean(service && service.up);
|
||||||
@@ -143,11 +135,6 @@ function renderStatus(data) {
|
|||||||
setServiceState(fields.pipelineGiteaState, services.gitea);
|
setServiceState(fields.pipelineGiteaState, services.gitea);
|
||||||
setServiceState(fields.pipelineWoodpeckerState, services.woodpecker);
|
setServiceState(fields.pipelineWoodpeckerState, services.woodpecker);
|
||||||
|
|
||||||
const giteaVersion = services.gitea && services.gitea.version
|
|
||||||
? "VERSION " + services.gitea.version
|
|
||||||
: "VERSION --";
|
|
||||||
fields.giteaVersion.textContent = giteaVersion;
|
|
||||||
fields.pipelineGiteaVersion.textContent = giteaVersion;
|
|
||||||
fields.deployedCommit.textContent = data.deployment && data.deployment.commit
|
fields.deployedCommit.textContent = data.deployment && data.deployment.commit
|
||||||
? data.deployment.commit
|
? data.deployment.commit
|
||||||
: "-------";
|
: "-------";
|
||||||
@@ -159,14 +146,21 @@ function renderStatus(data) {
|
|||||||
fields.telemetry.classList.remove("metric-error");
|
fields.telemetry.classList.remove("metric-error");
|
||||||
refreshElement.classList.remove("telemetry-error");
|
refreshElement.classList.remove("telemetry-error");
|
||||||
|
|
||||||
lastSuccessfulUpdate = Date.now();
|
lastSampledAt = Number(data.sampled_at) * 1000;
|
||||||
updateRefreshLabel();
|
updateRefreshLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateRefreshLabel() {
|
function updateRefreshLabel() {
|
||||||
if (!lastSuccessfulUpdate) return;
|
if (!lastSampledAt) return;
|
||||||
const ageSeconds = Math.max(0, Math.floor((Date.now() - lastSuccessfulUpdate) / 1000));
|
const ageSeconds = Math.max(0, Math.floor((Date.now() - lastSampledAt) / 1000));
|
||||||
refreshElement.textContent = "LIVE TELEMETRY | UPDATED " + String(ageSeconds).padStart(2, "0") + "s AGO";
|
const stale = ageSeconds > 45;
|
||||||
|
|
||||||
|
refreshElement.textContent = (stale ? "TELEMETRY STALE | SAMPLE " : "LIVE TELEMETRY | SAMPLE ")
|
||||||
|
+ String(ageSeconds).padStart(2, "0") + "s AGO";
|
||||||
|
refreshElement.classList.toggle("telemetry-error", stale);
|
||||||
|
|
||||||
|
fields.telemetry.textContent = stale ? "STALE" : "LIVE";
|
||||||
|
fields.telemetry.classList.toggle("metric-error", stale);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshTelemetry() {
|
async function refreshTelemetry() {
|
||||||
@@ -198,4 +192,3 @@ refreshTelemetry();
|
|||||||
window.setInterval(updateClock, 1000);
|
window.setInterval(updateClock, 1000);
|
||||||
window.setInterval(updateRefreshLabel, 1000);
|
window.setInterval(updateRefreshLabel, 1000);
|
||||||
window.setInterval(refreshTelemetry, 15000);
|
window.setInterval(refreshTelemetry, 15000);
|
||||||
|
|
||||||
|
|||||||
@@ -63,11 +63,6 @@ def check_services():
|
|||||||
try:
|
try:
|
||||||
status, body = get_text(url)
|
status, body = get_text(url)
|
||||||
services[name] = {"up": 200 <= status < 400, "status": status}
|
services[name] = {"up": 200 <= status < 400, "status": status}
|
||||||
if name == "gitea":
|
|
||||||
try:
|
|
||||||
services[name]["version"] = json.loads(body).get("version")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
except Exception:
|
except Exception:
|
||||||
services[name] = {"up": False, "status": None}
|
services[name] = {"up": False, "status": None}
|
||||||
return services
|
return services
|
||||||
|
|||||||
@@ -664,10 +664,10 @@ h1 {
|
|||||||
.module-header {
|
.module-header {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr minmax(260px, .75fr) auto;
|
grid-template-columns: 1fr minmax(260px, .75fr) auto;
|
||||||
gap: 40px;
|
gap: 28px;
|
||||||
align-items: end;
|
align-items: end;
|
||||||
min-height: 132px;
|
min-height: 108px;
|
||||||
padding: 28px 30px;
|
padding: 22px 28px;
|
||||||
border-bottom: 1px solid var(--line-strong);
|
border-bottom: 1px solid var(--line-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,7 +679,7 @@ h1 {
|
|||||||
|
|
||||||
.module h2 {
|
.module h2 {
|
||||||
margin: 7px 0 0;
|
margin: 7px 0 0;
|
||||||
font-size: clamp(1.8rem, 3vw, 3.3rem);
|
font-size: clamp(1.65rem, 2.5vw, 2.7rem);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
letter-spacing: -.05em;
|
letter-spacing: -.05em;
|
||||||
@@ -868,7 +868,7 @@ h1 {
|
|||||||
|
|
||||||
.service-row {
|
.service-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1.2fr .65fr .55fr;
|
grid-template-columns: 1fr 1.25fr .7fr;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: 53px;
|
min-height: 53px;
|
||||||
@@ -1331,7 +1331,7 @@ footer {
|
|||||||
|
|
||||||
.service-row span:nth-child(3),
|
.service-row span:nth-child(3),
|
||||||
.service-head span:nth-child(3) {
|
.service-head span:nth-child(3) {
|
||||||
display: none;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.system-map {
|
.system-map {
|
||||||
@@ -1376,3 +1376,57 @@ footer {
|
|||||||
animation-iteration-count: 1 !important;
|
animation-iteration-count: 1 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Final rhythm and evidence polish */
|
||||||
|
.module + .module {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module + .module::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
left: 28px;
|
||||||
|
width: 48px;
|
||||||
|
height: 1px;
|
||||||
|
background: var(--purple-low);
|
||||||
|
}
|
||||||
|
|
||||||
|
.evidence-note {
|
||||||
|
opacity: .72;
|
||||||
|
font-size: .49rem;
|
||||||
|
letter-spacing: .05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.evidence-wide img,
|
||||||
|
.crowdsec-shot img,
|
||||||
|
.recovery-layout img {
|
||||||
|
max-height: 430px;
|
||||||
|
object-fit: contain;
|
||||||
|
object-position: top center;
|
||||||
|
background: #050608;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-panel .event-list li {
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-panel .event-list p {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 560px) {
|
||||||
|
.module-header {
|
||||||
|
min-height: auto;
|
||||||
|
padding: 20px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module + .module::before {
|
||||||
|
left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-row {
|
||||||
|
grid-template-columns: 1fr 1fr auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user