Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers, document generators, FCC compliance tools, Canada CRTC formation, Ansible infrastructure, and deployment scripts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
f8cd37ac8c
1823 changed files with 145167 additions and 0 deletions
602
scripts/document_gen/templates/rmd_exhibit_a_generator.py
Normal file
602
scripts/document_gen/templates/rmd_exhibit_a_generator.py
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
"""
|
||||
Generate the Robocall Mitigation Plan (Exhibit A to RMD filing).
|
||||
|
||||
Follows the Performance West canonical 7-section outline observed across
|
||||
all example filings in docs/examplefilings/ (Engage, Franklin, Zingo,
|
||||
Syntracom, VoIPFlo, Fortel). This is a deterministic, template-driven
|
||||
generator — no LLM — with role-specific paragraphs for each carrier
|
||||
category.
|
||||
|
||||
2026 Updates (2025 RMD R&O effective Feb 5, 2026):
|
||||
* References recertification due March 2, 2026 (March 1 is Sunday)
|
||||
* Multi-factor authentication on RMD portal
|
||||
* $10,000 false-info forfeiture and $1,000/day late-update forfeiture
|
||||
* 10-business-day material-change update deadline
|
||||
* Explicit DNO list blocking + 4-hour high-risk alert review SLA
|
||||
* Penalty-of-perjury declaration at the end
|
||||
|
||||
Canonical section outline (mirrors docs/examplefilings/):
|
||||
|
||||
Introduction (scope narrative)
|
||||
1. Contact Information (+ Principals/Affiliates, past-2-years affirmation)
|
||||
2. Implementation of STIR/SHAKEN Framework (Option 1/2/3, named upstream)
|
||||
3. Robocall Monitoring and Mitigation
|
||||
3.5 Know Your Customer (KYC) Procedures (Performed In-House)
|
||||
4. Call Analytics and Upstream Provider Procedures
|
||||
5. Compliance with FCC Requirements
|
||||
6. Future Enhancements
|
||||
7. Commitment to Correct Deficiencies
|
||||
Conclusion
|
||||
Perjury declaration + signature block
|
||||
|
||||
Usage:
|
||||
from scripts.document_gen.templates.rmd_exhibit_a_generator import (
|
||||
generate_exhibit_a,
|
||||
)
|
||||
path = generate_exhibit_a(
|
||||
entity_name="Falcon Broadband LLC",
|
||||
entity_abbr="FBL",
|
||||
frn="0027160886",
|
||||
address="123 Example St, City, ST 00000",
|
||||
contact_name="Jane Doe",
|
||||
contact_title="President",
|
||||
contact_email="jane@falconbroadband.com",
|
||||
contact_phone="555-123-4567",
|
||||
principals=["Jane Doe"],
|
||||
carrier_role="ucaas",
|
||||
upstream_provider_name="VoIP Innovations",
|
||||
rmd_option="option2", # "option1" | "option2" | "option3"
|
||||
scope_narrative="small UCaaS provider serving retail end-users",
|
||||
output_path="/tmp/rmd_plan.docx",
|
||||
)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Awaitable, Callable, Optional
|
||||
|
||||
LOG = logging.getLogger("document_gen.rmd_exhibit_a")
|
||||
|
||||
try:
|
||||
from docx import Document
|
||||
from docx.shared import Pt, Inches, RGBColor
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.oxml.ns import qn
|
||||
from docx.oxml import OxmlElement
|
||||
except ImportError:
|
||||
LOG.warning("python-docx not installed — RMD plan generation unavailable")
|
||||
Document = None # type: ignore[assignment, misc]
|
||||
|
||||
NAVY_BLUE = RGBColor(0x1A, 0x27, 0x44) if Document else None
|
||||
HEADING_SIZE = Pt(12)
|
||||
BODY_SIZE = Pt(10)
|
||||
PARA_SPACING_AFTER = Pt(6)
|
||||
|
||||
|
||||
# ── RMD option labels per FCC 2025 R&O ────────────────────────────────────
|
||||
# Option 1: complete STIR/SHAKEN implementation
|
||||
# Option 2: partial STIR/SHAKEN — IP portions only, relying on upstream
|
||||
# Option 3: robocall-mitigation-only (no STIR/SHAKEN signing at all)
|
||||
RMD_OPTION_LABELS = {
|
||||
"option1": "Option 1 — Complete STIR/SHAKEN Implementation",
|
||||
"option2": "Option 2 — Partial STIR/SHAKEN Implementation",
|
||||
"option3": "Option 3 — Robocall Mitigation Only (no STIR/SHAKEN signing)",
|
||||
}
|
||||
|
||||
|
||||
# ── Role-specific STIR/SHAKEN language ────────────────────────────────────
|
||||
|
||||
|
||||
def _stir_shaken_paragraph(
|
||||
*,
|
||||
entity_name: str,
|
||||
entity_abbr: str,
|
||||
rmd_option: str,
|
||||
upstream_provider_name: str,
|
||||
carrier_role: str,
|
||||
ocn: str,
|
||||
) -> list[str]:
|
||||
"""Return a list of paragraphs describing STIR/SHAKEN implementation."""
|
||||
option = rmd_option.lower()
|
||||
upstream = upstream_provider_name or "its underlying carrier"
|
||||
|
||||
paras: list[str] = []
|
||||
|
||||
if option == "option1":
|
||||
paras.append(
|
||||
f"{entity_name} complies with the STIR/SHAKEN call authentication "
|
||||
f"framework and signs all outbound calls originated from its "
|
||||
f"network using its own STI certificate. {entity_abbr} certifies "
|
||||
f"complete STIR/SHAKEN implementation (Option 1 in the RMD)."
|
||||
)
|
||||
if ocn:
|
||||
paras.append(
|
||||
f"All calls are attested at level A, B, or C as appropriate "
|
||||
f"using {entity_abbr}'s OCN {ocn} under its STI-CA-issued "
|
||||
f"certificate."
|
||||
)
|
||||
elif option == "option2":
|
||||
paras.append(
|
||||
f"{entity_name} complies with FCC STIR/SHAKEN caller authentication "
|
||||
f"requirements through its partnership with {upstream}. This "
|
||||
f"partnership ensures that all outbound calls are attested under "
|
||||
f"the STIR/SHAKEN framework; calls originating from the network "
|
||||
f"are validated to verify caller identity and detect spoofed "
|
||||
f"calls."
|
||||
)
|
||||
paras.append(
|
||||
f"{entity_abbr} certifies partial STIR/SHAKEN implementation "
|
||||
f"(Option 2 in the RMD) for IP portions of the network, relying "
|
||||
f"on {upstream} for technical attestation/signing. {entity_abbr} "
|
||||
f"makes all attestation-level decisions based on verified customer "
|
||||
f"right-to-use of DIDs and provides this information to the "
|
||||
f"upstream carrier for signing. {entity_abbr} does not maintain "
|
||||
f"its own SPC token or certificate, as this is unnecessary and "
|
||||
f"disproportionate for a small provider without wholesale, "
|
||||
f"high-volume origination, or facilities-based IP origination "
|
||||
f"infrastructure."
|
||||
)
|
||||
elif option == "option3":
|
||||
paras.append(
|
||||
f"{entity_name} certifies no STIR/SHAKEN signing implementation "
|
||||
f"(Option 3 in the RMD). {entity_abbr} does not originate "
|
||||
f"outbound calls that require its own STIR/SHAKEN signing and "
|
||||
f"does not maintain an SPC token or certificate. Inbound call "
|
||||
f"authentication is verified using STIR/SHAKEN attestation "
|
||||
f"results provided by upstream carriers."
|
||||
)
|
||||
else:
|
||||
paras.append(
|
||||
f"{entity_name} complies with FCC STIR/SHAKEN requirements in "
|
||||
f"accordance with its filing option on the Robocall Mitigation "
|
||||
f"Database."
|
||||
)
|
||||
|
||||
paras.append(
|
||||
f"{entity_abbr} confirms that no previous certification has been "
|
||||
f"removed by Commission action."
|
||||
)
|
||||
return paras
|
||||
|
||||
|
||||
# ── Role-specific scope language ──────────────────────────────────────────
|
||||
|
||||
|
||||
_ROLE_SCOPE_DEFAULTS = {
|
||||
"ucaas": "small UCaaS provider serving end-users",
|
||||
"facilities": "facilities-based voice service provider serving end-users",
|
||||
"reseller": "voice service reseller",
|
||||
"wholesale_domestic": "domestic wholesale voice provider",
|
||||
"gateway": "international gateway provider",
|
||||
"international_only": "carrier handling exclusively international voice traffic",
|
||||
}
|
||||
|
||||
|
||||
def _scope_paragraph(
|
||||
*,
|
||||
entity_name: str,
|
||||
entity_abbr: str,
|
||||
carrier_role: str,
|
||||
scope_narrative: str,
|
||||
is_wholesale: bool,
|
||||
is_gateway: bool,
|
||||
foreign_traffic: bool,
|
||||
) -> str:
|
||||
default_role = _ROLE_SCOPE_DEFAULTS.get(carrier_role, "voice service provider")
|
||||
narrative = scope_narrative or default_role
|
||||
|
||||
parts = [
|
||||
f"{entity_name} (\"{entity_abbr}\"), a {narrative}, is committed to "
|
||||
f"mitigating unlawful robocalls and complying with Federal "
|
||||
f"Communications Commission (FCC) regulations."
|
||||
]
|
||||
if not is_wholesale and carrier_role not in ("wholesale_domestic", "gateway"):
|
||||
parts.append(
|
||||
f"{entity_abbr} does not provide wholesale services, SIP trunking, "
|
||||
f"origination for resellers, or act as an intermediate/gateway "
|
||||
f"provider in any call path."
|
||||
)
|
||||
if not foreign_traffic:
|
||||
parts.append(
|
||||
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."
|
||||
)
|
||||
parts.append(
|
||||
f"This Robocall Mitigation Plan outlines {entity_abbr}'s measures to "
|
||||
f"detect, prevent, and mitigate unlawful robocalls in compliance "
|
||||
f"with FCC regulations, including 47 CFR \u00a7 64.6305 and the updated "
|
||||
f"requirements from the 2025 Robocall Mitigation Database Report "
|
||||
f"and Order (effective February 5, 2026, with first annual "
|
||||
f"recertification due March 2, 2026 because March 1, 2026 falls on "
|
||||
f"a Sunday)."
|
||||
)
|
||||
return "\n\n".join(parts)
|
||||
|
||||
|
||||
# ── Doc helpers ───────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _add_heading(doc, text: str, level: int = 1) -> None:
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(12 if level == 1 else 8)
|
||||
p.paragraph_format.space_after = PARA_SPACING_AFTER
|
||||
run = p.add_run(text)
|
||||
run.bold = True
|
||||
run.font.size = HEADING_SIZE if level == 1 else Pt(11)
|
||||
run.font.color.rgb = NAVY_BLUE
|
||||
|
||||
|
||||
def _add_body(doc, text: str, bold: bool = False) -> None:
|
||||
for chunk in text.split("\n\n"):
|
||||
chunk = chunk.strip()
|
||||
if not chunk:
|
||||
continue
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_after = PARA_SPACING_AFTER
|
||||
run = p.add_run(chunk)
|
||||
run.font.size = BODY_SIZE
|
||||
run.bold = bold
|
||||
|
||||
|
||||
def _add_bullets(doc, items: list[str], *, indent: float = 0.25) -> None:
|
||||
for item in items:
|
||||
p = doc.add_paragraph(style="List Bullet")
|
||||
p.paragraph_format.left_indent = Inches(indent)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
p.clear()
|
||||
run = p.add_run(item)
|
||||
run.font.size = BODY_SIZE
|
||||
|
||||
|
||||
def _add_page_number_footer(doc) -> None:
|
||||
for section in doc.sections:
|
||||
footer = section.footer
|
||||
footer.is_linked_to_previous = False
|
||||
p = footer.paragraphs[0] if footer.paragraphs else footer.add_paragraph()
|
||||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
run = p.add_run()
|
||||
for field_char in ("begin",):
|
||||
fc = OxmlElement("w:fldChar")
|
||||
fc.set(qn("w:fldCharType"), field_char)
|
||||
run._element.append(fc)
|
||||
r2 = p.add_run()
|
||||
instr = OxmlElement("w:instrText")
|
||||
instr.set(qn("xml:space"), "preserve")
|
||||
instr.text = " PAGE "
|
||||
r2._element.append(instr)
|
||||
r3 = p.add_run()
|
||||
fc2 = OxmlElement("w:fldChar")
|
||||
fc2.set(qn("w:fldCharType"), "end")
|
||||
r3._element.append(fc2)
|
||||
|
||||
|
||||
# ── Main generator ───────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def generate_exhibit_a(
|
||||
# Identity
|
||||
entity_name: str,
|
||||
entity_abbr: str = "",
|
||||
frn: str = "",
|
||||
ocn: str = "",
|
||||
address: str = "",
|
||||
# Contact
|
||||
contact_name: str = "",
|
||||
contact_title: str = "",
|
||||
contact_email: str = "",
|
||||
contact_phone: str = "",
|
||||
# Principals / affiliates (list of names or short descriptions)
|
||||
principals: list[str] | None = None,
|
||||
affiliates: list[str] | None = None,
|
||||
# Classification
|
||||
carrier_role: str = "facilities",
|
||||
carrier_metadata: dict | None = None,
|
||||
upstream_provider_name: str = "",
|
||||
is_wholesale: bool = False,
|
||||
is_gateway: bool = False,
|
||||
foreign_traffic: bool = False,
|
||||
# RMD filing option — "option1", "option2", "option3"
|
||||
rmd_option: str = "option2",
|
||||
# Operational narrative
|
||||
scope_narrative: str = "",
|
||||
high_risk_alert_sla_hours: int = 4,
|
||||
# Analytics vendors / systems (optional)
|
||||
analytics_systems: list[str] | None = None,
|
||||
third_party_vendors: list[str] | None = None,
|
||||
# Signature
|
||||
signer_name: str = "",
|
||||
signer_title: str = "",
|
||||
# Legacy LLM hook kept for backwards compat — ignored (no-op)
|
||||
llm_generate: Callable[[str, str], Awaitable[str]] | None = None,
|
||||
# Output
|
||||
output_path: str = "/tmp/rmd_plan.docx",
|
||||
) -> Optional[str]:
|
||||
"""Generate the PW canonical Robocall Mitigation Plan as a DOCX file."""
|
||||
if Document is None:
|
||||
LOG.error("python-docx not installed")
|
||||
return None
|
||||
|
||||
entity_abbr = entity_abbr or _derive_abbr(entity_name)
|
||||
principals = principals or []
|
||||
affiliates = affiliates or []
|
||||
signer_name = signer_name or contact_name
|
||||
signer_title = signer_title or contact_title
|
||||
|
||||
doc = Document()
|
||||
for section in doc.sections:
|
||||
section.top_margin = Inches(1)
|
||||
section.bottom_margin = Inches(1)
|
||||
section.left_margin = Inches(1.25)
|
||||
section.right_margin = Inches(1.25)
|
||||
_add_page_number_footer(doc)
|
||||
|
||||
today = datetime.now().strftime("%B %d, %Y")
|
||||
|
||||
# Title + "Updated as of"
|
||||
title_p = doc.add_paragraph()
|
||||
title_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
title_run = title_p.add_run(f"Robocall Mitigation Plan for {entity_name}")
|
||||
title_run.font.size = Pt(14)
|
||||
title_run.bold = True
|
||||
title_run.font.color.rgb = NAVY_BLUE
|
||||
title_p.paragraph_format.space_after = Pt(2)
|
||||
|
||||
subtitle_p = doc.add_paragraph()
|
||||
subtitle_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
sub_run = subtitle_p.add_run(f"Updated as of {today}")
|
||||
sub_run.font.size = Pt(10)
|
||||
sub_run.italic = True
|
||||
subtitle_p.paragraph_format.space_after = Pt(12)
|
||||
|
||||
# ── Introduction ──
|
||||
_add_heading(doc, "Introduction")
|
||||
_add_body(doc, _scope_paragraph(
|
||||
entity_name=entity_name, entity_abbr=entity_abbr,
|
||||
carrier_role=carrier_role, scope_narrative=scope_narrative,
|
||||
is_wholesale=is_wholesale, is_gateway=is_gateway,
|
||||
foreign_traffic=foreign_traffic,
|
||||
))
|
||||
|
||||
# ── 1. Contact Information ──
|
||||
_add_heading(doc, "1. Contact Information")
|
||||
_add_body(doc, entity_name)
|
||||
if address:
|
||||
_add_body(doc, f"Address: {address}")
|
||||
if contact_name:
|
||||
title_suffix = f", {contact_title}" if contact_title else ""
|
||||
_add_body(doc, f"Primary Contact: {contact_name}{title_suffix}")
|
||||
if contact_email:
|
||||
_add_body(doc, f"Email Address: {contact_email}")
|
||||
if contact_phone:
|
||||
_add_body(doc, f"Phone: {contact_phone}")
|
||||
if frn:
|
||||
_add_body(doc, f"FRN: {frn}")
|
||||
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."
|
||||
))
|
||||
|
||||
_add_body(doc, "Principals, Affiliates, Subsidiaries, and Parent Companies:", bold=True)
|
||||
_add_bullets(doc, principals or [f"{contact_name or entity_name} (sole principal)"])
|
||||
if affiliates:
|
||||
_add_body(doc, "Affiliates:")
|
||||
_add_bullets(doc, affiliates)
|
||||
|
||||
_add_body(doc, (
|
||||
f"{entity_abbr} affirms that neither it nor any affiliated entity "
|
||||
f"has been subject to FCC or law enforcement action related to "
|
||||
f"illegal robocalling, spoofing, or RMD deficiencies in the past "
|
||||
f"two years."
|
||||
))
|
||||
|
||||
# ── 2. STIR/SHAKEN ──
|
||||
_add_heading(doc, "2. Implementation of STIR/SHAKEN Framework")
|
||||
stir_paras = _stir_shaken_paragraph(
|
||||
entity_name=entity_name, entity_abbr=entity_abbr,
|
||||
rmd_option=rmd_option,
|
||||
upstream_provider_name=upstream_provider_name,
|
||||
carrier_role=carrier_role,
|
||||
ocn=ocn,
|
||||
)
|
||||
for para in stir_paras:
|
||||
_add_body(doc, para)
|
||||
_add_body(doc, f"RMD filing option selected: {RMD_OPTION_LABELS.get(rmd_option.lower(), rmd_option)}", bold=True)
|
||||
|
||||
# ── 3. Robocall Monitoring and Mitigation ──
|
||||
_add_heading(doc, "3. Robocall Monitoring and Mitigation")
|
||||
_add_body(doc, (
|
||||
f"{entity_abbr} actively works to prevent illegal robocalls from "
|
||||
f"originating or transiting through its network. The program applies "
|
||||
f"to all voice traffic that {entity_abbr} originates, transits, or "
|
||||
f"terminates, and includes the following elements:"
|
||||
))
|
||||
_add_body(doc, "Traffic Monitoring:", bold=True)
|
||||
_add_bullets(doc, [
|
||||
"Monitoring call patterns for anomalies \u2014 high call volumes to specific destinations, short-duration calls, ASR/ACD deviations, call velocity, and snowshoeing.",
|
||||
f"Receiving and reviewing all high-risk alerts within {high_risk_alert_sla_hours} hours and taking immediate action.",
|
||||
"Investigating and addressing suspicious activity promptly.",
|
||||
])
|
||||
_add_body(doc, "Customer Vetting:", bold=True)
|
||||
_add_bullets(doc, [
|
||||
"Verifying the identity of new customers and assessing the legitimacy of their intended usage.",
|
||||
"Ensuring customers agree to terms prohibiting illegal robocalling.",
|
||||
"Immediately blocking any numbers identified on the FCC Do-Not-Originate (DNO) list via upstream enforcement.",
|
||||
"Taking reasonable steps to prevent new and renewing customers from originating illegal robocalls.",
|
||||
])
|
||||
_add_body(doc, "Complaint Resolution:", bold=True)
|
||||
_add_bullets(doc, [
|
||||
"Providing a clear process for individuals to report suspected robocalls (including email to the support address above).",
|
||||
"Investigating complaints and taking corrective actions, including termination of services for violators.",
|
||||
])
|
||||
|
||||
# ── 3.5 KYC (In-House) ──
|
||||
_add_heading(doc, "3.5. Know Your Customer (KYC) Procedures (Performed In-House)", level=2)
|
||||
_add_body(doc, (
|
||||
f"{entity_name} conducts its own internal Know Your Customer (KYC) "
|
||||
f"process for all new customers and customer renewals to ensure "
|
||||
f"that only legitimate entities and individuals are granted access "
|
||||
f"to services capable of making outbound calls or using numbering "
|
||||
f"resources."
|
||||
))
|
||||
_add_body(doc, "Collection and Verification of Customer Information. At account signup or upon any material change in service usage, we require and collect:", bold=True)
|
||||
_add_bullets(doc, [
|
||||
"Full legal name of the individual or entity",
|
||||
"Physical business address (no P.O. boxes accepted for high-volume or toll-free services)",
|
||||
"Business identification number (EIN, or equivalent tax ID for non-U.S. entities) or, for individuals, the last four digits of SSN or government-issued ID number",
|
||||
"At least one valid government-issued photo ID for the account owner or authorized officer",
|
||||
"Business website, or if none exists, a description of the legitimate business purpose for the service",
|
||||
])
|
||||
_add_body(doc, "Verification Steps Performed In-House. Staff manually verify the provided information by:", bold=True)
|
||||
_add_bullets(doc, [
|
||||
"Cross-referencing business name and EIN against public state business registry databases or IRS records where available",
|
||||
"Confirming the provided physical address via USPS address validation tools and third-party data sources (e.g., Google Maps satellite/street view confirmation)",
|
||||
"Verifying that the submitted photo ID matches the name and appears authentic",
|
||||
"Conducting an open-source and web search for the customer and principals to identify any prior association with illegal robocalling, call spoofing, or inclusion on the Industry Traceback Group's known bad-actor list",
|
||||
])
|
||||
_add_body(doc, "Red-Flag Review and Enhanced Due Diligence. If any of the following risk indicators are present, we perform enhanced in-house due diligence before activating or continuing service:", bold=True)
|
||||
_add_bullets(doc, [
|
||||
"Customer is unwilling or unable to provide complete KYC information",
|
||||
"Discrepancies between provided information and public records",
|
||||
"Use of privacy-protected or anonymous registration services for domains/websites",
|
||||
"Requested usage patterns inconsistent with stated business purpose",
|
||||
"Prior complaints or traceback involvement linked to the customer or its principals",
|
||||
])
|
||||
_add_body(doc, (
|
||||
"Acceptance of Terms Prohibiting Illegal Activity. All customers "
|
||||
"must electronically acknowledge and agree to our Acceptable Use "
|
||||
"Policy and Robocall Policy, which explicitly prohibit the "
|
||||
"origination or facilitation of illegal robocalls, unlawful caller "
|
||||
"ID spoofing, or any violation of the Telephone Consumer Protection "
|
||||
"Act (TCPA) or Telemarketing Sales Rule (TSR)."
|
||||
))
|
||||
_add_body(doc, (
|
||||
"Ongoing Monitoring and Re-Vetting. Existing customers are subject "
|
||||
"to periodic re-vetting (at least annually for high-volume or "
|
||||
"toll-free customers) and immediate re-review upon receipt of "
|
||||
"complaints, traceback requests, or detected anomalous traffic "
|
||||
"patterns."
|
||||
))
|
||||
|
||||
# ── 4. Call Analytics and Upstream Provider Procedures ──
|
||||
_add_heading(doc, "4. Call Analytics and Upstream Provider Procedures")
|
||||
_add_body(doc, "Call Analytics:", bold=True)
|
||||
if analytics_systems or third_party_vendors:
|
||||
items: list[str] = []
|
||||
if analytics_systems:
|
||||
items.append("Analytics systems deployed: " + ", ".join(analytics_systems))
|
||||
if third_party_vendors:
|
||||
items.append("Third-party analytics vendors: " + ", ".join(third_party_vendors))
|
||||
items.append("Systems analyze call patterns in real time to identify potential violations.")
|
||||
_add_bullets(doc, items)
|
||||
else:
|
||||
_add_bullets(doc, [
|
||||
f"{entity_abbr} utilizes the underlying carriers' real-time call analytics platforms, which monitor ASR, ACD, call velocity, short-duration patterns, and snowshoeing.",
|
||||
f"{entity_abbr} receives and reviews all high-risk alerts within {high_risk_alert_sla_hours} hours and takes immediate action.",
|
||||
"As a small provider without its own switching platform, independent analytics are unnecessary and disproportionate to risk.",
|
||||
])
|
||||
_add_body(doc, "Upstream Provider Procedures:", bold=True)
|
||||
_add_bullets(doc, [
|
||||
"Prior to contracting and annually thereafter, we verify each upstream provider's active RMD filing, STIR/SHAKEN status, and robocall mitigation plan via the RMD portal before routing any traffic.",
|
||||
"Periodic reviews confirm ongoing compliance.",
|
||||
])
|
||||
|
||||
# ── 5. Compliance with FCC Requirements ──
|
||||
_add_heading(doc, "5. Compliance with FCC Requirements")
|
||||
_add_body(doc, f"{entity_abbr} fully complies with FCC robocall mitigation rules:")
|
||||
_add_bullets(doc, [
|
||||
"Maintains an active filing in the FCC's Robocall Mitigation Database (RMD), including role (voice service provider serving end-users), STIR/SHAKEN status, and mitigation plan.",
|
||||
"Annual Recertification: Recertifies RMD filing annually by March 1 each year (for 2026, by March 2 because March 1 is a Sunday; window opened February 1, 2026). Recertification involves logging into the RMD portal with multi-factor authentication (required effective February 5, 2026), verifying accuracy of all information, updating if needed, and submitting via the \"Recertify\" button.",
|
||||
"Prompt Updates: Updates RMD and CORES registration within 10 business days of any material change (e.g., ownership, contacts, STIR/SHAKEN posture, upstream provider, trade names), per 47 CFR \u00a7 64.6305.",
|
||||
"Responds fully to Industry Traceback Group (ITG) traceback requests within 24 hours.",
|
||||
"Pays any required filing fees to the FCC.",
|
||||
"Responds promptly to FCC deficiency notices, curing issues within specified timeframes to avoid RMD removal or traffic blocking.",
|
||||
])
|
||||
_add_body(doc, (
|
||||
"Noncompliance risks under the 2025 RMD R&O include a base forfeiture "
|
||||
"of $10,000 for false or inaccurate information and $1,000 per day "
|
||||
"until cured for failure to update RMD information within 10 "
|
||||
"business days of a material change."
|
||||
))
|
||||
|
||||
# ── 6. Future Enhancements ──
|
||||
_add_heading(doc, "6. Future Enhancements")
|
||||
_add_body(doc, f"{entity_abbr} commits to ongoing improvement:")
|
||||
_add_bullets(doc, [
|
||||
"Monitoring upstream carrier enhancements for better analytics/blocking.",
|
||||
"Educating customers on robocall awareness and reporting.",
|
||||
"Adapting to emerging threats (e.g., AI voice cloning) via upstream partnerships and FCC guidance.",
|
||||
"Strengthening partnerships with industry organizations and regulators to stay ahead of emerging robocall trends.",
|
||||
])
|
||||
|
||||
# ── 7. Commitment to Correct Deficiencies ──
|
||||
_add_heading(doc, "7. Commitment to Correct Deficiencies")
|
||||
_add_body(doc, (
|
||||
f"{entity_abbr} will respond promptly to any FCC notice of deficiency "
|
||||
f"in its RMD certification. This includes:"
|
||||
))
|
||||
_add_bullets(doc, [
|
||||
"Updating RMD certifications and robocall mitigation plans to cure identified deficiencies.",
|
||||
"Providing detailed explanations to the FCC regarding corrective actions taken.",
|
||||
"Ensuring compliance within the specified timeframe to avoid removal from the RMD.",
|
||||
])
|
||||
|
||||
# ── Conclusion ──
|
||||
_add_heading(doc, "Conclusion")
|
||||
_add_body(doc, (
|
||||
f"{entity_name} is dedicated to protecting its customers and the "
|
||||
f"public from the harm caused by illegal robocalls. Through "
|
||||
f"reliable upstream partnerships, customer-focused controls, and "
|
||||
f"full adherence to updated FCC requirements (including 2026 annual "
|
||||
f"recertification by March 2, 2026), {entity_abbr} provides secure, "
|
||||
f"compliant voice services."
|
||||
))
|
||||
|
||||
# ── Perjury declaration + signature ──
|
||||
_add_heading(doc, " ", level=2) # spacer
|
||||
_add_body(doc, (
|
||||
"I declare under penalty of perjury under the laws of the United "
|
||||
"States of America that to the best of my knowledge the foregoing "
|
||||
"is true and correct."
|
||||
))
|
||||
_add_body(doc, "")
|
||||
sig = doc.add_paragraph()
|
||||
sig.add_run("_" * 45).font.size = BODY_SIZE
|
||||
_add_body(doc, signer_name or "[Authorized Signer]", bold=True)
|
||||
_add_body(doc, signer_title or "[Title]")
|
||||
_add_body(doc, entity_name)
|
||||
_add_body(doc, f"Date: {today}")
|
||||
|
||||
# Save
|
||||
output = Path(output_path)
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
doc.save(str(output))
|
||||
LOG.info("RMD Plan (Exhibit A) generated: %s", output)
|
||||
return str(output)
|
||||
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _derive_abbr(entity_name: str) -> str:
|
||||
"""Build a 2-4 letter abbreviation from the legal name."""
|
||||
words = [w for w in entity_name.replace(",", "").split() if w.lower() not in (
|
||||
"inc", "inc.", "llc", "llc.", "corporation", "corp", "corp.", "co", "co.",
|
||||
"ltd", "ltd.", "company", "the", "a", "of",
|
||||
)]
|
||||
if not words:
|
||||
return entity_name[:3].upper()
|
||||
letters = "".join(w[0].upper() for w in words[:3])
|
||||
return letters if len(letters) >= 2 else entity_name[:3].upper()
|
||||
Loading…
Add table
Add a link
Reference in a new issue