""" 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()