LCMS media architecture spike — Core Platform's input

CPT contribution to SPPLT-19123 · assigned to Tom Irons · reported by Kevin Pimentel
Measured from live AWS 2026-08-26/27 · accounts 210540517083 + 587454362389
Status: Story Creation (To Do) · appetite: 2 days

CPT's recommendation: provision an SP-owned S3 bucket in the v3 account now. It ships with the right controls, it's in the right account, the existing branch code targets it with a config change, and it doesn't create a consolidation problem later — it creates the destination.

Q1 Answered
What buckets exist today?

Eight buckets serve 2.0 content, split across two AWS accounts and two regions.

ContentBucketAccountSizeAccess
Lesson videosolidprofessor-cdnlegacy408 GiBCloudFront signed (root-managed)
HTML packages (2.0)plt-lessons.solidprofessor.com2.0 prod52 GiBPrivate, all 4 blocks on
HTML packages (legacy)lessons.solidprofessor.comlegacy53 GiBOAI only, no public-access block
Resources & imagessolidprofessorlegacy207 GiBWorld-readable
Practice filespractice-exerciseslegacy53 GiBWorld-readable + listable
Captionscc.solidprofessor.comlegacy0.3 GiBWorld-readable
Banners, avatars, SP Liveplt-prod-assets-legacylegacy0.7 GiBWorld-readable (mixed-use)
Assignments, ZIPs, assetsplt-prod-assests2.0 prod3,021 GiBWorld-readable (versioning on)

Every bucket is SSE-S3 encrypted. Access logging is off on all eight. No legacy bucket has a public-access block. 5 of platform-backend's 8 S3 disks point at legacy buckets — content is mostly not synced into 2.0; 2.0 reads it live from legacy.


Q2 CPT input
What is Core Platform's consolidation trajectory and timeline?

The six legacy buckets move out of the legacy account after v3 goes live, tracked under the legacy retirement epic. The destination account and timing are not yet set — legacy retirement drives the schedule, not a separate consolidation programme.

v3 builds every new bucket with the controls the legacy estate doesn't have:

Controlv3Legacy / v2 today
Encryption at restSSE-S3 with bucket keysSSE-S3 on all 8 — already there
VersioningOn by defaultOn for 1 of 8
Access loggingS3 + CloudFrontOff on all 8 (S3); legacy CF only
Signed URLs / cookiesKey-group-based, delegableRoot-managed signers on video only
Public-access blocksOn unless explicitly openedOn for 1 of 8

CPT can provision an SP-owned S3 bucket in the v3 account with full controls today. LCMS writes to it. If consolidation happens later, a bucket already in the right account with the right posture is either the target or trivially foldable — so this doesn't create a fourth thing to fold in, it creates the destination.


Q3 Needs contract review
Is the DoD sensitive-data constraint real, and does it demand bucket separation?

This requires the actual customer contract language or compliance rules, not a technical opinion. Kevin scoped this to "whoever holds the DoD customer contract details — probably Kevin or CS-adjacent leadership."

What CPT can contribute to the answer: every content bucket in both accounts is already SSE-S3 (AES256) encrypted at rest. If the constraint is satisfied by encryption + IAM boundaries + audit logging, then a shared bucket with proper controls is sufficient and no physical separation is needed. If the contract requires physical isolation, that's a different architecture.

If the answer turns out to require physical separation, a second isolated bucket can be added without undoing the work above — but that's a judgment for whoever reviews the contract language.


Q4 CPT recommendation
Where should LCMS media live?

Kevin's spike frames this as a dual-running question, but we think the same answer applies permanently — the reasons below aren't temporary.

SP-owned S3 in the v3 account, provisioned by CPT in Terraform. The existing branch code is S3-compatible and doesn't require changes to target a different bucket.

Signed cookies. R2 has no signed-cookie feature — Tom's design doc says so. On S3 behind CloudFront they exist, and they close 4 of his 7 open design questions (the leak window, streaming capacity, the secure flag, dead URLs on long sessions). All four exist only because there's no signed-cookie equivalent on R2.

First-party hostname. Packages render in an iframe, so cookie delivery depends on the serving hostname. An SP-owned host under solidprofessor.com makes the cookie first-party. Laravel Cloud's managed URL is on their domain — third-party, subject to browser blocking.

No custom domain on Laravel Cloud. This is a missing feature at every tier, not a pricing limit. No SP hostname, no zone-level WAF or cache rules on that bucket.

CDN caching works on signed URLs. CloudFront strips the signature from the cache key — measured 72% hit rate on 10K live requests. On R2 the only way to get caching is putting an SP-owned proxy back in the byte path.

One home for video, not two. Under the current design, new LCMS-authored video goes to R2 while the existing 408 GiB library stays in solidprofessor-cdn (legacy account) — permanently. LCMS would need fallback resolution logic to serve both, and there's no migration path that reunites them because the destinations are in different companies' accounts. An SP-owned S3 bucket is in the same account family as the legacy content, so consolidation into one bucket with one resolution path is at least possible.

The cost difference between S3+CloudFront and R2 is negligible at our volume — under $200/month either way.


Q5 Depends on Q4
What is the migration approach, and how does it sequence against consolidation?

There is no byte migration today. Kevin's "content-as-configuration" draft proposes avoiding one — Spatie's per-row disk column gives ownership at the application layer without moving bytes. 132K media rows seeded, zero bytes moved. That draft is still pending Pod 1 sign-off.

What happens with each content type if LCMS writes to an SP-owned bucket:

ContentTodayWith SP-owned bucketByte migration?
Thumbnails, jumbotrons, course imagesLCMS stores media, then syncs a URL pointer to 2.0's database (3 collections). No bytes cross — 2.0 reads them from wherever LCMS stored them.New uploads land in the SP-owned bucket. The URL pointer sync continues to work — it just points at a different host.No
Lesson videoServed from solidprofessor-cdn via CloudFront. LCMS has no code path for video today.New LCMS-authored video would land in the SP-owned bucket. Existing video stays in solidprofessor-cdn until legacy retirement moves it.Not during dual-running
CaptionsNot in LCMS yet. cc.solidprofessor.com consumed only by a manual artisan command.Same as video — future work.Not during dual-running
HTML packagesLegacy and 2.0 each have their own copy (lessons.solidprofessor.com and plt-lessons.solidprofessor.com). Content is still being synced from legacy into 2.0.Tom's one-time migration seeds LCMS from 2.0. After that, LCMS is the source of truth and the legacy sync stops.Tom's one-time migration

Sequencing against consolidation: legacy retirement drives the timeline, not a separate consolidation programme (Q2). The new bucket is in the v3 account from day one — already where consolidation would put it.


Q6 CPT recommendation
What are the SOC 2 requirements the consolidation is designed to satisfy?

SOC 2 readiness is a work in progress across the whole platform, and v3 is where we're laying the foundation. The controls in the Q2 table above aren't mapped to specific trust service criteria yet — they're the baseline any auditor will expect to see. An LCMS bucket built in v3 inherits all of them, starts in a better posture than any of the eight buckets serving content today, and is on the same footing as the rest of the v3 infrastructure when formal control mapping happens.