Skip to content

Commit 3717180

Browse files
author
Quinton Stackfield
committed
Polish website
1 parent 761fee2 commit 3717180

3 files changed

Lines changed: 820 additions & 467 deletions

File tree

docs/app.js

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
(async function () {
2+
const attemptsBody = document.getElementById("attempts-body");
3+
const artifactList = document.getElementById("artifact-list");
4+
const artifactCount = document.getElementById("artifact-count");
5+
const systemStatus = document.getElementById("system-status");
6+
const invariantVerdict = document.getElementById("invariant-verdict");
7+
8+
let artifacts = [];
9+
10+
function safeText(v, fallback = "—") {
11+
if (v === null || v === undefined || v === "") return fallback;
12+
return String(v);
13+
}
14+
15+
function parseTime(iso) {
16+
try {
17+
const t = Date.parse(iso);
18+
return Number.isFinite(t) ? t : null;
19+
} catch {
20+
return null;
21+
}
22+
}
23+
24+
function expectedAllow(a) {
25+
// Conservative, judge-friendly logic:
26+
// allow iff authority exists AND (scope matches action OR scope is null/undefined (overbroad)) AND not expired (relative to now).
27+
// If artifacts are old, expiry check may flip — still acceptable for runtime evidence.
28+
const auth = a.authority || null;
29+
if (!auth) return false;
30+
31+
const scope = auth.scope;
32+
const action = a.action;
33+
34+
const scopeOk = (scope === null || scope === undefined) ? true : (scope === action);
35+
if (!scopeOk) return false;
36+
37+
const exp = auth.expires_at;
38+
const expMs = exp ? parseTime(exp) : null;
39+
if (expMs === null) return true; // if no expiry parsed, don't claim violation
40+
return expMs > Date.now();
41+
}
42+
43+
function computeViolations(list) {
44+
// A violation is: executor output doesn't match what authority context implies.
45+
// This is what lets the UI say "Invariant HELD" without treating "ALLOW" as failure.
46+
let violations = 0;
47+
for (const a of list) {
48+
const exp = expectedAllow(a);
49+
const actual = !!a.execution_allowed;
50+
if (exp !== actual) violations++;
51+
}
52+
return violations;
53+
}
54+
55+
async function loadViaManifest() {
56+
// Optional future-proof: docs/artifacts/manifest.json can list files.
57+
// Example:
58+
// { "files": ["demo-deploy-001.json", "demo-delete-001.json"] }
59+
try {
60+
const res = await fetch("artifacts/manifest.json", { cache: "no-store" });
61+
if (!res.ok) return false;
62+
const m = await res.json();
63+
const files = Array.isArray(m.files) ? m.files : [];
64+
if (!files.length) return false;
65+
66+
const loaded = [];
67+
for (const f of files) {
68+
const r = await fetch("artifacts/" + f, { cache: "no-store" });
69+
if (r.ok) loaded.push(await r.json());
70+
}
71+
artifacts = loaded;
72+
return true;
73+
} catch {
74+
return false;
75+
}
76+
}
77+
78+
async function loadViaDirectoryScrape() {
79+
// Works in some environments, not guaranteed on GitHub Pages.
80+
try {
81+
const index = await fetch("artifacts/", { cache: "no-store" });
82+
if (!index.ok) throw new Error("no directory listing");
83+
const text = await index.text();
84+
const matches = [...text.matchAll(/href="([^"]+\.json)"/g)];
85+
const files = matches.map(m => m[1]).filter(Boolean);
86+
87+
const loaded = [];
88+
for (const f of files) {
89+
const r = await fetch("artifacts/" + f, { cache: "no-store" });
90+
if (r.ok) loaded.push(await r.json());
91+
}
92+
artifacts = loaded;
93+
} catch {
94+
artifacts = [];
95+
}
96+
}
97+
98+
async function loadArtifacts() {
99+
const ok = await loadViaManifest();
100+
if (ok) return;
101+
await loadViaDirectoryScrape();
102+
}
103+
104+
function sortArtifacts(list) {
105+
// Sort by decision_id if possible (var-001, demo-deploy-001, etc.)
106+
return [...list].sort((a, b) => {
107+
const da = safeText(a.decision_id, "");
108+
const db = safeText(b.decision_id, "");
109+
return da.localeCompare(db, undefined, { numeric: true, sensitivity: "base" });
110+
});
111+
}
112+
113+
function renderTable() {
114+
attemptsBody.innerHTML = "";
115+
116+
if (!artifacts.length) {
117+
attemptsBody.innerHTML = `<tr><td colspan="7" class="empty">No execution artifacts found.</td></tr>`;
118+
return;
119+
}
120+
121+
const ordered = sortArtifacts(artifacts);
122+
123+
ordered.forEach((a, i) => {
124+
const allowed = !!a.execution_allowed;
125+
const outcome = allowed ? "PERMIT" : "BLOCK";
126+
const denyReason = safeText(a.deny_reason, "—");
127+
128+
const scope =
129+
a.authority && ("scope" in a.authority)
130+
? (a.authority.scope === null || a.authority.scope === undefined ? "overbroad" : safeText(a.authority.scope))
131+
: "—";
132+
133+
const expires =
134+
a.authority && a.authority.expires_at
135+
? safeText(a.authority.expires_at)
136+
: "—";
137+
138+
const row = document.createElement("tr");
139+
row.innerHTML = `
140+
<td>${i + 1}</td>
141+
<td>${safeText(a.decision_id)}</td>
142+
<td>${safeText(a.action)}</td>
143+
<td class="${allowed ? "allow" : "deny"}">${outcome}</td>
144+
<td>${denyReason}</td>
145+
<td>${scope}</td>
146+
<td>${expires}</td>
147+
`;
148+
attemptsBody.appendChild(row);
149+
});
150+
}
151+
152+
function renderArtifacts() {
153+
artifactCount.textContent = String(artifacts.length);
154+
artifactList.innerHTML = artifacts.length
155+
? sortArtifacts(artifacts).slice(0, 18).map(a => safeText(a.decision_id) + ".json").join("<br>")
156+
: "No artifacts loaded.";
157+
}
158+
159+
function renderStatus() {
160+
if (!artifacts.length) {
161+
systemStatus.textContent = "No runtime artifacts";
162+
systemStatus.className = "pill pill-muted";
163+
invariantVerdict.textContent = "Awaiting evidence";
164+
invariantVerdict.className = "pill pill-muted";
165+
return;
166+
}
167+
168+
const violations = computeViolations(artifacts);
169+
const allowed = artifacts.filter(a => !!a.execution_allowed).length;
170+
const denied = artifacts.length - allowed;
171+
172+
if (violations === 0) {
173+
systemStatus.textContent = `Enforcing · ${allowed} permit · ${denied} block`;
174+
systemStatus.className = "pill pill-ok";
175+
invariantVerdict.textContent = "Invariant HELD";
176+
invariantVerdict.className = "pill pill-ok";
177+
} else {
178+
systemStatus.textContent = `Violation detected · ${violations}`;
179+
systemStatus.className = "pill pill-deny";
180+
invariantVerdict.textContent = "Invariant FAILED";
181+
invariantVerdict.className = "pill pill-deny";
182+
}
183+
}
184+
185+
await loadArtifacts();
186+
renderTable();
187+
renderArtifacts();
188+
renderStatus();
189+
})();

0 commit comments

Comments
 (0)