Disable owner-only launcher actions until attested

This commit is contained in:
Joshua 2026-02-18 14:23:37 -08:00
parent f41b014ebb
commit 82141d8e22
3 changed files with 26 additions and 0 deletions

View File

@ -60,6 +60,7 @@ Policy behavior in launcher shell:
1. Membership is required for all member-channel polling flows. 1. Membership is required for all member-channel polling flows.
2. `onramp_attested` identity assurance is required for owner support-ticket and governance install-token actions. 2. `onramp_attested` identity assurance is required for owner support-ticket and governance install-token actions.
3. Assurance state is displayed independently from membership state in the top summary cards. 3. Assurance state is displayed independently from membership state in the top summary cards.
4. Owner-only buttons are UI-disabled until both membership is active and assurance is `onramp_attested`.
Run locally: Run locally:

View File

@ -83,6 +83,23 @@ function setSummary(id, value) {
el.textContent = value; el.textContent = value;
} }
function setButtonDisabled(id, disabled) {
const el = $(id);
if (!el) return;
el.disabled = Boolean(disabled);
}
function refreshActionLocks(statusPayload) {
const effective = statusPayload && typeof statusPayload === "object" ? statusPayload : state.lastStatus;
const membershipActive = String(effective?.status || "").toLowerCase() === "active";
const attested = isOnrampAttested(effective?.identity_assurance_level);
const ownerActionReady = membershipActive && attested;
setButtonDisabled("btnSupportTicket", !ownerActionReady);
setButtonDisabled("btnInstallToken", !ownerActionReady);
setButtonDisabled("btnQuickInstallStatus", !membershipActive);
}
function refreshOverview(statusPayload) { function refreshOverview(statusPayload) {
const currentWallet = wallet(); const currentWallet = wallet();
setSummary("summaryWallet", currentWallet || "not connected"); setSummary("summaryWallet", currentWallet || "not connected");
@ -93,6 +110,7 @@ function refreshOverview(statusPayload) {
setSummary("summaryAssurance", assurance); setSummary("summaryAssurance", assurance);
setSummary("summaryAdminPolicy", isOnrampAttested(assurance) ? "ready" : "blocked"); setSummary("summaryAdminPolicy", isOnrampAttested(assurance) ? "ready" : "blocked");
setSummary("summaryLastSync", nowISO()); setSummary("summaryLastSync", nowISO());
refreshActionLocks(statusPayload);
return; return;
} }
const designation = $("designationCode")?.value?.trim() || "-"; const designation = $("designationCode")?.value?.trim() || "-";
@ -100,6 +118,7 @@ function refreshOverview(statusPayload) {
const assurance = assuranceDisplay(state.lastStatus?.identity_assurance_level); const assurance = assuranceDisplay(state.lastStatus?.identity_assurance_level);
setSummary("summaryAssurance", assurance); setSummary("summaryAssurance", assurance);
setSummary("summaryAdminPolicy", isOnrampAttested(assurance) ? "ready" : "blocked"); setSummary("summaryAdminPolicy", isOnrampAttested(assurance) ? "ready" : "blocked");
refreshActionLocks();
} }
function setFlowStatus(message) { function setFlowStatus(message) {

View File

@ -153,6 +153,12 @@ button:active {
transform: translateY(1px); transform: translateY(1px);
} }
button:disabled {
cursor: not-allowed;
filter: grayscale(0.5);
opacity: 0.55;
}
.event-list { .event-list {
display: grid; display: grid;
gap: 8px; gap: 8px;