```html

Automating Maritime Burial Compliance: Building a §7117 Permit Filing System

When you operate a boat charter business that occasionally becomes the final resting place for cremated remains—a legitimate and legal maritime service—you inherit a regulatory burden: California Health & Safety Code §7117 requires a verified statement (penalty of perjury, signed, notarized intent) filed with the county registrar within 10 days of the scattering. For a business that does this a few times a year, each deadline is a separate project. This post documents how we built a deterministic, reusable Python system to generate compliance-ready filings and integrated it into our charter operations workflow.

The Regulatory Requirement

§7117 is straightforward but unforgiving:

  • Decedent name, date and place of death
  • Exact time and location of scattering (latitude/longitude, or "off Point Loma")
  • A verified statement (statutory language, under penalty of perjury)
  • Filed with the county registrar of the county nearest the scatter point
  • Deadline: D+10 calendar days

For context, a recent charter (Shumway, Jun 27, 10:30–13:30, Pearl Tan scattered off Point Loma) meant the filing deadline was Tue Jul 7. Because our crew dispatch system (DynamoDB table jada-crew-dispatch) records the charter details (passengers, crew, times, GPS data) and our proposal system holds decedent information, we had all the raw data—but it lived in three disconnected systems. The compliance gap: manual copy-paste into a Word template, then manual filing. This scales poorly and invites transcription errors.

System Design

We built a three-stage pipeline:

Stage 1: Charter Data → JSON Filing Struct

The source of truth is a row in jada-crew-dispatch (DynamoDB). At charter completion, the dispatcher marks it done; a secondary process extracts the charter record and merges it with decedent details from our proposals database:

{
  "charter_id": "shumway-2026-06-27",
  "decedent_name": "Pearl Tan",
  "date_of_death": "2026-06-23",
  "place_of_death": "San Diego, CA",
  "scatter_location": "off Point Loma, San Diego County",
  "scatter_date": "2026-06-27",
  "scatter_time_start": "10:30",
  "scatter_time_end": "13:30",
  "vessel_name": "Shumway",
  "captain_name": "Darrell [surname]",
  "filer_name": "[your name]",
  "filer_address": "[your mailing address]"
}

Decedent details (date/place of death) live outside our system—typically held by the family or arrangement service. We added a template in /Users/cb/icloud-jada-ops/compliance/CONTEXT.md and CLAUDE.md to prompt for these at filing time.

Stage 2: JSON → Jinja2-Templated PDF

The core script, /Users/cb/icloud-jada-ops/compliance/fill_7117.py, uses Jinja2 to render a statutory template as a PDF:

from jinja2 import Template
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter

template_str = """
VERIFIED STATEMENT — BURIAL AT SEA (CALIFORNIA HEALTH & SAFETY CODE §7117)

I, {{ filer_name }}, declare under penalty of perjury that:

1. {{ decedent_name }}, died on {{ date_of_death }} at {{ place_of_death }}.
2. On {{ scatter_date }} between {{ scatter_time_start }} and {{ scatter_time_end }},
   I released the decedent's cremated remains into the Pacific Ocean at 
   {{ scatter_location }}, from the vessel {{ vessel_name }}, 
   with Captain {{ captain_name }} commanding.
3. This statement is true and correct.

I declare under penalty of perjury that the foregoing is true and correct.

_________________________
{{ filer_name }}
Date: {{ filing_date }}
"""

filing_data = load_json("filings/7117-2026-06-27-pearl-tan.json")
template = Template(template_str)
pdf_text = template.render(**filing_data)
generate_pdf(pdf_text, output_path="filings/7117-2026-06-27-pearl-tan.pdf")

The output is a printable, signature-ready PDF. We inject yellow [TO-CONFIRM] blocks for any missing fields so nothing slips through unsigned. The filing lives at /Users/cb/icloud-jada-ops/compliance/filings/, keyed by 7117-YYYY-MM-DD-decedent-lastname.pdf.

Stage 3: SMS Reminder + Deadline Tracking

The captain who ran the charter is the source of truth for scatter location (GPS, time, conditions). We SMS the captain immediately after filing creation asking for position confirmation:

import boto3

sms_client = boto3.client('sns', region_name='us-west-2')
message = (
    "Pearl Tan scatter filing due Tue Jul 7. "
    "Need exact position off Point Loma (GPS or description). "
    "Reply with location; file will be ready to sign/mail Monday."
)
sms_client.publish(PhoneNumber=captain_phone, Message=message)

We maintain a deadline registry in CONTEXT.md:

  • §7117 filing deadline: D+10 (within 10 days of scattering)
  • EPA online burial-at-sea report: D+30 (separate federal requirement, same decedent data)

For Pearl Tan: Jun 27 scatter → §7117 due Tue Jul 7, EPA due ~Jul 27.

Key Decisions

Why Not Full Automation (Lob Mail API)?

We evaluated Lob, a REST API that converts PDFs to USPS Certified Mail. It works perfectly for follow-up filings, but not for this one, for two reasons:

  1. Timing. Today is Jul 3 (Thursday). County offices and USPS close Jul 4–5 (Fri–Sat). Anything mailed today doesn't enter the system until Monday Jul 6; Monday mail arrives after Tue Jul 7 deadline. The only bulletproof move is hand-delivery to 5530 Overland Ave, San Diego, on Mon Jul 6 morning (15 minutes from your office).
  2. Signature. It's a penalty-of-perjury declaration. A human signs it. Mail automation can generate the PDF but not sign it.

For future filings (e.g., Nappi scattering Jul 18), mail will have enough margin, and we'll wire Lob automation into the post-signature workflow.

Why Jinja2 + ReportLab Instead of a PDF Form?

Adobe PDF forms with fillable fields work great, but they're fragile to schema changes and hard to version-control. Jinja2 templates are text (live in git), render deterministically, and survive refactors. ReportLab gives us fine-grained layout control and embed-friendly fonts. Most importantly: the template IS the compliance documentation—anyone reading the template sees exactly what will be filed, no hidden form state.

Decedent Data Outside Our System

We don't store date/place of death in our charter database. This data is sensitive (families often don't share it widely), typically held by arrangement services, and only needed at filing time. We prompt for it once, validate it's complete, then freeze it in the filing JSON. This prevents accidental leakage of decedent details in automated charters/emails.

Infrastructure and Integration

  • Data source: DynamoDB table jada-crew-dispatch (charter records), with captain contact info and vessel names.
  • SMS transport: AWS SNS (boto3 client), targeting captain phone numbers from crew registry.
  • Storage: iCloud Drive sync to /Users/cb/icloud-jada-ops/compliance/filings/ (backed by CloudKit, encrypted in transit and at rest).
  • Process routing: Documented in CONTEXT.md and CLAUDE.md, keyed to charter status changes in the dispatch table.
  • Future mail automation: Lob REST API (post-signature, post-deadline margin); will require a Lob account setup (~10 minutes) and credential storage in AWS Secrets Manager.

What's Next

For Pearl Tan: You print, sign, and hand-deliver Monday morning. For Nappi (scheduled Jul 18): full pipeline, including Lob mail automation after you sign. Once Lob is wired, the human touch is just signing a PDF—everything else runs in the dispatch workflow. The EPA online burial report (D+30) will follow the same data shape, reusing the filing JSON.

```