Enable STIR/SHAKEN card in compliance checker with originate/terminate toggle

- Uncomment STIR/SHAKEN check in fcc-lookup.ts — shows self-reported
  implementation status from RMD filing
- Add toggle: "Do you originate calls or only terminate?"
  - Terminate only → green, signing cert not required, file RMD as
    partial implementation
  - Originate or both → red, must have own STI certificate as of
    June 2025
- Toggle integrates with pending-question system (CTA waits for answer)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-04-29 10:55:00 -05:00
parent b02b5b4c1f
commit 050b19a43a
2 changed files with 57 additions and 14 deletions

View file

@ -367,18 +367,16 @@ router.get("/api/v1/fcc/lookup", async (req, res) => {
ssDetail = "Cannot determine — no RMD filing found for this FRN. Verify STIR/SHAKEN status directly.";
}
// STIR/SHAKEN hidden until STI-PA API access is operational.
// The check runs internally (ssStatus/ssDetail computed above) but
// is not exposed to the customer-facing compliance dashboard.
// Uncomment when iConectiv IP whitelist is approved.
// checks.push({
// id: "stir_shaken",
// label: "STIR/SHAKEN Compliance",
// status: ssStatus,
// detail: ssDetail,
// action_url: null,
// due_date: null,
// });
// STIR/SHAKEN — show based on RMD self-reported implementation status.
// Note: this is NOT verified against the STI-PA certificate authority.
checks.push({
id: "stir_shaken",
label: "STIR/SHAKEN Call Authentication",
status: ssStatus,
detail: ssDetail,
action_url: null,
due_date: null,
});
// 4. CPNI Annual Certification
//

View file

@ -406,6 +406,50 @@ Send reset link
});
}
// STIR/SHAKEN toggle — terminating-only providers don't need a signing certificate
if (!checks.querySelector(".pw-stir-toggle")) {
var stirCards = checks.querySelectorAll(":scope > div");
stirCards.forEach(function(card) {
if (card.textContent.indexOf("STIR/SHAKEN") < 0) return;
if (card.querySelector(".pw-stir-toggle")) return;
// Only show for red/amber cards
if (!card.classList.contains("bg-red-50") && !card.classList.contains("bg-amber-50")) return;
var detail = card.querySelectorAll("p")[1];
var icon = card.querySelector("span");
var flexDiv = card.querySelector(".flex-1");
if (!flexDiv || !detail) return;
var q = document.createElement("div");
q.className = "pw-stir-toggle mt-3 flex items-center gap-2 flex-wrap";
q.innerHTML =
'<span class="text-sm font-bold text-gray-900 w-full mb-1">Do you originate calls (place them onto the network), or do you only receive and terminate calls from other carriers?</span>' +
'<button type="button" class="px-4 py-1.5 text-xs font-bold rounded-lg border-2 border-red-300 bg-red-50 text-red-700 hover:bg-red-100 hover:border-red-400 transition-colors" data-stir="originate">I originate calls</button>' +
'<button type="button" class="px-4 py-1.5 text-xs font-bold rounded-lg border-2 border-green-300 bg-green-50 text-green-700 hover:bg-green-100 hover:border-green-400 transition-colors" data-stir="terminate">Terminate only</button>' +
'<button type="button" class="px-4 py-1.5 text-xs font-bold rounded-lg border-2 border-amber-300 bg-amber-50 text-amber-700 hover:bg-amber-100 hover:border-amber-400 transition-colors" data-stir="both">Both</button>';
flexDiv.appendChild(q);
q.addEventListener("click", function(ev) {
var b = ev.target.closest("[data-stir]");
if (!b) return;
q.remove();
if (b.dataset.stir === "terminate") {
card.className = "bg-green-50 border-green-200 border rounded-xl p-4 flex items-start gap-3";
detail.innerHTML = 'Signing certificate not required \u2014 terminating-only providers verify incoming call signatures (a software configuration) but do not need their own STI certificate. File your RMD as &ldquo;partial implementation&rdquo; describing your verification process.';
detail.className = "text-xs text-green-800 mt-1";
icon.className = "text-green-600 shrink-0 mt-0.5";
icon.innerHTML = '<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg>';
card.dataset.stirExempt = "true";
} else {
// Originate or both — needs own certificate
card.className = "bg-red-50 border-red-200 border rounded-xl p-4 flex items-start gap-3";
detail.innerHTML = 'STIR/SHAKEN signing certificate required \u2014 as of June 2025, originating providers must sign all calls with their own STI certificate. You can no longer rely on upstream providers to sign on your behalf. Obtain a certificate from an approved CA (e.g. Sievert Larsen, Neustar, TransNexus).';
detail.className = "text-xs text-red-800 mt-1";
icon.className = "text-red-600 shrink-0 mt-0.5";
icon.innerHTML = '<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>';
}
});
});
}
// CALEA toggle — wholesale-only carriers are exempt ("interconnecting carriers")
if (!checks.querySelector(".pw-calea-toggle")) {
var caleaCards = checks.querySelectorAll(":scope > div");
@ -472,7 +516,8 @@ Send reset link
var hasRmdQuestion = !!checks.querySelector(".pw-rmd-toggle");
var has499aQuestion = !!checks.querySelector(".pw-499a-toggle");
var hasCaleaQuestion = !!checks.querySelector(".pw-calea-toggle");
var hasPendingQuestion = hasBdcQuestion || hasRmdQuestion || has499aQuestion || hasCaleaQuestion;
var hasStirQuestion = !!checks.querySelector(".pw-stir-toggle");
var hasPendingQuestion = hasBdcQuestion || hasRmdQuestion || has499aQuestion || hasCaleaQuestion || hasStirQuestion;
var frn = currentFrn;
var url = "/order/fcc-compliance?services=" + services.join(",") + (frn ? "&frn=" + frn : "");
var cta = document.createElement("div");
@ -491,7 +536,7 @@ Send reset link
'<button type="button" id="pw-cta-btn" disabled class="inline-block px-6 py-2.5 bg-gray-400 text-white font-semibold rounded-lg cursor-not-allowed text-sm">Get Started \u2014 Fix ' + count + ' Item' + (count > 1 ? 's' : '') + '</button>';
// Watch for toggle removal (= answered)
var bdcWatcher = new MutationObserver(function() {
if (!checks.querySelector(".pw-bdc-toggle") && !checks.querySelector(".pw-rmd-toggle") && !checks.querySelector(".pw-499a-toggle") && !checks.querySelector(".pw-calea-toggle")) {
if (!checks.querySelector(".pw-bdc-toggle") && !checks.querySelector(".pw-rmd-toggle") && !checks.querySelector(".pw-499a-toggle") && !checks.querySelector(".pw-calea-toggle") && !checks.querySelector(".pw-stir-toggle")) {
bdcWatcher.disconnect();
// Re-count actionable items (red + amber)
var newServices = [];