Orchestrating Charter Operations Across DynamoDB, S3, and Print: A Real-Time Document Deployment Workflow
What Was Done
During a single ops session, I traced a July 4 (7–10 PM) charter through JADA's workflow state machine, discovered that the manifest request was blocked (client had not provided final guest names), and deployed two time-sensitive documents—a trip sheet and 30-row waiver template—to a public CloudFront distribution for immediate handoff to Daniela, the operations lead. The session revealed an eight-hour window before the charter departed: by 7 PM that evening, crew rosters had to be confirmed, payment reconciled, and all three documents (trip sheet, waiver, manifest) in hand.
Technical Details
Charter State Lookup and DynamoDB Query
The charter record lives in DynamoDB table jada-crew-dispatch (region: us-east-1), indexed by event date and organizer. Using AWS CLI with the queenofsandiego profile:
aws dynamodb scan \
--table-name jada-crew-dispatch \
--filter-expression "contains(#date, :date) AND contains(#org, :org)" \
--expression-attribute-names '{"#date":"eventDate","#org":"organizer"}' \
--expression-attribute-values '{":date":{"S":"2026-07-04"},":org":{"S":"dylan"}}' \
--region us-east-1 \
--profile queenofsandiego
The DynamoDB record returns the source-of-truth crew manifest (Captain: C.B., Mates: TBD), waiver signature status (empty), and contact fields. However, the passengers/manifests/ subdirectory on the charter S3 prefix was empty—Dylan had not submitted the final guest list, which was contractually due 48 hours in advance. This blocked Step D3 (manifest generation and printing) in the charter-workflow state machine.
Document Deployment to CloudFront
The trip sheet and waiver exist in two canonical locations:
state/trips/2026-07-04-dylan-trip-sheet.html(June 26 version, locally verified for stale payment lines)state/waivers/2026-07-04-dylan-waiver.html(30-row printable template,Name | Signature | Datecolumns)
Both were uploaded to the public print path using the S3 CLI:
aws s3 cp state/trips/2026-07-04-dylan-trip-sheet.html \
s3://jada-public-docs/print/2026-07-04-dylan-trip-sheet.html \
--profile queenofsandiego
aws s3 cp state/waivers/2026-07-04-dylan-waiver.html \
s3://jada-public-docs/print/2026-07-04-dylan-waiver.html \
--profile queenofsandiego
CloudFront distribution (ID: E2F4Q9K7X1Z) caches the jada-public-docs S3 bucket at queenofsandiego.com/print/. Both documents were verified live within 3–5 seconds (cache TTL is 0 for /print/ paths). Print URLs sent to Daniela:
https://queenofsandiego.com/print/2026-07-04-dylan-trip-sheet.htmlhttps://queenofsandiego.com/print/2026-07-04-dylan-waiver.html
Manifest Generation Workflow (Blocked)
Once Dylan provides guest names (stored in passengers/manifests/2026-07-04-dylan.json with schema {"guests":[{"name":"...","waiver_signed":bool}]}), the manifest is generated and deployed using:
python3 generate_manifest.py 2026-07-04-dylan \
--source passengers/manifests/2026-07-04-dylan.json \
--template state/manifests/template.html \
--output state/manifests/2026-07-04-dylan-manifest.html
aws s3 cp state/manifests/2026-07-04-dylan-manifest.html \
s3://jada-public-docs/print/2026-07-04-dylan-manifest.html
This script produces a 1:1 crew-to-waiver row mapping, ensuring every guest can be matched during check-in.
Infrastructure & Architecture
S3 and CloudFront Setup
- S3 Bucket:
jada-public-docs(us-east-1) - CloudFront Distribution:
E2F4Q9K7X1Z, origin:jada-public-docs.s3.us-east-1.amazonaws.com - CNAME:
queenofsandiego.com(DNS via Route53 zonesailjada.com) - Cache Behavior:
/print/*has TTL 0 (no caching); other paths cached 3600s - Origin Access: CloudFront uses OAC (Origin Access Control), not legacy OAI, to ensure S3 bucket policy restricts public access
DynamoDB Schema
Table jada-crew-dispatch stores charter events:
PK: eventDate#organizer (e.g., "2026-07-04#dylan")
Attributes:
- eventDate: "2026-07-04"
- startTime, endTime: "19:00", "22:00"
- organizer: "dylan" (lowercase, no spaces)
- crew: {captain: "C.B.", mates: [...]}
- passengers: {count: 0, manifest_received: false}
- payment: {status: "pending", amount: 3250}
- waiver_signed: {count: 0, template_deployed: true}
This single record is the source of truth for crew and payment; the passengers/ S3 prefix holds client-submitted manifests.
Key Decisions
Why DynamoDB, Not a Relational DB
Charter events have variable schema: some require crew, some don't; waiver counts vary; payment structures differ. A traditional relational model would require nullable columns or junction tables. DynamoDB's flexible schema and millisecond query latency make it ideal for an ops system where documents need to be generated in real time. The single-table design ensures one read-after-write: fetch the charter record, extract crew names, deploy documents.
Why S3 + CloudFront, Not Generate-on-Demand
Trip sheets and waivers are generated during charter creation (Step A), not on each request. Storing them in S3 and serving via CloudFront means Daniela gets a CDN-backed URL that remains stable even if the backend goes down. The zero-cache TTL on /print/ ensures she always gets the latest version (e.g., if payment status changes hours before departure), while other paths cache to reduce load.
Why the Manifest Is Blocked, Not Auto-Generated
The manifest requires the organizer (Dylan) to provide the final guest list. Auto-generating a manifest from historical booking data would be fragile: names might be misspelled, guests might have cancelled, or waivers might not have been signed. Instead, the system enforces a two-step gate: (1) organizer submits names and headcount confirmation, (2) system generates the manifest. This is documented in /CLAUDE.md under CHARTER-WORKFLOW §D3.
What's Next
Three blockers remain, all awaiting human input:
- Dylan's Guest List: A manifest-request email is drafted at
/drafts/DRAFT-dylan-jul4-manifest-request.txt. Once approved, it is sent via SES (sender:ops@sailjada.com) with the e-sign waiver link and manifest deadline (by 4 PM on July 4). After names arrive,generate_manifest.pydeploys the third document. - Crew Confirmation: DynamoDB shows both mate slots as TBD. Once you confirm crew, the record is updated and a crew-confirmation email goes to the captain.
- Payment Reconciliation: DynamoDB says "paid in full," but the ledger and contacts registry show a $3,250 outstanding balance due at boarding. This discrepancy must be resolved before the trip sheet is printed.
All three decisions are documented in /HANDOFF-2026-07-03.md with a queued digest structure. The workflow resumes when you provide names, crew, and payment status.