Add terminate-only STIR/SHAKEN option across RMD pipeline

STIRShakenStep intake:
- New "Terminate only" option for carriers that only receive pre-signed
  calls and don't originate
- Contextual hints for each option explaining requirements
- Show/hide vendor and upstream fields based on selection

RMD letter generator:
- New terminate_only section explaining verification-only posture,
  citing 47 CFR § 64.6301 (signing obligation on originating provider)
- Added to needs_exhibit_a list

RMD Exhibit A generator:
- New terminate_only STIR/SHAKEN paragraph with SBC verification language
- Fixed scope paragraph: wholesale/facilities carriers no longer get
  "small provider without Class 4 switch" boilerplate
- Fixed OCN paragraph: wholesale carriers get neutral wording instead
  of "no OCN required for small retail provider"

RMD filing handler:
- Maps stir_shaken_status to rmd_option for Exhibit A generation
- Passes entity metadata (ocn, wholesale, gateway, contact) to generator
- Maps terminate_only → partial_implementation for FCC RMD form radio

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-04-29 10:59:28 -05:00
parent 050b19a43a
commit fbf3b8a1ea
4 changed files with 123 additions and 17 deletions

View file

@ -138,6 +138,25 @@ def _stir_shaken_paragraph(
f"high-volume origination, or facilities-based IP origination "
f"infrastructure."
)
elif option == "terminate_only":
paras.append(
f"{entity_name} operates as a terminating provider and does not "
f"originate calls onto the public switched telephone network. "
f"{entity_abbr} receives exclusively pre-authenticated traffic "
f"from originating and intermediate providers. As a "
f"terminating-only carrier, {entity_abbr} is not required to "
f"maintain its own STI certificate or SPC token for call signing."
)
paras.append(
f"{entity_abbr} verifies STIR/SHAKEN attestation information on "
f"all inbound SIP INVITE messages using standard verification "
f"service (VS) functionality configured on its session border "
f"controllers. {entity_abbr} certifies partial STIR/SHAKEN "
f"implementation (Option 2 in the RMD) reflecting its "
f"verification-only posture. Per FCC rules, the obligation to "
f"sign calls falls on the originating provider, not the "
f"terminating provider."
)
elif option == "option3":
paras.append(
f"{entity_name} certifies no STIR/SHAKEN signing implementation "
@ -203,12 +222,18 @@ def _scope_paragraph(
f"{entity_abbr} does not accept foreign-originated traffic and "
f"operates solely with domestic U.S. NANP resources."
)
parts.append(
f"As a small provider without its own Class 4 switch or outbound "
f"origination platform, {entity_abbr} relies on trusted underlying "
f"carriers for DID origination, call termination, and STIR/SHAKEN "
f"attestation/signing where applicable."
)
if is_wholesale or carrier_role in ("wholesale_domestic", "gateway", "facilities"):
parts.append(
f"{entity_abbr} operates its own network infrastructure for "
f"voice service delivery."
)
else:
parts.append(
f"As a small provider without its own Class 4 switch or outbound "
f"origination platform, {entity_abbr} relies on trusted underlying "
f"carriers for DID origination, call termination, and STIR/SHAKEN "
f"attestation/signing where applicable."
)
parts.append(
f"This Robocall Mitigation Plan outlines {entity_abbr}'s measures to "
f"detect, prevent, and mitigate unlawful robocalls in compliance "
@ -382,12 +407,18 @@ def generate_exhibit_a(
if ocn:
_add_body(doc, f"OCN: {ocn}")
else:
_add_body(doc, (
f"{entity_abbr} does not possess an Operating Company Number "
f"(OCN). Per FCC guidance, no OCN is required for a small "
f"retail provider without local exchange carrier status; "
f"\"No\" is selected on the RMD form."
))
if is_wholesale or carrier_role in ("wholesale_domestic", "gateway"):
_add_body(doc, (
f"{entity_abbr} does not currently possess an Operating "
f"Company Number (OCN). \"No\" is selected on the RMD form."
))
else:
_add_body(doc, (
f"{entity_abbr} does not possess an Operating Company Number "
f"(OCN). Per FCC guidance, no OCN is required for a small "
f"retail provider without local exchange carrier status; "
f"\"No\" is selected on the RMD form."
))
_add_body(doc, "Principals, Affiliates, Subsidiaries, and Parent Companies:", bold=True)
_add_bullets(doc, principals or [f"{contact_name or entity_name} (sole principal)"])

View file

@ -463,6 +463,7 @@ def generate_rmd_letter(
needs_exhibit_a = stir_shaken_status in (
"partial_implementation",
"terminate_only",
"robocall_mitigation_only",
"exempt_small_carrier",
)
@ -1071,6 +1072,24 @@ def _build_stir_shaken_section(
italic=True,
)
elif stir_shaken_status == "terminate_only":
b.body(
f"{entity_name} operates exclusively as a terminating provider "
f"and does not originate calls onto the public switched telephone "
f"network. {entity_name} receives pre-authenticated traffic from "
f"originating and intermediate providers and verifies STIR/SHAKEN "
f"attestation information on all inbound SIP INVITE messages."
)
b.body(
f"As a terminating-only carrier, {entity_name} is not required to "
f"maintain its own STI certificate or SPC token for call signing. "
f"Per FCC rules (47 CFR § 64.6301), the obligation to sign calls "
f"falls on the originating provider, not the terminating provider. "
f"{entity_name} certifies partial STIR/SHAKEN implementation "
f"(reflecting its verification-only posture) and has implemented "
f"a robocall mitigation program as described in Exhibit A."
)
elif stir_shaken_status == "robocall_mitigation_only":
b.body(
f"{entity_name} has not implemented STIR/SHAKEN caller ID "

View file

@ -299,6 +299,7 @@ class RMDFilingHandler(BaseServiceHandler):
stir_status = entity.get("stir_shaken_status", "complete_implementation")
if stir_status in (
"partial_implementation",
"terminate_only",
"robocall_mitigation_only",
"exempt_small_carrier",
):
@ -317,12 +318,30 @@ class RMDFilingHandler(BaseServiceHandler):
work_dir,
f"robocall_mitigation_program_{order_number}_{date_str}.docx",
)
# Map stir_shaken_status to Exhibit A rmd_option
_STATUS_TO_OPTION = {
"complete_implementation": "option1",
"partial_implementation": "option2",
"terminate_only": "terminate_only",
"robocall_mitigation_only": "option3",
"exempt_small_carrier": "option3",
}
exhibit = generate_exhibit_a(
entity_name=entity.get("legal_name", ""),
frn=entity.get("frn", ""),
ocn=entity.get("ocn", ""),
carrier_role=role,
carrier_metadata=entity.get("carrier_metadata", {}),
upstream_provider_name=entity.get("upstream_provider_name", ""),
is_wholesale=entity.get("is_wholesale", False),
is_gateway=entity.get("is_gateway_provider", False),
foreign_traffic=entity.get("is_international_only", False),
rmd_option=_STATUS_TO_OPTION.get(stir_status, "option2"),
contact_name=entity.get("contact_name", ""),
contact_title=entity.get("contact_title", ""),
contact_email=entity.get("contact_email", ""),
contact_phone=entity.get("contact_phone", ""),
address=entity.get("address", ""),
llm_generate=self._call_llm,
output_path=exhibit_docx,
)
@ -422,8 +441,11 @@ class RMDFilingHandler(BaseServiceHandler):
)
# STIR/SHAKEN implementation status — value must match the
# RMD form radio options.
# RMD form radio options. terminate_only maps to
# partial_implementation on the FCC form (verification-only).
stir_status = entity.get("stir_shaken_status", "complete_implementation")
if stir_status == "terminate_only":
stir_status = "partial_implementation"
await page.click(f'input[name="stir_shaken_status"][value="{stir_status}"]')
await human_delay()

View file

@ -13,16 +13,18 @@
<label class="pw-field">STIR/SHAKEN implementation</label>
<select id="pw-ss-status" class="pw-input">
<option value="">Select…</option>
<option value="complete_implementation">Complete — I sign calls with my own cert</option>
<option value="partial_implementation">Partial — upstream carrier signs my traffic</option>
<option value="complete_implementation">Complete — I sign all outbound calls with my own STI certificate</option>
<option value="partial_implementation">Partial — my upstream carrier signs calls on my behalf</option>
<option value="terminate_only">Terminate only — I only receive pre-signed calls, I don't originate</option>
<option value="robocall_mitigation_only">Robocall Mitigation Only — no STIR/SHAKEN signing</option>
<option value="exempt_small_carrier">Exempt small carrier</option>
</select>
<p id="pw-ss-hint" class="pw-hint" hidden></p>
<label class="pw-field">STI-CA vendor preference (optional)</label>
<label class="pw-field" id="pw-ss-vendor-label">STI-CA vendor preference (optional)</label>
<input type="text" id="pw-ss-vendor" class="pw-input" placeholder="e.g. iconectiv, Peeringhub, Neustar (leave blank if unsure)" />
<label class="pw-field">Upstream voice provider (if partial / robocall-mitigation-only)</label>
<label class="pw-field" id="pw-ss-upstream-label">Upstream voice provider (if partial / robocall-mitigation-only)</label>
<input type="text" id="pw-ss-upstream" class="pw-input" placeholder="e.g. Bandwidth.com, VoIP Innovations" />
<div id="pw-ss-err" class="pw-err" hidden></div>
@ -33,17 +35,49 @@
.pw-help { color: #64748b; font-size: 0.9rem; margin-bottom: 1rem; }
.pw-field { display: block; font-weight: 600; margin: 0.8rem 0 0.2rem; font-size: 0.88rem; }
.pw-input { width: 100%; padding: 0.5rem 0.7rem; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 0.93rem; }
.pw-hint { color: #4b5563; font-size: 0.82rem; margin-top: 0.3rem; padding: 0.5rem 0.7rem; background: #f0f4f8; border-radius: 6px; line-height: 1.5; }
.pw-err { color: #b91c1c; margin-top: 0.75rem; font-size: 0.9rem; }
</style>
<script>
const g = <T extends HTMLElement>(id: string) => document.getElementById(id) as T;
const HINTS: Record<string, string> = {
complete_implementation: "You originate calls and sign them with your own STI certificate from an approved Certificate Authority.",
partial_implementation: "Your upstream carrier signs calls on your behalf. As of June 2025, you must still make all attestation-level decisions — your upstream only performs the technical act of signing.",
terminate_only: "You only receive and terminate pre-signed calls from other carriers. You verify incoming signatures (software config) but don't need your own signing certificate. Your RMD will be filed as \"partial implementation\" describing your verification process.",
robocall_mitigation_only: "You have a robocall mitigation program but no STIR/SHAKEN signing. This is typically for non-IP networks or providers awaiting STI certificate issuance.",
exempt_small_carrier: "Small carriers with fewer than 100,000 subscriber lines may qualify for a temporary exemption.",
};
function updateSSFields() {
const val = g<HTMLSelectElement>("pw-ss-status").value;
const hint = g<HTMLElement>("pw-ss-hint");
const vendorLabel = g<HTMLElement>("pw-ss-vendor-label");
const vendorInput = g<HTMLInputElement>("pw-ss-vendor");
const upstreamLabel = g<HTMLElement>("pw-ss-upstream-label");
const upstreamInput = g<HTMLInputElement>("pw-ss-upstream");
// Show hint
if (val && HINTS[val]) { hint.textContent = HINTS[val]; hint.hidden = false; }
else { hint.hidden = true; }
// Show/hide vendor (only for complete)
const showVendor = val === "complete_implementation";
vendorLabel.style.display = showVendor ? "" : "none";
vendorInput.style.display = showVendor ? "" : "none";
// Show/hide upstream (for partial and robocall-only)
const showUpstream = val === "partial_implementation" || val === "robocall_mitigation_only";
upstreamLabel.style.display = showUpstream ? "" : "none";
upstreamInput.style.display = showUpstream ? "" : "none";
}
g<HTMLSelectElement>("pw-ss-status").addEventListener("change", updateSSFields);
window.addEventListener("pw:step-shown", (evt: any) => {
if (evt.detail.step !== "stir_shaken") return;
const s = (window as any).PWIntake.get();
g<HTMLSelectElement>("pw-ss-status").value = s.intake_data?.target_stir_shaken_status || s.entity?.stir_shaken_status || "";
g<HTMLInputElement>("pw-ss-vendor").value = s.intake_data?.sti_ca_vendor || "";
g<HTMLInputElement>("pw-ss-upstream").value = s.intake_data?.upstream_provider_name || "";
updateSSFields();
});
window.addEventListener("pw:step-next", (evt: any) => {
const PW = (window as any).PWIntake;