new-site/scripts/document_gen/templates/cpni_cert_letter_generator.py
justin f8cd37ac8c 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>
2026-04-27 06:54:22 -05:00

588 lines
27 KiB
Python

"""
Generate the FCC CPNI Annual Certification Letter.
Produces the annual certification required by 47 CFR § 64.2009 certifying
compliance with the Customer Proprietary Network Information (CPNI) rules
(47 CFR §§ 64.2001-64.2011), including amendments from the 2023 Data Breach
Notification Order (FCC 23-111).
The letter is largely standard across carrier types. The only variation
is wholesale-only carriers, whose CPNI obligations are limited to wholesale
customer proprietary data rather than retail end-user CPNI.
Usage:
from scripts.document_gen.templates.cpni_cert_letter_generator import (
generate_cpni_cert_letter,
)
path = generate_cpni_cert_letter(
entity_name="Falcon Broadband LLC",
frn="0027160886",
filer_id_499="812345",
reporting_year=2025,
complaints_count=0,
output_path="/tmp/cpni_cert.docx",
)
"""
from __future__ import annotations
import logging
from datetime import datetime
from pathlib import Path
from typing import Optional
LOG = logging.getLogger("document_gen.cpni_cert")
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
except ImportError:
LOG.warning("python-docx not installed — CPNI cert letter generation unavailable")
Document = None # type: ignore[assignment,misc]
# Navy blue used for section headings (RGB 0x1A, 0x27, 0x44)
_NAVY = RGBColor(0x1A, 0x27, 0x44) if Document else None
# Spacing constants (in twips; 1 pt = 20 twips)
_AFTER_6PT = Pt(6) if Document else None
def generate_cpni_cert_letter(
# ── Entity identity ───────────────────────────────────────────
entity_name: str,
frn: str = "",
filer_id_499: str = "",
# ── Address ───────────────────────────────────────────────────
address_street: str = "",
address_city: str = "",
address_state: str = "",
address_zip: str = "",
# ── Contact / officer ─────────────────────────────────────────
officer_name: str = "",
officer_title: str = "Chief Executive Officer",
contact_email: str = "",
contact_phone: str = "",
# ── Reporting ─────────────────────────────────────────────────
reporting_year: int = 0,
complaints_count: int = 0,
complaints_description: str = "",
# ── Carrier flags ─────────────────────────────────────────────
is_wholesale: bool = False,
# ── Employee training ─────────────────────────────────────────
employee_training_conducted: bool = True,
# ── Disciplinary actions ──────────────────────────────────────
disciplinary_actions_taken: bool = False,
disciplinary_actions_description: str = "",
# ── Data broker actions ───────────────────────────────────────
data_broker_actions: str = "",
# ── Breaches (per FCC 23-111) ─────────────────────────────────
breaches: list[dict] | None = None,
# ── Marketing / CPNI usage ────────────────────────────────────
uses_cpni_for_marketing: bool = False,
cpni_approval_method: str = "opt_in", # "opt_in" or "opt_out"
# ── Pretexting safeguards ─────────────────────────────────────
pretexting_safeguards: str = "",
# ── Output ────────────────────────────────────────────────────
output_path: str = "/tmp/cpni_certification_letter.docx",
) -> Optional[str]:
"""
Generate a CPNI Annual Certification Letter as a DOCX file.
Compliant with 47 CFR § 64.2009, including the 2023 Data Breach
Notification Order (FCC 23-111).
Returns the output file path on success, None on failure.
"""
if Document is None:
LOG.error("python-docx not installed")
return None
if reporting_year == 0:
reporting_year = datetime.now().year - 1
if breaches is None:
breaches = []
doc = Document()
# ── Page setup ────────────────────────────────────────────────
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)
today = datetime.now().strftime("%B %d, %Y")
signer = officer_name or "Authorized Officer"
title = officer_title or "Officer"
cpni_scope = (
"wholesale customer proprietary data"
if is_wholesale
else "customer proprietary network information (CPNI)"
)
# ── Helper functions ──────────────────────────────────────────
def _set_spacing(paragraph, after_pt=6, before_pt=0):
"""Set paragraph spacing in points."""
pf = paragraph.paragraph_format
pf.space_after = Pt(after_pt)
if before_pt:
pf.space_before = Pt(before_pt)
def _heading(text: str, level: int = 1) -> None:
"""Add a navy blue section heading."""
p = doc.add_paragraph()
run = p.add_run(text)
run.font.size = Pt(12)
run.bold = True
run.font.color.rgb = _NAVY
_set_spacing(p, after_pt=4, before_pt=8)
def _body(text: str, bold: bool = False, size: int = 10) -> None:
"""Add body-text paragraph with 6pt spacing after."""
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
run = p.add_run(text)
run.font.size = Pt(size)
run.bold = bold
_set_spacing(p, after_pt=6)
def _checkbox(label: str, checked: bool = True) -> None:
"""Add a checkbox-style line item."""
mark = "\u2611" if checked else "\u2610"
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
run = p.add_run(f" {mark} {label}")
run.font.size = Pt(10)
_set_spacing(p, after_pt=3)
def _spacer() -> None:
p = doc.add_paragraph()
_set_spacing(p, after_pt=0)
# ── Page numbers ──────────────────────────────────────────────
for section in doc.sections:
footer = section.footer
footer.is_linked_to_previous = False
fp = footer.paragraphs[0] if footer.paragraphs else footer.add_paragraph()
fp.alignment = WD_ALIGN_PARAGRAPH.CENTER
# Insert PAGE field
run = fp.add_run()
run.font.size = Pt(8)
run.font.color.rgb = RGBColor(0x80, 0x80, 0x80)
fld_char_begin = run._element.makeelement(qn("w:fldChar"), {qn("w:fldCharType"): "begin"})
run._element.append(fld_char_begin)
run2 = fp.add_run()
run2.font.size = Pt(8)
run2.font.color.rgb = RGBColor(0x80, 0x80, 0x80)
instr = run2._element.makeelement(qn("w:instrText"), {})
instr.text = " PAGE "
run2._element.append(instr)
run3 = fp.add_run()
run3.font.size = Pt(8)
fld_char_end = run3._element.makeelement(qn("w:fldChar"), {qn("w:fldCharType"): "end"})
run3._element.append(fld_char_end)
# ══════════════════════════════════════════════════════════════
# TITLE
# ══════════════════════════════════════════════════════════════
title_p = doc.add_paragraph()
title_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
title_run = title_p.add_run("CPNI Annual Certification Letter")
title_run.font.size = Pt(14)
title_run.bold = True
title_run.font.color.rgb = _NAVY
_set_spacing(title_p, after_pt=2)
subtitle_p = doc.add_paragraph()
subtitle_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
sub_run = subtitle_p.add_run(
f"Pursuant to 47 CFR \u00a7 64.2009 \u2014 Calendar Year {reporting_year}"
)
sub_run.font.size = Pt(10)
sub_run.font.color.rgb = RGBColor(0x55, 0x55, 0x55)
_set_spacing(subtitle_p, after_pt=6)
# Horizontal rule
rule_p = doc.add_paragraph()
rule_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
rule_run = rule_p.add_run("\u2500" * 72)
rule_run.font.size = Pt(6)
rule_run.font.color.rgb = RGBColor(0xAA, 0xAA, 0xAA)
_set_spacing(rule_p, after_pt=8)
# ══════════════════════════════════════════════════════════════
# SECTION 1: Provider Information
# ══════════════════════════════════════════════════════════════
_heading("1. Provider Information")
info_lines = [f"Company Name: {entity_name}"]
if frn:
info_lines.append(f"FCC Registration Number (FRN): {frn}")
if filer_id_499:
info_lines.append(f"FCC Form 499 Filer ID: {filer_id_499}")
addr = ", ".join(filter(None, [address_street, address_city]))
if address_state or address_zip:
addr += f", {address_state} {address_zip}".strip()
if addr.strip(", "):
info_lines.append(f"Address: {addr.strip(', ')}")
if contact_phone:
info_lines.append(f"Telephone: {contact_phone}")
if contact_email:
info_lines.append(f"Email: {contact_email}")
info_lines.append(f"Certifying Officer: {signer}, {title}")
info_lines.append(f"Date of Filing: {today}")
info_lines.append(
f"Filing Deadline: March 1, {reporting_year + 1}"
)
_body("\n".join(info_lines))
# ══════════════════════════════════════════════════════════════
# SECTION 2: Certification of Compliance
# ══════════════════════════════════════════════════════════════
_heading("2. Certification of Compliance")
_body(
f"Pursuant to 47 CFR \u00a7 64.2009(e), {entity_name} "
f"({'FRN: ' + frn if frn else 'FRN pending'}"
f"{', Filer ID: ' + filer_id_499 if filer_id_499 else ''}) "
f"hereby submits its annual certification of compliance with the "
f"Commission's Customer Proprietary Network Information (CPNI) rules "
f"for calendar year {reporting_year}."
)
_body(
f"I, {signer}, {title} of {entity_name}, have personal knowledge "
f"of, have reviewed, and am familiar with {entity_name}'s CPNI "
f"compliance procedures and certify that the company has established "
f"operating procedures that ensure compliance with the Commission's "
f"CPNI rules set forth in 47 CFR \u00a7\u00a7 64.2001 through 64.2011. "
f"{entity_name} has taken appropriate actions to protect the "
f"confidentiality of {cpni_scope} and has limited access to and use "
f"of such information in accordance with the Commission's rules."
)
# ══════════════════════════════════════════════════════════════
# SECTION 3: Reporting Period
# ══════════════════════════════════════════════════════════════
_heading("3. Reporting Period")
_body(
f"This certification covers the period from January 1, {reporting_year} "
f"through December 31, {reporting_year}."
)
# ══════════════════════════════════════════════════════════════
# SECTION 4: CPNI Safeguards
# ══════════════════════════════════════════════════════════════
_heading("4. CPNI Safeguards")
_body(
f"{entity_name} has implemented the following safeguards to protect "
f"{cpni_scope}:"
)
# 4a - Customer authentication
_body("(a) Customer Authentication and Password Procedures", bold=True)
_checkbox(
f"{entity_name} requires customer authentication through a password "
f"or other secure credential before disclosing CPNI in response to "
f"customer-initiated contacts, in accordance with 47 CFR \u00a7 64.2010.",
checked=True,
)
# 4b - Employee training
_body("(b) Employee Training", bold=True)
_checkbox(
f"All employees with access to CPNI have been adequately trained on "
f"the Commission's CPNI rules, including proper handling, disclosure "
f"limitations, and breach notification procedures.",
checked=employee_training_conducted,
)
if not employee_training_conducted:
_body(
f"NOTE: {entity_name} is in the process of completing employee "
f"training and anticipates full compliance within 30 days of this "
f"filing."
)
# 4c - Supervisory review
_body("(c) Supervisory Review", bold=True)
_checkbox(
f"{entity_name} conducts regular supervisory reviews of CPNI access "
f"and usage to ensure compliance with established procedures.",
checked=True,
)
# 4d - Pretexting safeguards
_body("(d) Pretexting Safeguards", bold=True)
if pretexting_safeguards:
_checkbox(pretexting_safeguards, checked=True)
else:
_checkbox(
f"{entity_name} has implemented safeguards to protect against "
f"pretexting, including customer identity verification protocols, "
f"employee awareness training on social engineering tactics, and "
f"procedures to detect and report suspected pretexting attempts.",
checked=True,
)
# 4e - Notification of account changes
_body("(e) Notification of Account Changes", bold=True)
_checkbox(
f"{entity_name} notifies customers of account changes, including "
f"changes to passwords, address of record, or online account "
f"credentials, through a communication to the customer's address "
f"of record or established backup contact method, in accordance "
f"with 47 CFR \u00a7 64.2010.",
checked=True,
)
# 4f - Record retention
_body("(f) Record Retention", bold=True)
_checkbox(
f"{entity_name} maintains records of all CPNI access, disclosures, "
f"customer complaints, and compliance actions for a minimum period "
f"of five (5) years, as required by 47 CFR \u00a7 64.2009(e).",
checked=True,
)
# ══════════════════════════════════════════════════════════════
# SECTION 5: CPNI Complaints
# ══════════════════════════════════════════════════════════════
_heading("5. CPNI Complaints")
if complaints_count == 0:
_body(
f"During the reporting period, {entity_name} received no complaints "
f"regarding unauthorized release or use of CPNI."
)
else:
desc = complaints_description or (
f"Each complaint was investigated and resolved in accordance with "
f"{entity_name}'s CPNI compliance procedures."
)
_body(
f"During the reporting period, {entity_name} received "
f"{complaints_count} complaint{'s' if complaints_count != 1 else ''} "
f"regarding CPNI. {desc}"
)
# ══════════════════════════════════════════════════════════════
# SECTION 6: Data Breaches
# ══════════════════════════════════════════════════════════════
_heading("6. Data Breaches")
if not breaches:
_body(
f"During the reporting period, {entity_name} experienced no data "
f"breaches involving CPNI. No breach notifications were required "
f"to be filed with the Commission, law enforcement, or affected "
f"customers under 47 CFR \u00a7 64.2011."
)
else:
total_breaches = len(breaches)
total_affected = sum(b.get("customers_affected", 0) for b in breaches)
_body(
f"During the reporting period, {entity_name} experienced "
f"{total_breaches} data breach{'es' if total_breaches != 1 else ''} "
f"involving CPNI, affecting a total of {total_affected:,} "
f"customer{'s' if total_affected != 1 else ''}. Details of each "
f"breach are provided below."
)
# Breach detail table
table = doc.add_table(rows=1, cols=5)
table.style = "Table Grid"
# Header row
headers = [
"Breach #", "Date", "Customers\nAffected",
"Description", "Response Actions",
]
hdr_cells = table.rows[0].cells
for i, header in enumerate(headers):
hdr_cells[i].text = ""
p = hdr_cells[i].paragraphs[0]
run = p.add_run(header)
run.bold = True
run.font.size = Pt(9)
run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
# Navy background
shading = hdr_cells[i]._element.makeelement(
qn("w:shd"),
{
qn("w:val"): "clear",
qn("w:color"): "auto",
qn("w:fill"): "1A2744",
},
)
tc_pr = hdr_cells[i]._element.get_or_add_tcPr()
tc_pr.append(shading)
# Data rows
for idx, breach in enumerate(breaches, start=1):
row_cells = table.add_row().cells
values = [
str(idx),
str(breach.get("date", "N/A")),
f"{breach.get('customers_affected', 0):,}",
str(breach.get("description", "")),
str(breach.get("response_actions", "")),
]
for i, val in enumerate(values):
row_cells[i].text = ""
p = row_cells[i].paragraphs[0]
run = p.add_run(val)
run.font.size = Pt(9)
_spacer()
# ══════════════════════════════════════════════════════════════
# SECTION 7: Disciplinary Actions
# ══════════════════════════════════════════════════════════════
_heading("7. Disciplinary Actions")
if not disciplinary_actions_taken:
_body(
f"During the reporting period, {entity_name} did not take any "
f"disciplinary action against employees for violations of the "
f"Commission's CPNI rules."
)
else:
desc = disciplinary_actions_description or (
"Disciplinary action was taken in accordance with company policy."
)
_body(
f"During the reporting period, {entity_name} took disciplinary "
f"action against one or more employees for violations of the "
f"Commission's CPNI rules. {desc}"
)
# ══════════════════════════════════════════════════════════════
# SECTION 8: Data Broker Actions
# ══════════════════════════════════════════════════════════════
_heading("8. Actions Taken Against Data Brokers")
if data_broker_actions:
_body(
f"During the reporting period, {entity_name} took the following "
f"actions against data brokers: {data_broker_actions}"
)
else:
_body(
f"During the reporting period, {entity_name} did not identify any "
f"data brokers engaging in unauthorized access to or sale of CPNI, "
f"and no actions against data brokers were required."
)
# ══════════════════════════════════════════════════════════════
# SECTION 9: CPNI Marketing Usage
# ══════════════════════════════════════════════════════════════
_heading("9. CPNI Marketing Usage")
if uses_cpni_for_marketing:
method_label = (
"opt-in" if cpni_approval_method == "opt_in" else "opt-out"
)
_body(
f"{entity_name} uses CPNI for marketing purposes. Customer "
f"approval for such use is obtained through the {method_label} "
f"method, in accordance with 47 CFR \u00a7 64.2007."
)
else:
_body(
f"{entity_name} does not use CPNI for marketing purposes beyond "
f"the scope of services to which the customer already subscribes. "
f"No customer approval mechanism is required."
)
# ══════════════════════════════════════════════════════════════
# SECTION 10: Breach Notification Compliance
# ══════════════════════════════════════════════════════════════
_heading("10. Breach Notification Compliance")
_body(
f"{entity_name} certifies that its breach notification procedures "
f"are compliant with 47 CFR \u00a7 64.2011, as amended by the 2023 "
f"Data Breach Notification Order (FCC 23-111). These procedures "
f"include:"
)
_checkbox(
"Notification to the FCC and, where applicable, the FBI and U.S. "
"Secret Service, as soon as practicable and in no event later than "
"30 days after reasonable determination of a breach.",
checked=True,
)
_checkbox(
"Notification to affected customers as soon as practicable and in "
"no event later than 30 days after notification to law enforcement "
"(unless a delay is requested by law enforcement).",
checked=True,
)
_checkbox(
"Breach notifications include the required content specified in "
"\u00a7 64.2011, including a description of the breach, the categories "
"of information compromised, and contact information for inquiries.",
checked=True,
)
# ══════════════════════════════════════════════════════════════
# SECTION 11: Officer Certification & Signature
# ══════════════════════════════════════════════════════════════
_heading("11. Officer Certification and Signature")
_body(
f"I, {signer}, {title} of {entity_name}, certify under penalty of "
f"perjury that the foregoing is true and correct. I have personal "
f"knowledge of the facts stated herein, have reviewed {entity_name}'s "
f"CPNI compliance procedures, and am satisfied that {entity_name} has "
f"complied with the requirements of 47 CFR \u00a7\u00a7 64.2001 through "
f"64.2011 during calendar year {reporting_year}."
)
_spacer()
_body("Respectfully submitted,")
_spacer()
_spacer()
# Signature line
sig_line = doc.add_paragraph()
sig_run = sig_line.add_run("_" * 45)
sig_run.font.size = Pt(10)
_set_spacing(sig_line, after_pt=2)
sig_name_p = doc.add_paragraph()
name_run = sig_name_p.add_run(signer)
name_run.font.size = Pt(10)
name_run.bold = True
_set_spacing(sig_name_p, after_pt=2)
sig_title_p = doc.add_paragraph()
sig_title_p.add_run(f"{title}, {entity_name}").font.size = Pt(10)
_set_spacing(sig_title_p, after_pt=2)
sig_date_p = doc.add_paragraph()
sig_date_p.add_run(f"Date: {today}").font.size = Pt(10)
_set_spacing(sig_date_p, after_pt=2)
if contact_phone:
sig_phone_p = doc.add_paragraph()
sig_phone_p.add_run(f"Telephone: {contact_phone}").font.size = Pt(10)
_set_spacing(sig_phone_p, after_pt=2)
if contact_email:
sig_email_p = doc.add_paragraph()
sig_email_p.add_run(f"Email: {contact_email}").font.size = Pt(10)
_set_spacing(sig_email_p, after_pt=2)
# ── Save ──────────────────────────────────────────────────────
output = Path(output_path)
output.parent.mkdir(parents=True, exist_ok=True)
doc.save(str(output))
LOG.info("CPNI certification letter generated: %s", output)
return str(output)