Architecture Multi-Cloud

Secure Multi-Cloud Landing Zone and Enterprise Architecture for Media & Streaming: A Complete Azure + AWS Design

Executive summary

Lumina Media runs entertainment at the scale of a nation state: a global direct-to-consumer service that carries SVOD, AVOD, TVOD and FAST tiers, 24×7 linear channels, live sports and news, a ~400 PB video-on-demand library, partner and affiliate distribution, and a full production/post/archive supply chain. That reaches ~120 million subscribers across web, mobile, smart-TV, console and set-top-box, and peaks at ~45 million concurrent streams during live finals — the single most demanding minute in consumer software, where 45 million players all ask for the same segment inside the same two seconds. Today the estate spans two Media Operations Centres (Los Angeles + London) for live contribution, playout and near-line archive, an on-prem AD DS forest corp.luminamedia.net, SAP back office, and a sprawl of 100+ apps. This document specifies the secure multi-cloud landing zone that becomes the network, identity, governance, delivery and data foundation for running that estate on Azure (East US 2, West Europe, Southeast Asia) and AWS (us-east-1, eu-west-1, ap-southeast-1) — active/active for Tier-1 delivery, and without ever letting a viewer see an outage.

The hard constraints are not the epilogue; they are the design. Every decision here must hold under MovieLabs Enhanced Content Protection + TPN/CDSA for premium studio content, PCI-DSS for billing and payments, GDPR + CCPA for viewer PII and consent, plus SOC 2 and DPP for the operational estate — and it must do so while hitting quality-of-experience numbers a marketing team quotes on stage: video-start-time p95 < 2s, rebuffer ratio < 0.4%, live latency < 8s, playback-failure < 0.5%, and 99.99% availability for the Tier-1 delivery path. The reference bar is three existing KloudVin designs — the zero-downtime multi-cloud landing zone for a universal bank, the secure multi-cloud landing zone for global logistics, and the secure multi-cloud landing zone for healthcare — and this one must match their depth while adding what a streaming operator actually runs: multi-DRM packaging, multi-CDN steering, server-side ad insertion, a dual-plane identity model at 120M scale, and a QoE analytics pipeline that ingests tens of millions of concurrent events.

The design rests on seven load-bearing decisions. Dual-plane identity keeps workforce identity (on-prem AD DS projected to a single Entra ID tenant, with Okta and PIM for SSO/CA) rigorously separate from consumer identity (Azure AD B2C / Amazon Cognito at 120M users) — the two never share a directory, an app registration or a token. Package once, protect once: every asset is transcoded to an ABR ladder, packaged as CMAF and encrypted CENC exactly once, with Widevine/FairPlay/PlayReady keys issued by a SPEKE-class key service, never by a CDN. Multi-CDN by default: three commercial CDNs are steered per-session on live real-user metrics and per-GB cost, fronted by origin shield, signed-URL/JWT token auth, edge WAF, bot and DDoS. Segmented media domains: Live, VOD, CIAM+Subscription, Recommendation, Ad-tech and Corporate each become their own landing zone with default-deny east-west. Tiered resilience: a four-tier RTO/RPO model puts entitlement, playback, DRM-license, origin, live-event control, SSAI-core and CIAM-auth into active/active at RTO ≤15 min / RPO ≤1 min. Guardrails as code at the Azure lm management-group root and AWS Organizations SCP/RCP perimeter make non-compliance structurally impossible. QoE is the SLO: the number the business watches is video-start-time and rebuffer ratio, and the whole platform is instrumented to defend it.

Decision area Target-state choice Why it matters for Lumina Media
Cloud posture Dual-cloud with workload affinity (place each domain where it runs best), active/active for Tier-1 delivery A regional or single-cloud loss never darkens a live final; no lift-and-shift into both clouds
Identity Two planes, never mixed: Entra ID + Okta (workforce) and Azure AD B2C / Cognito (120M viewers) A CIAM breach cannot reach staff/admin; a workforce compromise cannot touch viewer PII
Content protection Package once: ABR + CMAF + CENC multi-DRM; SPEKE key service; MovieLabs/TPN pipeline controls Studio licensing and premium windows survive an audit; keys never live on a CDN
Delivery Multi-CDN (CDN-A/B/C) with RUM + cost steering, origin shield, signed URL/token, edge WAF/bot/DDoS No single CDN failure or price spike degrades QoE for 45M concurrent viewers
Connectivity Dual ExpressRoute + dual Direct Connect from both MOCs; Virtual WAN + Transit Gateway hubs Live contribution and origin egress never transit the public internet
Governance Azure MG root lm + AWS Organizations / Control Tower, policy-as-code, delegated admin Residency, encryption and content-protection guardrails inherit down; teams cannot opt out
Segmentation Per-domain landing zones, own /22 CIDR, default-deny east-west, brokered cross-domain A breach in Ad-tech or Corporate is not a breach in Live, VOD or CIAM
Resilience Four tiers; Tier-1 active/active (RTO ≤15 m / RPO ≤1 m); viewers must not see an outage Entitlement, playback, DRM and origin survive a regional loss with sub-minute data loss
Monetisation SSAI for live + VOD; editorial/ad path separation; PCI-segmented billing An ad-decision fault degrades ads, not the programme; card data stays in a small PCI scope
QoE + analytics RUM (Conviva-class) + high-scale event ingest feeding a steering + experimentation loop The KPI an executive watches — VST and rebuffer — is measured and defended continuously

Target-state overview

The target state reads left to right as five planes: the two on-prem Media Operations Centres and their contribution/playout edge; a private-connectivity layer that reaches both clouds without ever touching the internet; the Azure and AWS platforms with their workload landing zones where the media supply chain is packaged and protected; a multi-CDN edge that steers and secures delivery; and the viewers themselves, measured continuously for QoE. Contribution feeds both clouds over redundant circuits; each cloud carries its own governed platform and segmented workloads; and every viewer request meets a token-and-WAF-gated edge — the origins behind it are never directly exposed.

Lumina Media target-state: two Media Operations Centres (LA + London) reach Azure and AWS landing zones over dual ExpressRoute and Direct Connect, which package and protect the media supply chain and publish through a multi-CDN edge to 45M concurrent viewers measured for QoE

Each cloud is built as a small set of platform capabilities that never host workloads, plus workload landing zones that do. On Azure that is the lm management-group hierarchy with platform subscriptions lm-plat-{identity,connectivity,management,security}, an Azure Virtual WAN hub per region carrying an Azure Firewall for egress and east-west inspection, and landing-zone subscriptions lm-lz-{live,vod,ciam,reco,adtech,corp,data,sandbox} per environment. On AWS the mirror is Root > Security > Infrastructure > Workloads{Live,VOD,Data,AdTech,Corp} > {Prod,NonProd} plus a Sandbox OU, a Transit Gateway hub with a Gateway Load Balancer inspection VPC, and accounts named lm-<purpose>-<env>. The two clouds are deliberately symmetric in intent — same identity planes, same guardrail classes, same tiering — but idiomatic in mechanism, because forcing Azure to behave like AWS (or vice versa) is how landing zones rot. The Azure spine follows the enterprise-scale landing zone pattern; the AWS spine follows Control Tower multi-account guardrails.

The planes below are the control surfaces every workload inherits. Read this as “what exists once, centrally, so a workload team never re-invents it”:

Plane Azure realization AWS realization Purpose
Governance / policy Management groups under root lm; Azure Policy + initiatives; Bicep Organizations OUs; SCPs + Resource Control Policies; Control Tower controls Guardrails inherit down; residency/encryption/content-protection non-negotiable
Identity (dual-plane) Workforce: Entra ID + Okta + PIM/CA. Consumer: Azure AD B2C tenant Workforce: federated to Entra (OIDC/SAML). Consumer: Amazon Cognito Staff JML never mixes with 120M-viewer CIAM; two separate token stories
Connectivity Virtual WAN hub /20 per region; Azure Firewall; Private DNS; ExpressRoute GW Transit Gateway; GWLB inspection VPC; Route 53 Resolver; Direct Connect GW Private east-west + inspected egress; contribution never on the internet
Delivery edge Front Door (origin shield / one CDN), steering SDK, WAF, DDoS CloudFront (origin shield / one CDN), steering SDK, WAF, Shield Multi-CDN steering, signed URL/token, edge security for every viewer request
Security operations Defender for Cloud; Microsoft Sentinel SIEM; Key Vault (HSM); Wiz; Falcon Security Hub; GuardDuty; CloudTrail org trail; KMS; Wiz; Falcon Central detection, immutable audit, CMK/key custody, CSPM + EDR
Management / observability Azure Monitor; central Log Analytics; Dynatrace; QoE RUM; ServiceNow CloudWatch; central log archive account; Dynatrace; QoE RUM; ServiceNow One pane of telemetry; QoE and ops on the same rail
Workload landing zones lm-lz-* subscriptions × env; spoke VNets /22; PE subnets /26 Workloads OU accounts × env; spoke VPCs /22; PrivateLink subnets /26 Where Live, VOD, CIAM, Reco, Ad-tech and Corporate actually run

Regions carry two distinct jobs that must never be confused: resilience pairs (two regions that back each other active/active for Tier-1 delivery) and residency boundaries (a region that exists to keep a jurisdiction’s viewer PII inside it). Conflating them is how a well-meaning failover becomes a GDPR incident. Lumina’s map pins US/global delivery to the US pair, EU viewer PII to Europe, and APAC audience latency to Southeast Asia:

Region Cloud Role Residency scope Tier-1 pattern
East US 2 Azure Primary Americas Global content; US viewer PII Active/active with us-east-1 and West Europe for delivery
West Europe Azure EU primary EU viewer PII (GDPR) Active/active delivery; EU PII stays in-region
Southeast Asia Azure APAC primary APAC viewer PII Active/active delivery; regional audience latency
us-east-1 AWS Primary Americas Global content; US viewer PII Active/active with East US 2 (origin, VOD, data)
eu-west-1 AWS EU primary EU viewer PII (GDPR) Active/active delivery; EU PII stays in-region
ap-southeast-1 AWS APAC primary APAC viewer PII Active/active delivery; regional origin + data

Architecture principles and operating model

Principles are the tie-breakers you appeal to when two good options collide at 02:00 during a design review — and they only earn their keep if each one is enforceable by a named control and kills a named anti-pattern. “Be secure” is useless. Lumina’s twelve principles each map to a mechanism and a failure mode it forecloses, and several are streaming-specific in a way a generic landing zone would miss (P3, P5, P7, P10):

# Principle What it means in practice How it is enforced Anti-pattern it kills
P1 Two identity planes, never mixed Workforce and consumer identity are separate tenants, tokens and lifecycles Entra + Okta (staff) vs Azure AD B2C / Cognito (viewers); no shared app reg A CIAM breach reaching admin, or staff SSO leaking into the viewer app
P2 Identity is the perimeter Every access is authenticated, authorised, MFA’d, conditional Entra Conditional Access + PIM; Cognito/B2C token policies Flat network trust; standing admin credentials
P3 Package once, protect once One transcode → CMAF → CENC multi-DRM per asset; keys from a key service Packager + SPEKE/Key Vault/KMS; deny un-encrypted premium egress Re-encoding per device; DRM keys sitting on a CDN
P4 Guardrails as code, not tickets Compliance is a policy definition in git, applied at the root Azure Policy initiatives + AWS SCP/RCP via pipeline Drift; per-team exceptions that quietly become the norm
P5 QoE is the SLO Success is VST, rebuffer, latency and playback-failure — not CPU RUM + synthetic SLOs; steering loop consumes the metric Green infra dashboards while viewers buffer and churn
P6 Least privilege, just-in-time No standing production access; elevate through approval + audit PIM eligible roles; IAM Identity Center session policies 200 people with permanent Owner/AdministratorAccess
P7 Multi-CDN, no single edge Every asset is deliverable from ≥2 CDNs; steer on live health + cost Steering SDK + DNS; origin shield; per-asset cache policy One CDN outage or price spike degrading 45M viewers
P8 Everything auditable + immutable Control-plane and PII access logged to WORM storage Immutable Blob / S3 Object Lock; Sentinel + CloudTrail org trail Tamperable logs; unprovable “who exported this viewer’s data”
P9 Resilience by tier, not by wish Each app gets a tier with explicit RTO/RPO and a DR pattern Tag-driven backup/replication; game-day tests One-size DR that over-spends Tier-3, under-protects Tier-1
P10 Editorial and ad paths separate Ad decisioning cannot break the programme; SSAI degrades gracefully Separate SSAI service + failover to editorial manifest An ad-server outage taking a live channel off air
P11 Automate the landing zone Subs/accounts vended by pipeline with baseline baked in Account/subscription factory (IaC); no click-ops in prod Snowflake environments; inconsistent security baselines
P12 Residency is structural Viewer PII placed by jurisdiction at account/subscription/key level deny out-of-region resources; region-scoped CMKs A failover that moves EU viewer PII to a US region

The operating model that runs these principles is a thin Cloud Platform / CoE owning the platform planes, a Security & GRC function owning policy, content-protection standards and audit evidence, a Media Engineering guild owning the encode/package/DRM/origin/SSAI supply chain, and workload teams who consume vended landing zones through a self-service factory. The CoE does not deploy workloads; it vends a subscription/account with network, identity, logging and policy baseline already attached, then gets out of the way. Media Engineering owns the golden path for the delivery pipeline so that Live and VOD teams inherit a compliant packager and DRM integration rather than re-implementing CENC. Security & GRC author the guardrails as code and own the TPN/MovieLabs and PCI evidence, but do not gate every deployment by hand — the policies do that continuously. This split is what lets a 120M-subscriber operator move without a central bottleneck, and it is why P3, P4 and P11 exist:

Capability Cloud Platform / CoE Security & GRC Media Engineering Workload team Primary tooling
Management-group / OU tree Own Consulted Inherits Inherits Bicep / Terraform, Control Tower
Guardrail policies (Policy/SCP/RCP) Implements Own Complies Complies Policy-as-code pipeline
Landing-zone vending Own (factory) Reviews baseline Consumes Requests + consumes Subscription/account factory
Network hub + firewall Own Reviews rules Requests flows Requests spoke + rules Virtual WAN, Transit Gateway
Identity — workforce (PIM/CA) Operates Own policy Uses eligible roles Uses eligible roles Entra ID, Okta, IAM Identity Center
Identity — consumer (CIAM) Enables platform Own privacy standard Consulted Own (viewer flows) Azure AD B2C, Amazon Cognito
Encode / package / DRM / origin Provides landing zone Owns content-protection std Own Consumes golden path Media packager, SPEKE, Key Vault/KMS
Multi-CDN + edge security Provides platform Owns WAF/bot policy Own steering Requests cache policy Front Door/CloudFront, steering SDK
QoE + analytics ingest Provides pipeline Owns privacy/consent Instruments players Own their KPIs RUM SDK, Event Hubs/Kinesis
DR tier + game-days Provides patterns Validates evidence Owns delivery RTO/RPO Own their RTO/RPO Backup, replication, traffic steering

Media domains and segmentation

Segmentation is the single most important streaming-specific decision in the whole design, because the blast radius of a breach in an under-segmented media estate is either 120M viewers’ PII, the studio content that underwrites every licensing deal, or the billing system that funds the company. Lumina splits the estate into six media/functional domains, each realised as its own landing zone with a dedicated /22 from the Azure 10.20.0.0/12 super-net (AWS mirrors from 10.40.0.0/12, on-prem is 10.0.0.0/12), default-deny east-west, and its own data-sensitivity and content-protection posture. The only traffic that crosses a domain boundary is a governed contract — an entitlement check, a viewing-event stream, a personalised manifest, a billing settlement — and every one is brokered by the event mesh or integration engine and inspected at the hub firewall. There is no flat network in which a compromised ad partner can read the entitlement database, or a corporate laptop can reach a premium origin.

Lumina Media domain segmentation: six isolated landing zones — Live, VOD, CIAM+Subscription, Recommendation, Ad-tech, Corporate — each with its own CIDR and default-deny east-west, connected only by brokered entitlement, event, manifest and billing contracts crossing the hub firewall and event mesh

The domain matrix below is the authoritative placement and isolation reference. Cloud placement follows workload affinity (P-cloud posture): live encode/package/origin, CIAM and the entitlement plane lean Azure (closest to Entra, B2C and the on-prem MOCs), while the ~400 PB VOD library, the recommendation/analytics estate and much of Ad-tech lean AWS (object-storage economics, Lake Formation governance, SageMaker), with cross-cloud replication for DR. Every premium-content and PII-bearing row carries private endpoints, CMK and immutable audit as a non-negotiable baseline. The VOD deep-dive lives in video-on-demand streaming on AWS with multi-DRM; the consumer-identity deep-dive in Entra External ID / CIAM custom policies; the edge in multi-CDN edge with origin cloaking.

Domain Key applications Data classes Landing zone (Azure sub / AWS OU) CIDR /22 Cloud placement Tier Isolation boundary
Live Contribution ingest, live encode/package, linear playout, SSAI, live origin, event control Premium content, ad markers (SCTE-35) lm-lz-live / Workloads/Live 10.20.16.0/22 Azure primary (EUS2+WEU a/a), AWS active Tier-1 Redundant origin; event-day freeze; editorial/ad path split
VOD Transcode farm, packaging, VOD origin, catalog, ~400 PB library Premium content, metadata lm-lz-vod / Workloads/VOD 10.20.20.0/22 AWS primary (S3 economics), Azure Blob cache/DR Tier-1 playback / Tier-2 archive Own library; lifecycle + Object Lock; no direct viewer read
CIAM + Subscription Azure AD B2C / Cognito, token service, entitlement, subscription mgmt, billing Viewer PII, PCI (billing) lm-lz-ciam / Workloads/Corp 10.20.24.0/22 Azure primary (B2C), AWS Cognito a/a Tier-1 PE-only; PCI scope; bot/rate-limit; never mixed with workforce IdP
Recommendation Reco models, feature store, watch-history, A/B experimentation De-identified viewing events, model features lm-lz-reco / Workloads/Data 10.20.28.0/22 AWS primary (SageMaker/Lake), Azure secondary Tier-2 Reads event stream only; no CIAM DB read path
Ad-tech SSAI, ad-decision server (ADS), VAST/VMAP, ad-reporting Ad impressions, some targeting PII lm-lz-adtech / Workloads/AdTech 10.20.32.0/22 AWS primary, Azure presence Tier-1 (SSAI serve) / Tier-2 (report) Editorial/ad path separation; consent-gated targeting
Corporate SAP S/4HANA, HR, finance, content ops, collaboration Business, some PII lm-lz-corp / Workloads/Corp 10.20.36.0/22 Azure primary (M365) Tier-2 Segregated from delivery + CIAM; separate IdP groups
Data & analytics QoE ingest, hot/cold stores, RUM, warehouse, DSAR pipeline De-identified events, aggregated PII lm-lz-data / Workloads/Data 10.20.40.0/22 AWS primary (Lake Formation), Azure secondary Tier-2 Ingest-only inbound; export audit; DSAR fulfilment
Sandbox / dev Dev, test, load-generation, synthetic-content pipelines Synthetic only, no prod PII lm-lz-sandbox / Sandbox OU 10.20.44.0/22 Both, isolated Tier-3 No prod data; no prod network path

Cross-domain traffic is a small, explicit, auditable set of contracts — not an open mesh. Each is a real streaming integration pattern with a defined transport and a single broker, which is exactly what makes end-to-end traceability, consent enforcement and resilient replay possible. The event mesh follows the event-driven EventBridge/SQS/Lambda pattern, and the ingest is sized with the backpressure and flow-control discipline:

From → To Contract / standard Transport Broker Control
Live → VOD Catch-up / start-over / DVR record Segment copy + manifest handoff Packaging pipeline Rights window; retention policy; watermark
VOD / Live → CIAM Entitlement + concurrency + geo check HTTPS API (JWT) over private link Entitlement service Token validation; concurrency counter; geo rules
CIAM → Ad-tech / Reco Viewing + subscription events (de-identified) Event stream (Event Hubs / Kinesis) Event mesh De-id + consent flag; no raw PII on the stream
Ad-tech → Live / VOD Personalised manifest (SSAI) + ad break Manifest manipulation + VAST/VMAP SSAI service SCTE-35 markers; editorial/ad split; graceful fallback
Reco → Playback / UI Recommendation rows HTTPS API (cached, rate-limited) API gateway Rate-limit; graceful fallback to editorial rows
CIAM → Corporate Billing / revenue / PCI settlement Tokenised API / X12 over private link Integration + payment gateway PCI scope; tokenised PAN; no card data at rest outside scope
All → Data & analytics QoE + telemetry ingest Event stream + batch Event mesh + ingest Ingest-only; export audit; backpressure/DLQ

Requirements and non-functional targets

Non-functional targets are where “compliant” and “high quality” become numbers an engineer can build to and an auditor or a Chief Product Officer can test. Lumina’s resilience model is four tiers, each with an explicit RTO, RPO, availability SLO, multi-region pattern and backup posture — assigned by tag so the platform, not a human, enforces the right protection. The delivery core (entitlement, playback API, DRM-license, origin, live-event control, SSAI-core, CIAM-auth) is Tier-1 at RTO ≤15 min / RPO ≤1 min, delivered by active/active per the Azure active/active and AWS multi-region active/active patterns — because a viewer mid-final must not see a stall. Tier-0 (identity, DNS, network control, privileged access, core security, multi-CDN steering control) is the foundation everything depends on and carries the tightest target of all:

Tier Example workloads RTO RPO Availability SLO Multi-region pattern Backup / immutability
Tier-0 Entra/AD DS, DNS, network control plane, PIM, Key Vault/KMS, Sentinel, multi-CDN steering control ≤15 min ≈0 99.99% Global/active-active; region-independent Geo-redundant; HSM key backup; immutable audit
Tier-1 Entitlement, playback API, DRM-license, origin, live-event control, SSAI-core, CIAM-auth ≤15 min ≤1 min 99.99% Active/active cross-region (EUS2+us-east-1+WEU) Continuous replication; PITR; immutable + geo copy
Tier-2 Business apps, analytics, support, ad-reporting, reco training, corporate ≤4 h ≤1 h 99.9% Active/passive warm standby; cross-region backup Nightly + hourly log; cross-region restore tested
Tier-3 Dev, sandbox, load-generation, batch ≤24 h ≤24 h 99.5% Backup/restore; single region acceptable Daily backup; standard redundancy

Resilience keeps the service up; quality-of-experience is what keeps viewers watching, and it is measured at the player, not the server. These are the targets the steering loop, the origin design and the ABR ladder all exist to defend — every one a real-user or synthetic number a dashboard proves against an SLO, not an aspiration:

QoE / performance metric Target Measured where Why it matters
Video-start-time (VST) p95 < 2s Player SDK RUM (Conviva-class) Abandonment climbs sharply past 2s; first impression of every session
Rebuffer ratio < 0.4% of watch time Player SDK RUM The single biggest driver of churn and complaints
Live latency (glass-to-glass) < 8s (LL-CMAF) Synthetic + player timestamp Social spoilers beat a laggy live stream; sports demands it
Playback-failure rate < 0.5% of attempts Player SDK + gateway A failed start is a lost session and a support ticket
Tier-1 availability 99.99% Synthetic + SLO burn Entitlement/playback/DRM must not have a visible outage
CDN cache hit-ratio > 95% edge / > 98% with shield CDN logs + origin shield Origin cost and rebuffer both track cache misses
DRM license latency p95 < 300 ms License service metric A license stall delays the first frame as surely as a slow origin

QoE and resilience are only real if the security, privacy, content-protection and durability posture holds underneath them. The NFRs below are the measurable states that make the compliance obligations concrete across both clouds — the mechanism differs, the target does not:

NFR domain Target / measurable state Azure control AWS control
Premium content protection MovieLabs ECP + TPN controls on 100% of premium pipelines Private origin + CMK + Bicep pipeline PrivateLink + KMS + SPEKE
DRM coverage 100% premium assets CMAF/CENC multi-DRM Packager + Key Vault; SPEKE MediaPackage/SPEKE + KMS
Viewer PII exposure Zero public endpoints on CIAM/PII stores Private Endpoint + Policy deny public PrivateLink + SCP + BlockPublicAccess
PCI billing scope Cardholder data tokenised; PCI zone segmented Segregated sub + tokenisation Segregated account + tokenisation
Encryption in transit TLS 1.2+ everywhere; signed token at edge Front Door/APIM policy CloudFront/API GW policy
Consent + privacy (GDPR/CCPA) Consent captured; DSAR ≤ statutory SLA B2C + Purview + DSAR pipeline Cognito + Lake Formation + DSAR
Identity separation Workforce and CIAM never share a directory Entra/Okta (staff) vs B2C (viewers) Entra-fed IAM vs Cognito
Privileged access 0 standing prod admins; JIT only PIM eligible + approval IAM Identity Center + session policy
Data residency EU viewer PII stays in EU regions Policy allowedLocations (EU) SCP aws:RequestedRegion deny
Bot / credential-stuffing Login + token endpoints bot-scored + rate-limited Front Door WAF + bot manager WAF + Shield + rate-based rules
Event durability 0 lost QoE/entitlement events; replay ≥ 72 h Event Hubs + Capture + DLQ Kinesis + Firehose + archive/replay
Audit immutability Control-plane + PII access WORM ≥ 1 yr Immutable Blob + Sentinel S3 Object Lock + CloudTrail org trail

Capacity and performance carry streaming-specific numbers too: the live plane must absorb dual contribution (SRT/RIST/Zixi) from both MOCs and fan a single live event out to 45M concurrent players without an origin-fetch storm, so origin shield and per-segment cache policy are sized for the finals, not the average night; the VOD plane must serve diagnostic-quality bitrates from a ~400 PB archive with hot-tier latency for new releases and lifecycle demotion of long-tail to archive; the CIAM token service must sustain login and entitlement bursts at launch and event start without becoming the bottleneck that fails 45M playbacks; and the QoE analytics pipeline must ingest tens of millions of concurrent events without back-pressure loss. These ceilings (Event Hubs throughput units, CDN egress commits, APIM/token capacity, storage tiers) are specified per workload in the detailed design; the platform’s job is to make them elastic and observable rather than fixed.

Requirements traceability

Traceability is the artefact an auditor — or a studio’s content-security assessor — asks for first: for every requirement, the driver that mandates it, the control that satisfies it, and the concrete cloud service that implements the control on both clouds, plus how you prove it. This matrix is the spine that ties the compliance and QoE obligations in the executive summary to the segmentation and NFRs above — and every later section of this document elaborates a row of it. Requirement IDs are stable references used throughout the design.

Req ID Requirement Compliance / driver Control Azure service AWS service Evidence / verify
R-01 Premium content protected end-to-end MovieLabs ECP; TPN/CDSA Private pipeline + multi-DRM + watermark Media Services + Key Vault + private origin MediaConnect/MediaPackage + SPEKE + KMS TPN assessment; no public origin
R-02 All premium assets multi-DRM encrypted Studio licensing; MovieLabs CMAF/CENC (cbcs+cenc); key rotation Packager + Key Vault MediaPackage/SPEKE + KMS DRM coverage report; key-rotation log
R-03 Viewer PII never traverses public internet GDPR; CCPA PE-only + deny public Private Endpoint + Policy PrivateLink + SCP No public IP on PII service
R-04 Billing is PCI-DSS compliant PCI-DSS Tokenisation + segmented PCI zone Segregated sub + tokenise Segregated account + tokenise PCI ROC; scope diagram
R-05 Consent captured; honour DSAR GDPR Art. 7/15–17; CCPA Consent store + DSAR pipeline B2C + Purview Cognito + Lake Formation DSAR SLA logs; consent record
R-06 Workforce and consumer identity separate Security; least-mix Two IdP planes Entra/Okta (staff) + B2C (viewers) Entra-fed IAM + Cognito Directory inventory; no shared app reg
R-07 EU viewer PII stays in EU GDPR Art. 44–49 Residency guardrail Policy allowedLocations SCP aws:RequestedRegion Policy compliance; region audit
R-08 Tier-1 RTO ≤15 m / RPO ≤1 m Business continuity Active/active + replication Front Door + Cosmos DB geo Route 53 + DynamoDB GT / Aurora Global Game-day RTO/RPO evidence
R-09 Playback survives a CDN failure QoE; availability Multi-CDN steering + origin shield Traffic Manager + steering SDK Route 53 + steering SDK CDN failover drill; steer logs
R-10 Meet VST/rebuffer/latency QoE targets Customer experience RUM + steering loop Azure Monitor + RUM SDK CloudWatch + RUM SDK QoE dashboard vs SLO
R-11 Enforce entitlement (plan/concurrency/geo/device) Anti-fraud; licensing Entitlement service + session token APIM + entitlement svc API Gateway + entitlement svc Concurrency test; geo-block test
R-12 Guardrails enforced, not optional SOC 2; governance Policy-as-code at root Azure Policy at lm MG SCP/RCP + Control Tower Deny-event logs; drift report
R-13 Streaming events durable + replayable Ad/QoE integrity; revenue Broker + DLQ + replay Event Hubs + Capture Kinesis + DLQ + archive 0 lost events; replay test
R-14 Single workforce identity lifecycle (JML) SOC 2 Entra hub + federation Entra Connect + Governance Federated to Entra IdP Joiner/leaver logs; access reviews
R-15 Central detection + response SOC 2; NIST IR SIEM/SOAR + CSPM/EDR Sentinel + Defender + Wiz/Falcon Security Hub + GuardDuty + Wiz/Falcon MTTD/MTTR; playbooks
R-16 Immutable, restorable backups Business continuity Tiered backup + immutability Azure Backup + immutable vault AWS Backup + Vault Lock Restore test; lock state
R-17 De-identify viewing data before analytics/reco GDPR minimisation De-id gate before analytics Data Factory + Purview Glue + Lake Formation De-id job logs; no PII in lake
R-18 Control content-sharing / credential fraud Anti-piracy; revenue Concurrency + device + watermark + bot B2C + WAF + watermark Cognito + WAF + watermark Fraud rate; concurrency alerts

Azure landing zone

Azure carries Lumina Media’s largest consumer-facing footprint — the CIAM token service at 120M-subscriber scale, the entitlement and multi-DRM license plane, the live-event and origin tier, the personalisation stack and the QoE lake — so its foundation is not a generic landing zone but the Azure Landing Zone (ALZ) accelerator with a direct-to-consumer streaming overlay, deployed through Azure Verified Modules (AVM) so the hierarchy itself is reproducible Bicep, not a console artefact nobody can rebuild during a live final. A Tenant Root Group anchors a management-group tree that pushes Azure Policy and RBAC down by inheritance — the single mechanism that lets a control authored once apply to every subscription beneath it. When you peak at 45M concurrent and run active/active across three regions, that inheritance is exactly what stops an encryption, content-isolation or residency rule from silently lapsing in some ad-tech subscription nobody is watching at kickoff. Directly beneath the tenant root sits the intermediate root management group lm, and under it four deliberately different policy postures: an lm-platform group for shared services, an lm-landingzones group for workloads, an lm-sandbox group for guarded experimentation where premium content and viewer PII are structurally forbidden, and an lm-decommissioned group for subscriptions being offboarded. The full tree is code:

Tenant Root Group
└── lm                              # intermediate root — regs + residency + content rules
    ├── lm-platform
    │   ├── lm-plat-identity        # Entra Connect cloud sync, PHS, Okta federation, private DNS, DC extension
    │   ├── lm-plat-connectivity    # Virtual WAN hubs, Azure Firewall Premium, dual ExpressRoute, multi-CDN egress
    │   ├── lm-plat-management       # Log Analytics, Azure Monitor, Backup / Recovery Services
    │   └── lm-plat-security        # Sentinel, Key Vault Managed HSM (content keys + CMK), break-glass, Defender
    ├── lm-landingzones
    │   ├── lm-lz-live              → -prod / -nonprod   (Tier-1 contribution, encode/package, LL-CMAF origin)
    │   ├── lm-lz-vod               → -prod / -nonprod   (Tier-1 per-title encode, VOD origin, 400 PB archive)
    │   ├── lm-lz-ciam              → -prod / -nonprod   (Tier-1 B2C 120M, entitlement, multi-DRM license)
    │   ├── lm-lz-reco              → -prod / -nonprod   (Tier-2 recommendations, feature store, A/B)
    │   ├── lm-lz-adtech            → -prod / -nonprod   (Tier-1 SSAI, ad-decision, VAST/VMAP, SCTE-35)
    │   ├── lm-lz-corp              → -prod / -nonprod   (Tier-2 SAP, billing/payments PCI CDE, HR)
    │   └── lm-lz-data              → -prod / -nonprod   (Tier-2 QoE ingest, lakehouse, ad-reporting)
    ├── lm-sandbox
    │   └── lm-lz-sandbox           # detached policy, egress-locked, content + PII denied by policy
    └── lm-decommissioned           # deny-all posture, pending deletion

The lm-platform group holds four dedicated platform subscriptions; the separation keeps shared services out of any single workload’s blast radius and lets each platform team run its own change cadence. Because Tier-1 delivery carries RTO ≤ 15 min and RPO ≤ 1 min — viewers must never see an outage — the platform subscriptions are themselves Tier-0 (RPO ≈ 0): if identity, DNS, the network control plane or the content-key custodian is down, nothing else can safely serve.

Platform subscription Region footprint What it holds Why it is isolated
lm-plat-identity East US 2 + West Europe Entra Connect cloud sync, Password Hash Sync, Seamless SSO, Okta federation for workforce SSO, private DNS resolver, extended AD DS domain controllers fronting corp.luminamedia.net Workforce identity is Tier-0; a workload compromise must never reach the directory plane, and CIAM stays entirely separate from it
lm-plat-connectivity East US 2, West Europe, Southeast Asia Virtual WAN secured hubs (/20 per region), Azure Firewall Premium, dual ExpressRoute gateways to LA + London MOCs, DDoS Network Protection, DNS Private Resolver, Bastion, controlled egress to the three commercial CDN origins Single central inspection and egress point; workloads never own their own internet path or CDN origin exposure
lm-plat-management East US 2 (US), West Europe (EU) Log Analytics workspaces (US + EU, residency-split), Azure Monitor, Automation, Update Manager, Recovery Services vaults, Dynatrace/ServiceNow connectors Observability and backup must survive a workload-subscription failure or a ransomware event
lm-plat-security East US 2 (US), West Europe (EU) Microsoft Sentinel, Key Vault Managed HSM (FIPS 140-3 Level 3) holding DRM content keys and CMK, the two break-glass identities, Defender for Cloud, Wiz + CrowdStrike Falcon signal ingest Security tooling and key custody — the crown jewels for premium-content protection — must survive a compromise of everything else

The lm-landingzones group then splits by streaming domain, not org chart, so a customer-facing live-event workload and an internal ad-reporting workload never share a guardrail set sized for the wrong risk. Each landing-zone family fans into -prod and -nonprod subscriptions, addressed out of the Azure super-net 10.20.0.0/12 with /22 spoke allocations and /26 private-endpoint subnets, and each carries a data-classification, criticality-tier and licensing-territory tag from birth.

Landing-zone subscription Parent MG Tier Sensitive data Home regions Env pattern Primary systems
lm-lz-live-* lm-lz-live Tier-1 Premium content East US 2 + West Europe + SE Asia (a/a) prod / nonprod SRT/RIST/Zixi contribution, multi-zone encode/package, LL-CMAF origin, SCTE-35, linear playout, blackout control
lm-lz-vod-* lm-lz-vod Tier-1 Premium content East US 2 + West Europe + SE Asia prod / nonprod Per-title encode, CMAF/HLS/DASH packaging, VOD origin, 400 PB tiered archive, catalog sync, cache warming
lm-lz-ciam-* lm-lz-ciam Tier-1 Viewer PII East US 2 + West Europe + SE Asia prod / nonprod Azure AD B2C (120M), token service, entitlement (sub + concurrency + geo + device), multi-DRM license, consent, DSAR
lm-lz-reco-* lm-lz-reco Tier-2 Viewer behaviour East US 2 + West Europe prod / nonprod Two-tower recommenders, feature store, A/B assignment, model serving
lm-lz-adtech-* lm-lz-adtech Tier-1 Limited PII East US 2 + West Europe + SE Asia prod / nonprod SSAI, ad-decision server, VAST/VMAP, personalised manifest, SCTE-35 conditioning
lm-lz-corp-* lm-lz-corp Tier-2 Cardholder (CDE) + HR PII East US 2 prod / nonprod SAP back office, billing/payments PCI CDE (tokenised), partner/affiliate settlement, HR, M365 adjacency
lm-lz-data-* lm-lz-data Tier-2 Viewer PII + QoE East US 2 + West Europe prod / nonprod QoE/RUM ingest (Conviva-class, tens of millions concurrent), hot/cold stores, lakehouse, ad-reporting

What makes this a streaming landing zone rather than a generic one is the control plane wrapped around the hierarchy, assigned at the lm and lm-landingzones scopes so every subscription beneath inherits it. The built-in PCI DSS v4.0, ISO/IEC 27001:2013 and SOC 2 regulatory-compliance initiatives run in audit posture for continuous evidence, while a custom lm-content-baseline initiative enforces the non-negotiables for premium content and viewer PII — private endpoints, customer-managed-key encryption, immutable (WORM) content storage and approved-region placement — with Deny and DeployIfNotExists effects. The assignment is Bicep at management-group scope, with a system-assigned identity so DINE remediations can run:

targetScope = 'managementGroup'

@description('Approved Azure regions for Lumina Media workloads (US / EU / APAC).')
param allowedLocations array = [ 'eastus2', 'westeurope', 'southeastasia' ]

@description('Built-in PCI DSS v4.0 initiative — resolve display name in the pipeline to avoid a stale GUID.')
param pciDssV4SetId string = '/providers/Microsoft.Authorization/policySetDefinitions/c676748e-3af9-4e22-bc28-50feed564afb'

// Custom content + PII baseline: private endpoints + CMK + WORM + region pinning (Deny / DINE)
resource contentBaseline 'Microsoft.Authorization/policyAssignments@2024-04-01' = {
  name: 'lm-content-baseline'
  location: 'eastus2'
  identity: { type: 'SystemAssigned' }          // required for DINE remediation tasks
  properties: {
    displayName: 'Lumina content + PII baseline — private endpoints, CMK, WORM, US/EU/APAC only'
    policyDefinitionId: tenantResourceId(
      'Microsoft.Authorization/policySetDefinitions', 'lm-content-baseline')
    enforcementMode: 'Default'                    // Deny/DINE actively enforced (not DoNotEnforce)
    parameters: { allowedLocations: { value: allowedLocations } }
  }
}

// Built-in PCI DSS v4.0 — audit posture, continuous evidence for the billing CDE
resource pciDss 'Microsoft.Authorization/policyAssignments@2024-04-01' = {
  name: 'lm-pci-dss-v4'
  properties: {
    displayName: 'PCI DSS v4.0'
    policyDefinitionId: pciDssV4SetId
    enforcementMode: 'Default'
  }
}

Application subscriptions are never hand-built. A subscription vending factory — the AVM avm/ptn/lz/sub-vending pattern — places each new subscription under the correct management group, inherits policy, assigns RBAC, applies the cost, data-classification, tier and licensing-territory tags, peers it to the regional Virtual WAN hub, and enrols it in logging and Defender before a single workload resource exists. A live-delivery production environment arrives already governed, well inside the vend in under one business day target — the gated self-service discipline described in ServiceNow-gated cloud provisioning of self-service landing zones:

targetScope = 'tenant'

module liveProd 'br/public:avm/ptn/lz/sub-vending:0.3.0' = {
  name: 'vend-lm-lz-live-prod'
  params: {
    subscriptionAliasName:         'lm-lz-live-prod'
    subscriptionDisplayName:       'lm-lz-live-prod'
    subscriptionBillingScope:      billingScope
    subscriptionManagementGroupId: 'lm-lz-live'             // inherits lm-content-baseline
    subscriptionTags: {
      dataClass: 'Content', tier: 'Tier-1', residency: 'US'
      licensingTerritory: 'NA', costCenter: 'LIVE-01'
    }
    virtualNetworkEnabled:         true
    virtualNetworkAddressSpace:    [ '10.20.16.0/22' ]      // /22 spoke from 10.20.0.0/12
    virtualNetworkPeeringEnabled:  true
    hubNetworkResourceId:          hubVwanEastUs2Id          // peer to regional hub
  }
}

Several vending inputs are load-bearing rather than cosmetic — each one either drives a policy decision or wires the subscription into the platform, so the environment is compliant the moment it exists.

Vending input Example value What it drives
subscriptionManagementGroupId lm-lz-live Placement under the right MG → inherits lm-content-baseline Deny/DINE
subscriptionTags.dataClass Content | PII | CDE Policy predicate that forces private endpoints, CMK and WORM on the matching resources
subscriptionTags.residency US | EU | APAC Selects paired region set and residency-split Log Analytics workspace
subscriptionTags.licensingTerritory NA | EMEA | APAC Drives geo-blackout and content-territory guardrails for rights-limited titles
subscriptionTags.tier Tier-1 Sets backup RPO/RTO policy and Defender plan tier
virtualNetworkAddressSpace 10.20.16.0/22 /22 spoke from the 10.20.0.0/12 super-net; non-overlapping by IPAM
hubNetworkResourceId regional Virtual WAN hub Auto-peers the spoke; forces egress through Azure Firewall Premium

Two matter most: dataClass=Content is what the lm-content-baseline policies key on to force private endpoints, CMK content keys and immutable storage, while residency and licensingTerritory select the paired region set and which titles the subscription may serve. This mirrors the enterprise-scale landing zone management-group design but tightens it for premium content and 120M-subscriber PII.

The lm tree vends and pushes policy left-to-right into platform then landing-zone subscriptions; every arrow is one-way inheritance a workload can build within but never weaken.

Azure Media Landing Zone — lm management groups, platform subscriptions and live/vod/ciam/reco/adtech landing-zone subscriptions

AWS landing zone

AWS mirrors the same intent through AWS Organizations with a multi-account model delivered by Control Tower and Account Factory for Terraform (AFT) — the same philosophy (accounts as the unit of isolation, guardrails inherited from above) in that cloud’s native primitives. A management account sits at the apex purely as the org root and billing anchor, deliberately empty of workloads. Beneath it the organisation is partitioned into organisational units whose boundaries follow blast-radius and audit lines, not the org chart: the Security OU isolates the accounts that must survive a compromise of everything else; the Infrastructure OU carries the shared network and services (including SPEKE-based DRM key origination); and workloads split by streaming domain into Prod and NonProd child OUs, with a detached Sandbox OU where premium content and viewer PII are denied outright. Accounts follow the lm-<purpose>-<env> convention, addressed from the AWS super-net 10.40.0.0/12:

Root (lm Organization)
├── Security OU
│   ├── lm-logarchive-prod        # org CloudTrail sink → S3 Object Lock (compliance mode)
│   ├── lm-audit-prod             # AWS Config aggregator, Security Hub delegated admin
│   └── lm-sectooling-prod        # GuardDuty, Macie, Detective, Inspector, Wiz + Falcon delegated admin
├── Infrastructure OU
│   ├── lm-network-prod           # Transit Gateway, Network Firewall, ingress/egress + multi-CDN origin VPCs
│   └── lm-sharedsvcs-prod        # IAM Identity Center, AD Connector, private DNS, ECR, SPEKE key service
└── Workloads OU
    ├── Live    → lm-live-prod    / lm-live-nonprod      (Tier-1, MediaLive encode/package, origin, SSAI)
    ├── VOD     → lm-vod-prod     / lm-vod-nonprod       (Tier-1, MediaConvert, S3 origin 400 PB, CloudFront)
    ├── Data    → lm-data-prod    / lm-data-nonprod      (Tier-2, Kinesis QoE ingest, lakehouse, reco ML)
    ├── AdTech  → lm-adtech-prod  / lm-adtech-nonprod    (Tier-1, SSAI, ad-decision, VAST/VMAP)
    ├── Corp    → lm-corp-prod    / lm-corp-nonprod      (Tier-2, billing PCI CDE, partner settlement)
    └── Sandbox OU (detached)  → lm-sandbox-*             (SCP: deny content/media services + regions)

The Security OU is the estate’s spine. lm-logarchive-prod holds the tamper-evident record: the org-wide CloudTrail trail lands in S3 with Object Lock (compliance mode), write-once and delete-proof even for the account owner, satisfying SOC 2 and PCI DSS retention. lm-audit-prod aggregates AWS Config and runs Security Hub as delegated administrator; lm-sectooling-prod runs GuardDuty, Macie (which discovers viewer PII in S3), Detective and Inspector across every account, forwarding to the cross-cutting Wiz + CrowdStrike Falcon estate. The Infrastructure OU’s lm-network-prod owns the regional Transit Gateway and AWS Network Firewall, so live, VOD and ad-tech VPCs are segmented and every east-west and egress flow — including the controlled path to the three CDN origins — is inspected, never peered directly.

Account OU Purpose Sensitive data Home region(s) CIDR (primary)
lm-management Root Billing, org root, Control Tower home — no workloads No us-east-1 n/a
lm-logarchive-prod Security Immutable CloudTrail / Config log sink (Object Lock) Metadata us-east-1 10.40.0.0/24
lm-audit-prod Security Config aggregator, Security Hub, Audit Manager No us-east-1 10.40.1.0/24
lm-sectooling-prod Security GuardDuty, Macie, Detective, Inspector, Wiz/Falcon Findings us-east-1 10.40.2.0/24
lm-network-prod Infrastructure Transit Gateway, Network Firewall, ingress/egress + CDN-origin VPCs In transit us-east-1, eu-west-1, ap-southeast-1 10.40.16.0/20
lm-sharedsvcs-prod Infrastructure IAM Identity Center, AD Connector, private DNS, ECR, SPEKE DRM key service No us-east-1 10.40.32.0/20
lm-live-prod Workloads/Live/Prod Elemental MediaLive encode/package, LL-CMAF origin, SSAI Premium content us-east-1 + eu-west-1 + ap-southeast-1 (a/a) 10.41.0.0/16
lm-vod-prod Workloads/VOD/Prod MediaConvert per-title encode, S3 origin (400 PB), CloudFront Premium content us-east-1 + eu-west-1 + ap-southeast-1 10.42.0.0/16
lm-data-prod Workloads/Data/Prod Kinesis QoE ingest, lakehouse, recommendation ML, Cognito CIAM surfaces Viewer PII us-east-1 + eu-west-1 10.43.0.0/16
lm-adtech-prod Workloads/AdTech/Prod SSAI, ad-decision server, VAST/VMAP, SCTE-35 conditioning Limited PII us-east-1 + eu-west-1 + ap-southeast-1 10.44.0.0/16
lm-corp-prod Workloads/Corp/Prod Billing/payments CDE (tokenised), partner/affiliate settlement Cardholder (CDE) us-east-1 10.45.0.0/16

Guardrails on the AWS side are Service Control Policies that inherit down the OU tree, expressing exactly the controls the Azure hierarchy enforces on the other cloud: approved regions only, no public exposure of content or PII stores, mandatory customer-managed KMS encryption, premium-content isolation with immutable storage, and an unbreakable path from every account to the immutable Log Archive. The OU tree, its SCPs and the accounts are all Terraform — an application account is never a manual ticket but a reviewed pull request through AFT that yields an account already wired to centralised logging, Security Tooling enrolment, a Transit Gateway attachment, IAM Identity Center federation brokered by Entra + Okta, approved-region settings and baseline KMS keys, the same discipline detailed in multi-account AWS governance with Control Tower. The region-deny SCP is the sharpest example, careful to exempt the global services that have no regional endpoint:

data "aws_iam_policy_document" "region_deny" {
  statement {
    sid    = "DenyOutsideApprovedRegions"
    effect = "Deny"
    # global / control-plane services must stay reachable from any region
    not_actions = [
      "iam:*", "organizations:*", "sts:*", "route53:*", "cloudfront:*",
      "waf:*", "wafv2:*", "shield:*", "support:*", "health:*", "kms:CreateKey",
    ]
    resources = ["*"]
    condition {
      test     = "StringNotEquals"
      variable = "aws:RequestedRegion"
      values   = ["us-east-1", "eu-west-1", "ap-southeast-1"]   # US / EU / APAC delivery
    }
  }
}

resource "aws_organizations_policy" "region_deny" {
  name    = "lm-scp-region-deny"
  type    = "SERVICE_CONTROL_POLICY"
  content = data.aws_iam_policy_document.region_deny.json
}

resource "aws_organizations_policy_attachment" "region_deny_root" {
  policy_id = aws_organizations_policy.region_deny.id
  target_id = aws_organizations_organization.lm.roots[0].id   # inherits to every OU
}

The encryption SCP rides alongside it, denying any s3:PutObject that is not aws:kms-encrypted and blocking disabling of account-level S3 Block Public Access, so a workload account physically cannot create a plaintext or internet-exposed store. A dedicated content-protection SCP on the Live/VOD OUs forces Object Lock on mezzanine buckets and blocks cross-account content sharing off an allow-list — MovieLabs/TPN isolation made structural, not procedural. Below the SCP floor, Control Tower controls and Config conformance packs add framework-specific rules, evaluated against named PCI and content-security baselines.

SCP / control Attached at Effect Streaming purpose
lm-scp-region-deny Root Deny actions outside us-east-1/eu-west-1/ap-southeast-1 Residency + content-licensing territory, structurally
lm-scp-require-kms Root Deny unencrypted S3/EBS/RDS; force SSE-KMS CMK Content-at-rest, PII and CDE encryption
lm-scp-deny-public Workloads Deny disabling S3 BPA; deny public RDS/ELB No mezzanine, origin or PII store ever exposed to the internet
lm-scp-content-tpn Workloads/Live + VOD Force Object Lock on mezzanine; deny cross-account content share off allow-list MovieLabs/TPN premium-content isolation
lm-scp-cde-isolation Workloads/Corp Restrict CDE account to PCI-approved services; deny lateral trust PCI-DSS segmentation of billing/payments
lm-scp-protect-guardrails Root Deny leaving org, disabling GuardDuty/Config/CloudTrail Detective controls cannot be silenced by a workload team
lm-scp-sandbox-lock Sandbox Deny MediaLive/MediaConvert/S3 content buckets + all regions but us-east-1 Sandbox may never touch premium content or PII
Control Tower — CloudTrail enabled Root (mandatory) Detective Every account streams to the immutable Log Archive

Control Tower governs the lm Organizations root; SCPs inherit down the OU tree into the Security, Infrastructure and per-domain workload accounts, and AFT vends every account pre-governed.

AWS Media Landing Zone — Organizations OU tree with Security, Infrastructure and Live/VOD/Data/AdTech/Corp workload accounts governed by Control Tower and SCPs

Governance hierarchy and policy inheritance

The two landing zones look different on the surface — Azure management groups on one side, AWS organisational units on the other — but they are deliberately governed by one guardrail set compiled into both, the load-bearing decision of the entire foundation. Lumina cannot afford two divergent control estates audited separately, because a TPN assessment, a PCI QSA audit and a SOC 2 Type II examination each ask whether the same control holds everywhere content and viewer PII live, on either provider. So the controls are authored once, as policy-as-code in Terraform and Bicep, and rendered into each cloud’s native enforcement: Azure Policy and RBAC that inherit down the management-group tree, and Service Control Policies that inherit down the AWS OU tree. The module library is the canonical artefact; the cloud-specific assignments are compiled outputs of it. Six shared guardrails travel down both hierarchies as the non-negotiable floor every subscription and account inherits.

Shared guardrail Azure mechanism AWS mechanism Effect
Approved regions / residency only Allowed locations policy at lm (US/EU/APAC set) lm-scp-region-deny at Root Deny (preventive)
No public exposure of content / PII Custom Deny on publicNetworkAccess + built-in Private Link audit lm-scp-deny-public (S3 BPA, public RDS/ELB) Deny (preventive)
Private endpoints for PaaS DeployIfNotExists private endpoint + private DNS VPC interface endpoints + PrivateLink baseline (AFT) DINE / provisioned
CMK & content-key encryption DINE binds Storage/SQL/KV to Managed HSM key lm-scp-require-kms forces SSE-KMS CMK Deny + DINE
Immutable audit logging DINE diagnostic settings → immutable Log Analytics / storage Org CloudTrail → S3 Object Lock (compliance mode) DINE / provisioned
Premium-content isolation (TPN) Deny public content storage + WORM + no cross-tenant share lm-scp-content-tpn + S3 Object Lock Deny + DINE

Inheritance lets this operating model scale without growing a control team in proportion to the estate. Platform engineers change a guardrail in one module, and on the next pipeline run every subscription and account beneath both trees conforms — no fleet of environments to revisit by hand, no window in which half the estate runs an old rule. Crucially the relationship is one-directional: teams build freely inside the guardrails but cannot weaken a control handed down from above. A live-operations team cannot grant itself an unapproved region during an event scramble, and an ad-tech owner cannot open a personalised-manifest store to the internet, because the denial is asserted higher in the tree than they can edit. That asymmetry — full delivery autonomy inside a floor a single misconfigured workload can never sink — is precisely what a 45M-concurrent Tier-1 estate needs.

The enforcement verbs matter as much as the rules, and streaming uses all of them. Azure Policy’s effect model (covered in Azure Policy effects: Deny, Audit, Modify, DeployIfNotExists) maps cleanly onto the AWS side, and the choice of verb is a choice about when the control acts.

Effect What it does Azure use AWS analogue Streaming example
Deny Blocks a non-compliant create/update outright Deny effect SCP Deny statement Reject a public-facing origin or mezzanine storage account at create time
DeployIfNotExists Auto-provisions the missing control DeployIfNotExists AFT baseline / Config remediation Create the private endpoint + DNS record for a new CMAF packager
Modify Adds/changes a property to conform Modify Config auto-remediation (SSM) Stamp dataClass=Content tag; enforce HTTPS-only on origin storage
Audit Flags drift without blocking Audit / AuditIfNotExists Config rule (non-blocking) Report any content bucket without Object Lock enabled
DenyAction Blocks a specific operation (e.g. delete) DenyAction SCP deny on s3:DeleteObject Prevent deletion of an immutable master/mezzanine object under legal hold

The custom lm-content-baseline initiative is where these verbs become concrete. Its Deny arm rejects any Content-tagged storage account that leaves public network access enabled or uses a platform-managed key; its DINE arm provisions the private endpoint the workload team forgot. A compact custom definition shows the shape — note the tags[dataClass] predicate that scopes enforcement to premium content only:

resource denyPublicContentStorage 'Microsoft.Authorization/policyDefinitions@2024-04-01' = {
  name: 'lm-deny-public-content-storage'
  properties: {
    policyType:  'Custom'
    mode:        'All'
    displayName: 'Deny public network access on premium-content storage accounts'
    metadata:    { category: 'Lumina Content' }
    policyRule: {
      if: {
        allOf: [
          { field: 'type', equals: 'Microsoft.Storage/storageAccounts' }
          { field: 'tags[dataClass]', equals: 'Content' }
          { anyOf: [
            { field: 'Microsoft.Storage/storageAccounts/publicNetworkAccess', notEquals: 'Disabled' }
            { field: 'Microsoft.Storage/storageAccounts/encryption.keySource', notEquals: 'Microsoft.Keyvault' }
          ] }
        ]
      }
      then: { effect: 'deny' }        // no content store may be public OR platform-key encrypted
    }
  }
}

The one place inheritance bends is break-the-glass and live-event freeze. A single misapplied Deny can turn an emergency-access account into a locked-out account at exactly the wrong moment — the middle of a live final — so the two break-glass identities and the event-day emergency path are covered by a narrow, audited policy exemption at the lm-plat-security scope, time-boxed and alerting on every use, rather than a blanket carve-out. DINE remediation itself runs through a managed identity, the pattern detailed in Azure Policy remediation tasks with managed identity. The initiatives assigned at each scope build the posture up in layers, most general at the root and most specific at the workload.

Scope Assigned initiative(s) Posture
lm (intermediate root) Microsoft Cloud Security Benchmark; Allowed locations (US/EU/APAC) Baseline + residency, all subs
lm-landingzones PCI DSS v4.0; ISO 27001:2013; SOC 2 (audit) Continuous framework evidence
lm-lz-live / -vod lm-content-baseline (Deny + DINE) Private endpoints, CMK, WORM, TPN isolation
lm-lz-ciam lm-ciam-baseline + consent / DSAR audit B2C hardening, PII private-only, consent gate
lm-lz-corp (billing) lm-pci-cde-baseline CDE segmentation, tokenisation, no PAN at rest
lm-sandbox lm-deny-content-services Content + PII services denied; egress-locked

Controls are authored once and compiled into Azure Policy and AWS SCPs; each arrow is one-way inheritance from root to workload, where Deny is preventive, DINE remediates and Audit reports.

Governance hierarchy and policy inheritance — root to platform to landing zone to workload, with Deny, DeployIfNotExists and Audit effects across Azure and AWS

Compliance, content security and data protection

Lumina Media answers not to one regulator but to an overlapping mesh of them, and the architecture must satisfy all of them at once without forking into a dozen incompatible estates. The control plane is therefore designed so that a single set of controls maps to many drivers simultaneously — one encryption model serving PCI DSS, GDPR, CCPA and MovieLabs at once; one segmentation boundary serving both the PCI cardholder data environment and the TPN content pipeline. The regulatory reality is explicit: PCI DSS v4.0 for the billing/payment path; GDPR (Schrems II, transfer-impact) for EU viewer data in West Europe and CCPA/CPRA for California residents; the MovieLabs Enhanced Content Protection (ECP) specification and the Trusted Partner Network (TPN) / CDSA assessment for premium and pre-release content; SOC 2 Type II for service assurance; and DPP broadcast-security expectations for the contribution and playout chain. A four-band data classification underpins the mapping — the band an asset carries determines its encryption, residency, key custody and access controls.

Data class Examples Residency / territory rule Enforcement
Restricted — Premium content Pre-release masters, mezzanine, live contribution feeds, VOD library, DRM content keys Region + licensing-territory pinned; no plaintext on general-purpose storage Private-only, WORM masters, CMK/content-key in Managed HSM/CloudHSM, forensic watermark, TPN-assessed pipeline
Restricted — Cardholder (CDE) PAN, payment tokens, billing records PCI-scoped segment; tokenised at the edge so PAN never lands at rest Ring-fenced CDE, SSE-KMS CMK, no PAN storage, quarterly ASV scan
Confidential — Viewer PII / consent Account, email, watch history, device, geo, consent state Resident in data-subject jurisdiction; transfer-impact assessed Approved-region guardrails, CMK, Purview/Macie classification, consent capture + DSAR/erasure
Internal / Public Catalog metadata, artwork, public marketing No residency constraint; integrity-protected Standard encryption, edge WAF, signed URL for rights-limited artwork

The control matrix below is the operational heart of the posture: it maps each control domain to Lumina’s named control, to the Azure service and AWS service that implement it, and to the policy that enforces it — so no control is orphaned and no framework unmapped. Every row is defensible to an auditor with a real tool and enforcement point, not a policy aspiration.

Control domain Lumina control Azure service AWS service Enforcing policy / SCP
Content at rest CMK + content keys, WORM masters, no plaintext Key Vault Managed HSM + Storage CMK + immutable blob KMS/CloudHSM + SSE-KMS + S3 Object Lock lm-content-baseline DINE + lm-scp-require-kms
Content in transit TLS 1.2+, signed URL/JWT at edge, private origin Private Link + Front Door token auth PrivateLink + CloudFront signed URL/OAC Deny publicNetworkAccess + lm-scp-deny-public
DRM & key service Multi-DRM (Widevine/FairPlay/PlayReady), CENC, key rotation Media key service + Managed HSM SPEKE + KMS content keys Content-key policy; deny plaintext key export
Forensic watermarking Session + A/B watermark on premium / pre-release Packager watermark integration MediaConvert / partner watermark + SSAI TPN control set; lm-content-baseline audit
Cardholder data (CDE) Tokenise at edge, no PAN at rest, minimised scope API Management + tokenisation + isolated spoke API Gateway + payment tokenisation + isolated VPC lm-pci-cde-baseline + lm-scp-cde-isolation
Consumer identity (CIAM) 120M B2C/Cognito, JWT, bot/credential-stuffing defence Azure AD B2C + Front Door WAF/bot Amazon Cognito + WAF Bot Control CIAM baseline; WAF rate-limit policy
Consumer PII & consent Consent capture, DSAR, right-to-erasure Purview + B2C + consent store Macie + Cognito + consent store GDPR/CCPA initiative; residency guardrail
Identity & access (workforce) Entra + Okta hub, phishing-resistant MFA, zero standing privilege Entra ID + PIM + Conditional Access IAM Identity Center (Entra/Okta federated) CA policies + permission sets, Audit no-MFA
Network segmentation Segmented live/vod/ciam/adtech, default-deny, central inspection Azure Firewall Premium + NSG Network Firewall + Security Groups UDR-to-firewall policy + TGW route tables
Audit logging Immutable, centralised, framework-aligned retention Sentinel + immutable Log Analytics/storage CloudTrail → S3 Object Lock DINE diagnostic settings + lm-scp-protect-guardrails
Data residency / territory US/EU/APAC pinned, licensing-territory geo Allowed locations policy lm-scp-region-deny Deny outside approved region set
Threat detection 24×7 SOC, cross-cloud, credential-sharing fraud Defender for Cloud + Sentinel + Wiz/Falcon GuardDuty + Security Hub + Macie Config/Defender enabled by policy; deny disable
Multi-CDN edge security WAF/bot/DDoS, signed URL/token, origin shield Front Door Premium WAF + DDoS CloudFront + WAF + Shield Advanced Edge WAF policy; token-auth at the edge

The billing path is where PCI DSS bites hardest, and the decisive move is scope minimisation: card data is tokenised at the edge by the payment provider so the PAN never lands at rest in Lumina’s estate, shrinking the cardholder data environment to a ring-fenced spoke inside lm-lz-corp / lm-corp-prod — the segmentation-first discipline described in PCI-DSS cardholder data environment segmentation on AWS. Each PCI requirement lands on a named, enforced control on both clouds.

PCI DSS v4.0 area Lumina implementation Azure enforcement AWS enforcement
Req 1 — Segment the CDE Isolated corp CDE spoke, default-deny, no flat network NSG/UDR + Azure Firewall Premium Security Groups + Network Firewall + lm-scp-cde-isolation
Req 3 — Protect stored data No PAN at rest; tokenise; CMK on residual data Managed HSM CMK on storage/SQL KMS CMK + SSE-KMS
Req 4 — Encrypt transmission TLS 1.2+, private paths only Front Door / API Management TLS API Gateway TLS + PrivateLink
Req 6 — Secure development DevSecOps supply chain, SAST/DAST, IaC scan Defender for DevOps pipeline gates CodeBuild + Wiz Code gates
Req 8 — Identify & authenticate Phishing-resistant MFA on all CDE admin access Entra CA + PIM elevation IAM Identity Center MFA + permission sets
Req 10 — Log & monitor Immutable CDE audit trail, daily review Sentinel + immutable Log Analytics CloudTrail Object Lock + Config
Req 11 — Test security Quarterly ASV scan, annual pen test, IDS Defender for Cloud + ASV GuardDuty + Inspector + ASV

Premium content is the other high-stakes domain, governed to the MovieLabs ECP specification and the TPN assessment rather than a generic baseline — the studios that license Lumina’s content demand it. The controls run the full pipeline from contribution through encode, package, storage, edge delivery and partner exchange, mapping to concrete services just as the privacy and payment controls do and extending the multi-DRM model detailed in video-on-demand streaming with multi-DRM on AWS.

MovieLabs ECP / TPN control Lumina implementation Enforcement
Hardware root of trust & output control Widevine L1 / FairPlay / PlayReady SL3000, HDCP 2.2+ for UHD/4K Multi-DRM license service enforces robustness + HDCP rules per session
No plaintext content on general storage Encrypted mezzanine + content keys; WORM masters Managed HSM/CloudHSM content keys; S3 Object Lock; deny public bucket
Forensic watermarking Session-based + A/B variant watermark on 4K / pre-release Packager/SSAI watermark; a leak traces back to a subscriber session
Key & title diversity, rotation Per-title / per-session keys, CENC (cenc + cbcs), rotation SPEKE / media key service, KMS-backed, no plaintext key export
Secure processing environment (TPN) TPN-assessed encode/package env, least-privilege, segmented Isolated live/vod accounts, private-only, MFA, fully logged
Partner / affiliate exchange Signed, encrypted, time-boxed content delivery Private share + KMS grant + expiry; no standing cross-account access

Two crosswalks prove the “one control, many drivers” claim explicitly. A single encryption control answers PCI Req 3/4, GDPR Art. 32, CCPA reasonable-security and MovieLabs ECP storage at once; a single immutable-audit control answers PCI Req 10, GDPR breach records and TPN logging together — the design never forks into a separate estate per regulation.

Lumina control PCI DSS v4.0 GDPR CCPA / CPRA MovieLabs / TPN SOC 2
CMK / content-key encryption Req 3 / 4 Art. 32 §1798.150 reasonable security ECP secure storage CC6.1
Private-endpoint-only access Req 1 Art. 32 Reasonable security ECP no-public-content CC6.6
Immutable audit trail Req 10 Art. 30 / 33 Breach records TPN logging CC7.2
Region / territory guardrail (CDE locality) Art. 44–49 (transfers) Licensing-territory control CC6.1
Consent capture + DSAR / erasure Art. 6 / 7 / 17 §1798.100–.130 Privacy (P) series
Forensic watermark / anti-piracy ECP watermarking CC6.1

Data residency closes the loop between classification and enforcement, and the decisive property is that it is enforced structurally, not by policy memo. The approved-region guardrails inherited down both trees mean a workload simply cannot place restricted content or EU viewer PII outside its geography: US data is pinned to East US 2 / us-east-1, EU data to West Europe / eu-west-1, APAC to Southeast Asia / ap-southeast-1, and the residency-split workspaces and separate CMK scopes mean even the telemetry and keys respect the boundary. On top of residency sits licensing territory — a title licensed only for EMEA cannot be packaged or served from a North American origin, because the licensingTerritory tag drives a geo-blackout guardrail the packager and the multi-CDN steering layer both honour. Content crossing to a partner passes through a signed, encrypted, time-boxed exchange with no standing access before it leaves its home segment — so Lumina can answer the two hardest questions a studio or regulator asks: where does this master live and who can decrypt it, and where does my subscriber’s data live and who can read it. The frameworks map to one control, the control maps to a named service on each cloud, and the enforcement is a policy an auditor can inspect running.

A regulatory driver maps to a control domain, then to a named Azure service and its AWS parity, then to the policy or SCP that proves it — one control answering many frameworks at once.

Compliance and content-security control mapping — PCI-DSS, GDPR, CCPA and MovieLabs/TPN mapped to Azure and AWS controls and their enforcing policies

Global hybrid connectivity

Everything private that Lumina Media runs — live contribution from a stadium, playout out of a Media Operations Centre, an entitlement lookup for one of 120M subscribers, a DRM key request during a live final — rides through one small, deliberately boring, ruthlessly redundant core. Get this core wrong and you do not get a slow site; you get a black screen for 45M concurrent viewers during the one event they will remember. So the connectivity design has exactly two jobs: carry contribution and control traffic on private circuits that never touch the public internet, and make sure no single circuit, carrier, cloud region or even whole cloud can isolate a delivery region. Delivery itself (segments, manifests, images) rides the multi-CDN edge and is not in scope here; this is the private substrate underneath it.

The physical anchors are the two broadcast facilities: the Los Angeles MOC (Americas contribution, live sports/news ingest, near-line archive) and the London MOC (EMEA playout and post). Each MOC is dual-homed into both clouds — ExpressRoute into Azure and Direct Connect into AWS — on physically diverse fibre from different meet-me rooms. There is no third MOC in Asia; Southeast Asia and ap-southeast-1 are reached over the cloud backbones (Virtual WAN hub-to-hub and Transit Gateway inter-region peering), so a fibre cut near a MOC degrades throughput but never strands a region.

The two MOCs land on ExpressRoute and Direct Connect, which feed Azure Virtual WAN and AWS Transit Gateway, which fan out to the three regions on each cloud.

Two on-prem Media Operations Centres in Los Angeles and London home into both clouds over ExpressRoute and Direct Connect, feeding Azure Virtual WAN and AWS Transit Gateway which each fan out to three active/active regions

The circuit inventory is small on purpose — fewer moving parts, each one doubled:

| Path | Cloud | Service | Speed / SKU | Peering | Terminates at | Carries | | — | — | — | — | — | — | | LA MOC → East US 2 | Azure | ExpressRoute (Premium) | 2× 10 Gbps | Private peering + FastPath | vWAN ER gateway, vhub-eus2 | Americas contribution, control-plane | | LA MOC → us-east-1 | AWS | Direct Connect (dedicated) | 2× 10 Gbps, MACsec | Transit VIF → DX gateway | tgw-use1 | Americas encode/archive, control-plane | | London MOC → West Europe | Azure | ExpressRoute (Premium) | 2× 10 Gbps | Private peering + FastPath | vWAN ER gateway, vhub-weu | EMEA playout, control-plane | | London MOC → eu-west-1 | AWS | Direct Connect (dedicated) | 2× 10 Gbps, MACsec | Transit VIF → DX gateway | tgw-euw1 | EMEA post/archive, control-plane | | LA ↔ London (Azure) | Azure | ExpressRoute Global Reach | via circuits | private | both ER circuits | MOC-to-MOC failover, archive sync | | LA ↔ London (AWS) | AWS | Direct Connect SiteLink | via DX | private | both DX connections | MOC-to-MOC over AWS backbone | | SEA / ap-southeast-1 | both | Backbone only | n/a | vWAN hub-to-hub, TGW peering | via EUS2/WEU + use1/euw1 | Asia delivery, cross-region replication |

Two design choices in that table earn their keep. FastPath on ExpressRoute lets contribution traffic bypass the Virtual WAN hub’s gateway data path and go straight to the spoke, shaving the added latency for live encode ingest to under 8 ms — which matters when your live-latency budget (LL-CMAF glass-to-glass) is 8 seconds end to end and every hop counts. SiteLink on Direct Connect lets the two MOCs talk to each other over the AWS global backbone without backhauling through a cloud region, so archive replication between LA and London does not consume a region’s egress.

The routing is BGP everywhere, with a private ASN plan that keeps the two estates unambiguous. Advertise the on-prem 10.0.0.0/12 from both MOCs (with AS-path prepending so each MOC is primary for its own region and backup for the other), and accept the cloud supernets back:

Endpoint ASN Advertises Accepts Notes
LA MOC edge routers 65010 10.0.0.0/12 (10.0/16 no-prepend) 10.20.0.0/12, 10.40.0.0/12 Primary for Americas
London MOC edge routers 65011 10.0.0.0/12 (10.1/16 no-prepend) 10.20.0.0/12, 10.40.0.0/12 Primary for EMEA
Azure MSEE (private peering) 12076 10.20.0.0/12 10.0.0.0/12 Microsoft-owned; FastPath on
AWS TGW / DX gateway 64512 10.40.0.0/12 10.0.0.0/12 Allowed prefixes on DX gateway
vWAN hub-to-hub internal region /16 region /16 Any-to-any across 3 hubs
TGW inter-region peering internal region /16 region /16 Static routes per peer

A subtle but critical rule: the two clouds never learn each other’s routes through on-prem. If Azure could reach AWS via MOC → ER → BGP → DX → AWS, a routing leak would send Tier-1 traffic hairpinning through a broadcast facility. On-prem only ever originates 10.0.0.0/12; it does not re-advertise one cloud’s prefixes to the other. Cross-cloud private traffic (rare — mostly analytics replication) goes over a dedicated, inspected path, never accidental transit.

Every private hop is encrypted, because “it’s a private circuit” is not a control an auditor (or the MovieLabs/TPN content-security regime) accepts:

Segment Encryption Mechanism Why
MOC ↔ ExpressRoute MACsec (ER Direct) or IPsec-over-ER Layer-2 or VPN tunnel over private peering Contribution feeds carry pre-release premium content
MOC ↔ Direct Connect MACsec 802.1AE on the dedicated port Same premium-content requirement
vWAN hub-to-hub Microsoft backbone (encrypted) native Inter-region control + replication
TGW inter-region peering AWS backbone (encrypted) native Inter-region control + replication
Contribution transport SRT / Zixi / RIST with AES-128/256 app-layer over the circuit Defence in depth; survives a mis-scoped BGP change

az and aws to stand up the LA pair of circuits — one command each, then the peerings that make them useful:

# Azure — ExpressRoute at the LA meet-me room into East US 2, private peering + FastPath
az network express-route create \
  --resource-group lm-plat-connectivity-rg --name er-lax-eus2 --location eastus2 \
  --provider "Equinix" --peering-location "Los Angeles" \
  --bandwidth 10000 --sku-family MeteredData --sku-tier Premium
az network express-route peering create \
  --resource-group lm-plat-connectivity-rg --circuit-name er-lax-eus2 \
  --peering-type AzurePrivatePeering --peer-asn 65010 --vlan-id 100 \
  --primary-peer-subnet 169.254.10.0/30 --secondary-peer-subnet 169.254.10.4/30
# FastPath is enabled on the connection object that binds the circuit to the vWAN ER gateway.

# AWS — dedicated Direct Connect at the LA DX location, transit VIF into the DX gateway → TGW
aws directconnect create-connection --location EqLA3 \
  --bandwidth 10Gbps --connection-name dx-lax-use1 --request-macsec-capable
aws directconnect create-transit-virtual-interface --connection-id dxcon-abc123 \
  --new-transit-virtual-interface \
  '{"virtualInterfaceName":"vif-lax-tgw","vlan":200,"asn":65010,
    "directConnectGatewayId":"dxgw-0a1b2c","mtu":9001}'

Jumbo frames (MTU 9001 on the transit VIF, 1500 clamped where the path can’t guarantee it) matter for the encode/archive bulk transfers; get it wrong and you pay a fragmentation tax on every 400 PB you move. With the core in place, each cloud’s regional topology hangs off it.

Azure regional network

Every Azure region is the same shape — a secured Virtual WAN hub with six single-purpose spokes hanging off it — which is the whole point: build East US 2 once, then for region in weu sea and change three variables. The hub is a /20, the spokes are /22, and a /26 private-endpoint subnet lives inside each spoke. We use Virtual WAN with Routing Intent rather than hand-rolled hub-and-spoke because Routing Intent is what forces all spoke egress and all east-west traffic through Azure Firewall without you maintaining route tables by hand across three regions. (The trade-offs behind that choice are laid out in Hub-Spoke vs Virtual WAN enterprise topology.)

The ExpressRoute on-ramp lands in the hub; Routing Intent points a default and an RFC1918 route at Azure Firewall Premium; inspected traffic reaches the workload spokes; and each spoke’s private-endpoint /26 is where PaaS lives.

An Azure region: the ExpressRoute gateway feeds a secured Virtual WAN hub running Azure Firewall Premium, which inspects traffic into media-processing, customer-API and analytics spokes, each with a private-endpoint subnet

The hub is not a workload zone — it is fixed, reserved infrastructure with Azure’s required subnet names at fixed prefixes so the /20 never has to renumber when a hub service is added:

Hub subnet Prefix (EUS2) Purpose
GatewaySubnet 10.20.0.0/27 ExpressRoute + VPN gateways (reserved name)
AzureFirewallSubnet 10.20.0.64/26 Azure Firewall Premium data plane (reserved name)
AzureFirewallManagementSubnet 10.20.0.128/26 Firewall forced-tunnel mgmt (reserved name)
AzureBastionSubnet 10.20.0.192/26 Bastion for break-glass admin (reserved name)
snet-dnspr-inbound 10.20.1.0/28 Private DNS Resolver inbound endpoint
snet-dnspr-outbound 10.20.1.16/28 Private DNS Resolver outbound + forwarding rules
snet-shared 10.20.1.32/27 Shared platform services (jump, scanners)
(reserved) 10.20.2.0 – 10.20.15.255 Hub growth — never allocated to spokes

The six spokes map one-to-one onto the segmentation model. Each is its own VNet, peered only to the hub (no spoke-to-spoke peering exists — that is the entire security value), with NSGs written against service tags rather than raw CIDRs so rules survive renumbering:

Segment spoke CIDR (EUS2) Runs PE subnet Egress posture
control-plane 10.20.16.0/22 GitOps, deploy runners, KMS/Key Vault access 10.20.18.0/26 Firewall → allow-list only
customer-API 10.20.20.0/22 Playback + entitlement APIs (behind Front Door/WAF/B2C) 10.20.22.0/26 No inbound public IP; PL from edge
media-processing 10.20.24.0/22 Transcode/ABR, packaging, DRM packaging, live encode 10.20.26.0/26 Firewall → CDN origins, DRM SaaS
analytics 10.20.28.0/22 QoE ingest, Event Hubs, Stream Analytics, ADX 10.20.30.0/26 Firewall → allow-list only
corp 10.20.32.0/22 SAP connectivity, back-office, internal tools 10.20.34.0/26 Firewall → on-prem only
management 10.20.36.0/22 Bastion targets, scanners (Wiz), agents (Falcon) 10.20.38.0/26 Firewall → mgmt SaaS

The Routing Intent + firewall is the enforcement point. It is what stops the media-processing spoke from reaching corp (and therefore SAP, and therefore the crown-jewel financials) uninspected, and it is where egress FQDN allow-listing, TLS inspection and IDPS live. That the same firewall handles internet and private traffic is deliberate — one policy, one log stream, one place to prove to a TPN auditor that pre-release content cannot leave for an unapproved destination. The broader egress pattern is covered in centralized internet egress with FQDN filtering.

The hub and its Routing Intent in Terraform — this block, parameterised, is the entire per-region build:

resource "azurerm_virtual_hub" "eus2" {
  name                = "vhub-eus2"
  resource_group_name = azurerm_resource_group.conn.name
  location            = "eastus2"
  virtual_wan_id      = azurerm_virtual_wan.lumina.id
  address_prefix      = "10.20.0.0/20"          # the /20 hub
}

resource "azurerm_virtual_hub_routing_intent" "eus2" {
  name           = "ri-eus2"
  virtual_hub_id = azurerm_virtual_hub.eus2.id

  routing_policy {                               # all internet egress → firewall
    name         = "InternetTraffic"
    destinations = ["Internet"]
    next_hop     = azurerm_firewall.eus2.id
  }
  routing_policy {                               # all east-west + on-prem → firewall
    name         = "PrivateTraffic"
    destinations = ["PrivateTraffic"]
    next_hop     = azurerm_firewall.eus2.id
  }
}

resource "azurerm_virtual_hub_connection" "media" {
  name                      = "conn-media-processing"
  virtual_hub_id            = azurerm_virtual_hub.eus2.id
  remote_virtual_network_id = azurerm_virtual_network.media_processing.id
  # inherits Routing Intent: no manual route table needed
}

Because Routing Intent programs the effective routes, a spoke owner cannot add a 0.0.0.0/0 → Internet UDR to sneak past the firewall — the hub-injected route wins, and any attempt shows up as drift. NSG design, service tags and the effective-route mechanics that back this up are in Azure virtual networks, subnets and NSGs. The AWS side reaches the same posture by a different road.

AWS regional network

AWS gives you the pieces but not the opinion, so the regional network is an inspection-VPC hub wired up by hand: one Transit Gateway per region with segmented route tables, a dedicated inspection VPC running AWS Network Firewall, and the workload VPCs attached in appliance mode so both directions of every flow are pinned to the same firewall endpoint. This is the AWS translation of the Azure secured hub, and it lands on the same guarantees — no lateral movement, central inspected egress, symmetric flows.

The DX gateway feeds the TGW; segmented route tables send all workload traffic to the inspection VPC; Network Firewall inspects and NAT+IGW provides the single central egress; only then does traffic reach the workload VPCs and their endpoints.

An AWS region: Direct Connect gateway feeds a Transit Gateway with three route tables into an inspection VPC running Network Firewall and central NAT+IGW egress, then out to media-processing, customer-API and analytics VPCs with VPC endpoints

The TGW route-table design is the security control. Three route tables enforce that no workload VPC has a route to any other workload VPC — the only next hop a workload gets is the inspection VPC attachment:

TGW route table Associated attachments Propagations Effect
rt-inspection inspection VPC none (static routes) Sees all VPCs; returns post-inspection traffic
rt-workloads all workload VPCs none Default 0.0.0.0/0 + RFC1918 → inspection VPC only
rt-onprem DX gateway attachment workload summaries On-prem reaches workloads via inspection VPC

The inspection VPC is a /20 (mirroring the Azure hub) carved for three AZs. Network Firewall needs a firewall subnet per AZ; the TGW attachment needs its own subnets; NAT gateways and the IGW provide the single central egress:

Inspection subnet (us-east-1) Prefix Purpose
firewall-a/b/c 10.40.0.0/28, .16/28, .32/28 Network Firewall endpoints, one per AZ
tgw-a/b/c 10.40.0.64/28, .80/28, .96/28 TGW ENIs (appliance-mode attachment)
public-a/b/c 10.40.1.0/27, .32/27, .64/27 NAT gateways + IGW (central egress)
(reserved) 10.40.2.0 – 10.40.15.255 Growth — never allocated to workloads

The workload VPCs repeat the same six-segment model at the same offsets as Azure, which is what makes the estate comprehensible: 10.40.24.0/22 is media-processing on AWS exactly as 10.20.24.0/22 is on Azure.

Segment VPC CIDR (use1) Runs Endpoints Egress posture
control-plane 10.40.16.0/22 CodePipeline, deploy roles, KMS access STS, KMS (interface) via inspection VPC only
customer-API 10.40.20.0/22 Cognito-backed playback/entitlement (CloudFront/WAF) Secrets Mgr, DDB (gw) PrivateLink from edge; no public IP
media-processing 10.40.24.0/22 MediaConvert/MediaLive-class encode, packaging, SPEKE S3 (gw), ECR, Kinesis Firewall → CDN origins, DRM SaaS
analytics 10.40.28.0/22 Kinesis, QoE ingest, OpenSearch S3 (gw), Kinesis via inspection VPC only
corp 10.40.32.0/22 SAP edge, back-office STS Firewall → on-prem only
management 10.40.36.0/22 SSM targets, Wiz, Falcon agents SSM, SSM-messages Firewall → mgmt SaaS

The one detail that separates a working inspection VPC from a mysteriously broken one is appliance mode. Without it, the return path of a flow can land on a different AZ’s firewall endpoint than the request, Suricata sees only half the conversation, and it drops the flow as invalid — an intermittent failure that looks like anything but a routing setting. Enabling it on the attachment guarantees flow affinity. The rule-engineering side of this firewall is a whole discipline of its own, covered in AWS Network Firewall Suricata egress inspection.

The core of the region in Terraform — TGW with default association/propagation disabled (so nothing is reachable until you say so), plus the appliance-mode workload attachment:

resource "aws_ec2_transit_gateway" "use1" {
  description                     = "lm-tgw-use1"
  default_route_table_association = "disable"    # explicit RTs only
  default_route_table_propagation = "disable"    # no automatic reachability
  amazon_side_asn                 = 64512
}

resource "aws_ec2_transit_gateway_vpc_attachment" "media" {
  transit_gateway_id     = aws_ec2_transit_gateway.use1.id
  vpc_id                 = aws_vpc.media_processing.id
  subnet_ids             = [for s in aws_subnet.media_tgw : s.id]  # one /28 per AZ
  appliance_mode_support = "enable"              # pin both directions to one FW endpoint
}

# workload RT: the only next hop a workload gets is the inspection VPC
resource "aws_ec2_transit_gateway_route" "wl_default" {
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.workloads.id
  destination_cidr_block         = "0.0.0.0/0"
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.inspection.id
}

That terraform-module-aws-transit-gateway and its Network-Firewall companion are reused verbatim across all three AWS regions (Terraform module: AWS Transit Gateway). Inter-region TGW peering, with the same three-route-table discipline, extends the segmentation to eu-west-1 and ap-southeast-1 so Asia is reachable without a MOC. None of this works without an address plan that guarantees the /22s never collide — which is the next section.

IP address management plan

The whole design above depends on one invariant: no two subnets anywhere in the estate overlap. Overlap is not a style problem; it is the failure that makes a route ambiguous, breaks a Private Endpoint’s DNS, and turns a clean multi-cloud active/active into a support ticket that never closes. So IPAM is governed, not improvised: three non-overlapping /12 supernets, each carved into per-region /16s, each of those into a /20 hub and /22 segment spokes, each spoke holding a /26 private-endpoint subnet.

The supernet → region → hub → segment → endpoint carve-up, left to right:

IP address plan carve-up: three /12 estate supernets for on-prem, Azure and AWS, each split into per-region /16 blocks, then a /20 hub, six /22 segment spokes and /26 private-endpoint subnets

The top of the plan is three supernets that can never be confused for one another — a glance at the second octet tells you which estate you are in:

Estate Supernet Second-octet range Governed by
On-prem (MOCs + corp) 10.0.0.0/12 10.0 – 10.15 NetOps / on-prem IPAM
Azure 10.20.0.0/12 10.20 – 10.31 (region /16s at .20–.22) Azure Virtual Network Manager IPAM pools
AWS 10.40.0.0/12 10.40 – 10.47 (region /16s at .40–.42) AWS VPC IPAM
(reserved) 10.48.0.0/12 + future clouds / M&A Central architecture

On-prem consumes its /12 for the two facilities and the corporate estate, leaving generous room for the near-line archive to grow:

On-prem block CIDR Contents
LA MOC 10.0.0.0/16 Contribution encoders, playout, near-line archive, SRT/Zixi receivers
London MOC 10.1.0.0/16 Playout, post, EMEA contribution
Corp 10.2.0.0/16 AD DS (corp.luminamedia.net), SAP, back-office
Reserved 10.3.0.0 – 10.15.255.255 Archive expansion, future facilities

The Azure supernet expands per region. This is the master table an engineer opens to answer “what CIDR is the analytics spoke in West Europe?” without guessing — every region repeats the identical offset pattern, so the answer is mechanical:

Segment East US 2 (10.20/16) West Europe (10.21/16) Southeast Asia (10.22/16) Prefix
Secured hub 10.20.0.0/20 10.21.0.0/20 10.22.0.0/20 /20
control-plane 10.20.16.0/22 10.21.16.0/22 10.22.16.0/22 /22
customer-API 10.20.20.0/22 10.21.20.0/22 10.22.20.0/22 /22
media-processing 10.20.24.0/22 10.21.24.0/22 10.22.24.0/22 /22
analytics 10.20.28.0/22 10.21.28.0/22 10.22.28.0/22 /22
corp 10.20.32.0/22 10.21.32.0/22 10.22.32.0/22 /22
management 10.20.36.0/22 10.21.36.0/22 10.22.36.0/22 /22
private-endpoints (per spoke /26) 10.20.18/22/26/30/34/38.0/26 10.21.* mirror 10.22.* mirror /26

AWS mirrors it exactly, one supernet over — which is the feature, not a coincidence. media-processing is always at offset .24.0/22; only the second octet changes between clouds and regions:

Segment us-east-1 (10.40/16) eu-west-1 (10.41/16) ap-southeast-1 (10.42/16) Prefix
Inspection VPC 10.40.0.0/20 10.41.0.0/20 10.42.0.0/20 /20
control-plane 10.40.16.0/22 10.41.16.0/22 10.42.16.0/22 /22
customer-API 10.40.20.0/22 10.41.20.0/22 10.42.20.0/22 /22
media-processing 10.40.24.0/22 10.41.24.0/22 10.42.24.0/22 /22
analytics 10.40.28.0/22 10.41.28.0/22 10.42.28.0/22 /22
corp 10.40.32.0/22 10.41.32.0/22 10.42.32.0/22 /22
management 10.40.36.0/22 10.41.36.0/22 10.42.36.0/22 /22

A /22 (1,024 addresses) per segment is deliberate. Azure burns five addresses per subnet; AWS burns five per subnet and forces a per-AZ carve, so a /22 VPC across three AZs becomes three /24s (one per AZ) plus a /26 endpoint subnet per AZ — still comfortable for a media-processing fleet that scales to hundreds of encode tasks during a live event, without wasting a /20 on every segment. The /26 PE subnets (59 usable addresses) hold Private Endpoint NICs / interface-endpoint ENIs, which are low-count and slow-growing.

None of this is hand-maintained in a spreadsheet — both clouds have a native IPAM authority that hands out blocks and refuses overlaps:

Concern Azure AWS
Authority Virtual Network Manager IPAM pools VPC IPAM (pools + scopes)
Enforcement Pool allocations; VNet must draw from pool --allocation-min/max-netmask-length; auto-import
Overlap detection Pool rejects overlapping CIDR IPAM alarms + refuses conflicting alloc
Utilisation alerting Azure Monitor on pool usage CloudWatch on IPAM utilisation (>70%)
Reserved-but-unallocated Hub /20, supernet tail Inspection /20, supernet tail

Standing up the pools so no team can self-allocate a colliding block:

# Azure — root pool for the Azure /12, then a region child pool the VNets must draw from
az network manager ipam-pool create \
  --network-manager-name lm-avnm --resource-group lm-plat-connectivity-rg \
  --name azure-root --display-name "Azure 10.20.0.0/12" \
  --addressing-family IPv4 --address-prefixes "10.20.0.0/12"
az network manager ipam-pool create \
  --network-manager-name lm-avnm --resource-group lm-plat-connectivity-rg \
  --name eus2 --parent-pool-name azure-root \
  --addressing-family IPv4 --address-prefixes "10.20.0.0/16"

# AWS — region pool with hard min/max netmask so nobody carves a /21 or a /28 spoke
aws ec2 create-ipam-pool --ipam-scope-id ipam-scope-0abc \
  --address-family ipv4 --locale us-east-1 \
  --allocation-min-netmask-length 22 --allocation-max-netmask-length 26 \
  --allocation-default-netmask-length 22
aws ec2 provision-ipam-pool-cidr --ipam-pool-id ipam-pool-0def --cidr 10.40.0.0/16

Three guardrails keep the plan honest over years: the hub/inspection /20 and each supernet tail are reserved and never allocated (so growth never forces a renumber); overlap is a hard failure in IPAM, not a review comment; and the offset pattern is frozen.16 control-plane, .20 customer-API, .24 media-processing, .28 analytics, .32 corp, .36 mgmt — so firewall rules, NSGs and route tables are copy-paste across six regions. That symmetry is exactly what lets the private-connectivity layer be templated too.

Private connectivity for PaaS services

Every managed data service Lumina touches — the entitlement database, the VOD-master blob origin, the CIAM session store, every Key Vault and Secrets Manager — is reached over a private IP only. The public endpoint is not firewalled; it is switched off, and a policy guardrail stops anyone switching it back on. This is the control that satisfies TPN/MovieLabs (premium content masters in Blob/S3 have no internet-reachable endpoint), PCI-DSS (the billing data store is not on the public internet), and plain blast-radius sense (a leaked SAS token or access key is useless without a route to the service). On Azure that means Private Endpoints + Private DNS; on AWS, interface/gateway VPC endpoints (PrivateLink) + Route 53 Resolver.

The workload resolves a privatelink.* name to a private IP via Private DNS, connects through the Private Endpoint / VPC endpoint NIC in the /26, and reaches the PaaS service — whose public endpoint is denied by policy.

Private PaaS connectivity: a playback API resolves a privatelink DNS name to a private IP, connects through a Private Endpoint or AWS VPC endpoint to Azure SQL, Blob and Cosmos, while the public endpoint is denied by Azure Policy and an AWS SCP

The Azure Private Endpoint plan is one row per service per region — a NIC in the spoke’s /26, wired to the right sub-resource, with the A record auto-registered in the matching Private DNS zone:

PaaS service Sub-resource PE subnet (EUS2) Private DNS zone
Azure SQL (entitlement) sqlServer 10.20.22.0/26 privatelink.database.windows.net
Blob (VOD masters, origin) blob 10.20.26.0/26 privatelink.blob.core.windows.net
Cosmos DB (session/CIAM) Sql 10.20.22.0/26 privatelink.documents.azure.com
Key Vault (DRM keys, secrets) vault 10.20.18.0/26 privatelink.vaultcore.azure.net
Event Hubs (QoE ingest) namespace 10.20.30.0/26 privatelink.servicebus.windows.net
Container Registry (images) registry 10.20.18.0/26 privatelink.azurecr.io

The AWS side is the same intent with AWS’s two endpoint flavours — interface endpoints (an ENI + PrivateLink, for most services) and gateway endpoints (a route-table entry, only S3 and DynamoDB, and free):

AWS service Endpoint type Where DNS
S3 (VOD masters, origin) Gateway route table in each VPC native (path/virtual-host)
DynamoDB (session/metadata) Gateway route table in each VPC native
Secrets Manager (DRM/secrets) Interface PE subnet ENI private-DNS-enabled
STS / KMS (identity, keys) Interface PE subnet ENI private-DNS-enabled
Kinesis (QoE ingest) Interface PE subnet ENI private-DNS-enabled
ECR (api + dkr) Interface PE subnet ENI private-DNS-enabled

DNS is where private connectivity quietly succeeds or fails, so it is designed explicitly, not left to defaults. Azure Private DNS zones link to every spoke; an Azure Private DNS Resolver (inbound endpoint in the hub) answers the on-prem MOCs so LA and London resolve the same private IPs; and forwarding rules send privatelink.* and corp.luminamedia.net to the right resolver. On AWS, Route 53 Resolver inbound/outbound endpoints and a forwarding rule set do the equivalent, with private hosted zones associated to the workload VPCs. The public resolver’s answer for these names is never trusted — the entire point is that a nslookup from anywhere returns a 10.x address or nothing. The zone-linking and auto-registration mechanics are detailed in Azure Private DNS zones, auto-registration and linking, and the cross-region resolution pattern (so an EUS2 failover reaches WEU’s private IPs) in cross-region Private Link DNS for active/active apps.

A Private Endpoint plus its DNS zone group in Terraform — the public_network_access_enabled = false line is the one that actually matters:

resource "azurerm_mssql_server" "entitlement" {
  name                          = "lm-sql-entitlement-eus2"
  resource_group_name           = azurerm_resource_group.data.name
  location                      = "eastus2"
  version                       = "12.0"
  public_network_access_enabled = false          # no public endpoint exists
}

resource "azurerm_private_endpoint" "sql" {
  name                = "pe-sql-entitlement-eus2"
  location            = "eastus2"
  resource_group_name = azurerm_resource_group.data.name
  subnet_id           = azurerm_subnet.pe_customer_api.id   # the /26

  private_service_connection {
    name                           = "psc-sql"
    private_connection_resource_id = azurerm_mssql_server.entitlement.id
    subresource_names              = ["sqlServer"]
    is_manual_connection           = false
  }
  private_dns_zone_group {
    name                 = "sql-dns"
    private_dns_zone_ids = [azurerm_private_dns_zone.sql.id]  # privatelink.database.windows.net
  }
}

Turning the public endpoint off is necessary but not sufficient — someone with rights will eventually flip it back on “just to test,” so the deny-public guardrail is enforced at the platform, auto-remediated, and fed to Wiz as compliance evidence:

Guardrail Azure AWS Effect
Block public PaaS Policy: deny publicNetworkAccess != Disabled on SQL/Storage/Cosmos/KV SCP: deny s3:PutAccountPublicAccessBlock Cannot create/keep a public data endpoint
Force central egress Routing Intent (no spoke 0.0.0.0/0 UDR wins) SCP: deny ec2:CreateInternetGateway in workload OUs No workload-local internet path
Require Private Endpoint Policy: audit resources without PE Config rule: vpc-endpoint present Drift is visible + remediated
Private DNS only Policy: deny linking public DNS to spokes Resolver rule set (no public forwarder) Names resolve to private IPs only

The Azure Policy rule and the AWS SCP that anchor row one:

// Azure Policy — deny SQL/Storage/Cosmos with public network access enabled
{ "if": { "allOf": [
    { "field": "type", "in": ["Microsoft.Sql/servers","Microsoft.Storage/storageAccounts","Microsoft.DocumentDB/databaseAccounts"] },
    { "field": "Microsoft.Sql/servers/publicNetworkAccess", "notEquals": "Disabled" }
  ] },
  "then": { "effect": "deny" } }
// AWS SCP — no workload account may weaken account-level public-access block or add an IGW
{ "Version": "2012-10-17", "Statement": [
    { "Sid": "DenyDisableS3AccountBPA", "Effect": "Deny",
      "Action": "s3:PutAccountPublicAccessBlock", "Resource": "*" },
    { "Sid": "DenyIgwInWorkloads", "Effect": "Deny",
      "Action": ["ec2:CreateInternetGateway","ec2:AttachInternetGateway"], "Resource": "*" }
  ] }

And the interface endpoint that keeps AWS API calls private, scoped by an endpoint policy to Lumina principals so a stolen credential cannot reach another tenant’s Secrets Manager:

aws ec2 create-vpc-endpoint --vpc-id vpc-0mediaproc \
  --vpc-endpoint-type Interface \
  --service-name com.amazonaws.us-east-1.secretsmanager \
  --subnet-ids subnet-0pe-a subnet-0pe-b subnet-0pe-c \
  --security-group-ids sg-0pe-only --private-dns-enabled \
  --policy-document file://endpoint-policy-lumina-only.json

The result is a data plane with no public attack surface: the playback API has no public IP, the entitlement store has no public endpoint, DRM keys in Key Vault resolve only to a private address, and the deny-public guardrails make that state the only legal state. The trade-offs between Private Endpoints and the cheaper Service Endpoints — and why premium content mandates the former — are in Private Endpoint vs Service Endpoint, with the PaaS-scale pattern in Azure Private Link and Private DNS for PaaS. With the private substrate, regional topology and address plan settled, the platform is ready for the identity and workload tiers that ride on top of it.

Workforce identity and zero-trust control plane

For Lumina Media, identity is not a supporting service — it is the Tier-0 control plane every other tier authenticates against, and its blast radius is total. If the workforce identity plane is down, platform engineers cannot reach the clouds, the operations centres cannot drive playout, and editorial cannot open the media asset manager; if it is compromised, an attacker with one stolen admin credential can reach the encode farm, the origin, the ad-decision stack and the 400 PB archive. That is why identity carries the strictest objective in the estate — RTO ≤ 15 minutes, RPO ≈ 0 — and why the zero-trust posture (“never trust, always verify, assume breach”) is enforced here first, before network, before content. The organising principle follows the zero-trust architecture blueprint: every workforce access is an explicitly verified transaction — authenticated identity, healthy device, evaluated risk, least-privilege authorisation, immutable audit — with no trust granted by network location.

The one rule that governs this whole part: workforce identity and consumer identity are two physically separate planes that never mix. Staff live in the workforce plane (AD DS + Entra ID + Okta); the 120M subscribers live in the CIAM plane (Azure AD B2C + Amazon Cognito), covered in the next section. They share no directory, no token issuer and no trust — a compromise of a consumer account can never reach a production system, and a workforce credential can never assume a viewer identity. Everything below is workforce-only.

The system of record for the workforce is the on-prem AD DS forest corp.luminamedia.net, with domain controllers in both Media Operations Centres (Los Angeles and London) so authentication survives the loss of either facility — the forest and DC placement follow Active Directory DS forest design and DC promotion. AD DS is deliberately not the cloud identity provider. The cloud plane is deliberately dual: Entra ID is the authentication and policy authority (device trust, Conditional Access, PIM, and federation to Azure/AWS/M365), and Okta is the workforce access-management layer for the sprawling third-party SaaS and media-production tool catalogue — but Okta delegates every sign-in back to Entra, so a single Conditional Access gate covers both.

Here is the Tier-0 identity plane inventory — what each component is for, where it runs, and the recovery objective it inherits:

Component Role in the plane Where it runs Objective Failure blast radius
AD DS corp.luminamedia.net Workforce system of record; Kerberos for on-prem/broadcast apps 4 DCs across LA + London MOCs RTO ≤15m, RPO ≈0 On-prem playout/editorial login, file, legacy auth
Entra Connect cloud sync AD DS → Entra provisioning + PHS HA agent pairs at LA + London RTO ≤30m New/changed accounts stop flowing (existing tokens fine)
Entra ID tenant Auth + policy authority; cloud token issuer Microsoft global (geo US) RTO ≤15m, RPO ≈0 Every cloud + M365 + Okta-fronted sign-in
Okta org SaaS/media app-catalog SSO + lifecycle Okta cloud RTO ≤30m Third-party SaaS SSO (auth still via Entra)
Conditional Access Per-sign-in policy engine Entra (part of tenant) Wrong policy = mass lockout or over-grant
Entra ID Protection User/sign-in risk scoring Entra P2 best-effort Risk-based blocks stop firing
PIM Just-in-time privileged role activation Entra P2 RTO ≤15m Admins cannot elevate (break-glass covers)
Break-glass accounts ×2 Emergency Global Admin, excluded from all policy Entra (cloud-only) always available Last-resort admin if PIM/CA fails

Sync topology and the Entra + Okta split

Lumina runs Entra Connect cloud sync, not the heavyweight Connect Sync server — the trade-offs are in Entra Connect Sync vs cloud sync. Cloud sync uses lightweight provisioning agents (HA pairs at each MOC, so no single sync server is a Tier-0 SPOF) and authentication uses Password Hash Synchronisation (PHS) plus Seamless SSO. PHS is chosen over Pass-Through Auth and AD FS for one overriding reason: cloud sign-in must not depend on an on-prem service being reachable. When a MOC or circuit fails during a live final, engineers must still authenticate to the cloud control plane — PHS makes Entra self-sufficient; PTA/AD FS would couple every cloud login to an on-prem endpoint.

The Entra/Okta division of labour is the media-specific decision that generic estates skip. A streaming operator carries hundreds of niche SaaS and post-production vendor apps (MAM/DAM, non-linear editors, ad-sales order management, encoding vendor portals) that predate the Entra estate and live naturally in Okta’s catalogue; meanwhile device trust, PIM and Intune are native to Entra. Rather than fight that, Lumina assigns each plane what it is best at and wires them so Conditional Access still governs everything — the governance model mirrors multicloud identity governance with Okta and SailPoint:

Plane Owns Representative apps Why it owns this
Entra ID Auth + policy authority: device trust, Conditional Access, PIM, ID Protection; M365 + Azure + AWS federation M365 E5, Azure/AWS portals, security tooling Device compliance, PIM and Intune are Entra-native; it must own the CA gate
Okta Workforce app-catalog SSO + fine-grained app assignment + Workflows lifecycle for non-Microsoft SaaS MAM/DAM, Avid/editorial, ad-sales, encoding vendor portals Deep SaaS catalogue + Okta Workflows JML automation for the media-tool estate
Integration Okta delegates sign-in to Entra via inbound federation (“Sign in with Microsoft”) Every Okta chiclet One CA gate — even Okta apps inherit device + phishing-resistant checks

The load-bearing detail is that last row: Okta is configured with Entra as its Identity Provider, so a user launching an Okta-catalogued MAM is bounced to Entra, satisfies Conditional Access (compliant device, phishing-resistant MFA), and only then returns to Okta for the app SSO and assignment. There is no second, weaker authentication path around the CA gate. Confirming the sync agents and the federation are healthy:

# Cloud sync agents are HA-paired at LA + London — confirm both are active:
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/onPremisesPublishingProfiles/provisioning/agents" \
  --query "value[].{id:id, status:status, machine:machineName}" -o table

# Confirm PHS is on and Seamless SSO is live (AZUREADSSOACC computer object in AD):
az rest --method GET \
  --url "https://graph.microsoft.com/beta/directory/onPremisesSynchronization" \
  --query "value[0].features.passwordSyncEnabled"

Federation hub, break-glass and the JML lifecycle

Every relying party trusts Entra (directly, or via Okta which itself trusts Entra). AWS consumes Entra through IAM Identity Center, which federates via SAML/OIDC and maps Entra groups to AWS permission sets — AWS has zero local IAM users except break-glass, so an HR termination revokes AWS access on the next SCIM cycle; the account-tree and permission-set model is the one in AWS Organizations and IAM foundations. M365/SaaS SSO uses SAML/OIDC with SCIM provisioning; the enterprise-app + claims pattern is Entra SAML SSO enterprise-app configuration.

Two cloud-only break-glass accounts (*.onmicrosoft.com, not synced from AD) are the emergency floor: permanently assigned Global Administrator, excluded from every Conditional Access policy and from PIM, credentialed with FIDO2 keys split across sealed safes in LA and London, and wired to a Sentinel alert that fires on any sign-in — the full model is Entra break-glass emergency access. They are the only standing privileged accounts in the tenant.

Joiner-Mover-Leaver (JML) is driven from Workday as the authoritative HR source, with a media-specific gate that generic IAM misses: studio-partner and pre-release content access requires a content-security (TPN) onboarding gate on top of the HR feed — a contractor colourist is not merely “hired”, they are cleared to touch a specific unreleased title, time-boxed to the engagement. Personas are materialised as dynamic groups (Entra dynamic groups membership rules) computed from HR attributes, so a move between roles re-buckets a user automatically:

Lifecycle event Authoritative source Gate / condition Automated action
Joiner (staff) Workday hire → AD DS Start date reached Provision account, baseline apps, licence; add to persona dynamic group
Joiner (studio partner) B2B invite + TPN clearance Content-security clearance for the title — else no premium access Scoped to the specific production, time-boxed access package
Mover Workday transfer Dept/role change Recompute dynamic persona groups; access review of retained rights
Leaver (planned) Workday termination Last-day timestamp Disable sign-in, revoke sessions/tokens (Entra + AWS + Okta), reclaim licence
Leaver (emergency) Security/HR manual trigger Immediate Real-time session revoke across Entra + AWS + SaaS via SCIM de-provision
Contractor / freelancer Sponsor + access package Time-boxed package Auto-expiry with sponsor recertification; no standing access

AD DS as the source, dual cloud sync into an Entra + Okta hub with Conditional Access in the middle of every issuance, then federation out to AWS, SaaS and the media/ops apps:

Lumina Media workforce identity control plane: on-prem AD DS forest corp.luminamedia.net synchronises via Entra cloud sync and the Okta AD agent into a dual Entra + Okta hub, where Conditional Access and PIM gate every token before Entra federates as the authentication authority to AWS IAM Identity Center, SaaS SSO and media/ops apps, with two excluded break-glass accounts

Customer identity and access management at scale

The consumer plane is a different universe of scale and threat. It holds 120M subscriber accounts, authenticates viewers across web, mobile, smart-TV, console and set-top, and must issue playback authorization at 45M concurrent during a live final — while facing the open internet, credential-stuffing botnets, free-trial fraud and credential-sharing at population scale. This is CIAM (Customer Identity and Access Management), and it is engineered as an entirely separate estate from the workforce plane above: separate directories, separate token issuers, separate subscriptions (lm-lz-ciam), separate on-call. A viewer is never in AD DS or Entra; a staff member is never in the consumer directory. The two planes do not federate to each other in either direction.

The reason the separation is absolute is written into the tiering: CIAM authentication is Tier-1 (RTO ≤ 15 min, RPO ≤ 1 min — a viewer must never see an outage), but it must fail independently of the Tier-0 workforce plane, so a workforce identity incident cannot take down subscriber sign-in, and vice-versa. Here is how the two planes differ on every axis that matters — this is the table to internalise before anything else:

Dimension Workforce plane Consumer plane (CIAM)
Population ~staff + contractors + partners (thousands) 120M subscriber accounts
Directory AD DS + Entra ID + Okta Azure AD B2C + Amazon Cognito
Source of truth Workday HR → AD DS Self-service sign-up + social IdPs
Auth authority Entra (Okta delegates to it) B2C (Cognito federates to it)
Primary factor Phishing-resistant (FIDO2/passkey), CA-gated Password / social / passwordless; step-up only on sensitive ops
Policy engine Conditional Access + PIM Edge WAF/bot + risk-based step-up + entitlement
Token purpose App/API access under least privilege Playback authorization (short-lived, per-asset)
Peak load Business-hours, thousands of sign-ins 45M concurrent, millions of token issues/min
Governance JML, access reviews, PIM Consent, DSAR (GDPR/CCPA), self-service account control
Compliance driver SOC 2, least privilege, TPN GDPR + CCPA (PII/consent), PCI-DSS (billing)
Blast radius of compromise Production systems, content, cloud One viewer’s watch history + PII; never a production system

The CIAM platform: B2C authority, Cognito at the AWS edge

Lumina runs a dual-cloud CIAM with a single account authority. Azure AD B2C is the consumer identity provider and holds the 120M-account directory: local accounts (email/username + password), social IdPs, and custom user journeys built with the Identity Experience Framework (IEF). Amazon Cognito user pools front the AWS-hosted apps and the AWS-native playback APIs; each Cognito pool is configured with B2C as an OIDC identity provider, so B2C stays the single source of consumer truth while Cognito mints AWS-scoped access tokens (and identity-pool temporary credentials where an app must call AWS directly) at the us-east-1 / eu-west-1 / ap-southeast-1 edge. The OAuth2/OIDC flows underneath are the standard ones in OIDC and OAuth2 flows with authorization-code + PKCE — every native app uses authorization-code + PKCE, never the implicit flow.

Component Role Runs in Notes
Azure AD B2C Consumer directory + IdP; 120M accounts; sign-up/sign-in journeys Azure (geo US + EU) IEF custom policies; social + local; source of consumer truth
Amazon Cognito user pools AWS-edge auth + token broker for AWS-native apps AWS ×3 regions Federates to B2C (OIDC); issues AWS-scoped tokens
Token / authorization service Stateless issuer of short-lived playback tokens Both clouds, active/active Signs JWT; verified at CDN edge; fed by entitlement
Entitlement service Decides sub status + plan + concurrency + geo + device Both clouds Deny-list for instant revocation
Profile + consent store Durable PII, preferences, consent, device registry Cosmos DB / DynamoDB, region-partitioned GDPR/CCPA residency; DSAR source

Sign-in methods, the token model and playback authorization

Consumers sign in with whatever they already have — that is a conversion decision as much as a security one. Every method resolves to the same B2C account and the same downstream tokens:

Sign-in method Mechanism User friction Where implemented
Email/username + password B2C local account Low; password-manager friendly B2C sign-up/sign-in user flow
Social (Apple, Google, Facebook, Amazon) OIDC/OAuth2 federation Lowest; one tap B2C social IdP; Apple required for iOS/tvOS
Phone / OTP SMS or authenticator OTP Medium B2C custom policy
Passwordless (passkey / magic link) WebAuthn or one-time email link Low; no password B2C + device platform
TV/console device code RFC 8628 device authorization grant Enter code on phone B2C + device flow (10-foot UX)

The token model is where CIAM stops looking like a login box and starts looking like a media system. A consumer session issues ordinary OIDC tokens, but the playback path is authorized by a separate, deliberately short-lived token so that a leaked session cannot stream forever and a shared credential is caught by the entitlement check on every renewal:

Token Issued by TTL Audience Verified where Revocation
ID token (OIDC) B2C ~1h The app App Re-auth
Access token B2C / Cognito ~1h Account/profile APIs API gateway Short TTL + deny-list
Refresh token B2C / Cognito Sliding, revocable Token endpoint Token service Revoke on password change / leaver
Playback token Token service ~90s Manifest + segment + DRM CDN edge Expiry + entitlement deny-list (instant)
DRM licence Multi-DRM (SPEKE) Per policy Player CDM License service Bound to entitlement + playback token

The playback flow ties CIAM to the delivery and DRM stack. A viewer authenticates once (B2C/Cognito), then every play request carries a session token to the edge; the token service calls the entitlement service — which evaluates subscription status, plan tier, concurrent-stream cap, geo/blackout and registered-device binding — and only on a pass mints a ~90s signed playback token that the CDN edge verifies before serving a signed manifest/segment, with the multi-DRM licence (Widevine/FairPlay/PlayReady, CENC cbcs) issued against that same entitlement. Bot and credential-stuffing defence sits at the edge before any of this reaches the directory:

Lumina Media CIAM at 120M scale: a viewer on web/TV/mobile/console passes the multi-CDN edge and its WAF/bot wall, authenticates against the Azure AD B2C authority (with Amazon Cognito bridging AWS-hosted apps), then the token service checks the entitlement (subscription, concurrency, geo, device) and mints a ~90-second playback token that authorizes the multi-DRM licence and signed origin delivery

Defending 120M accounts: bots, credential-stuffing and rate limits

At this scale the login and sign-up endpoints are under continuous automated attack, and the defence is layered so no single control is load-bearing. The media-specific twist is free-trial abuse (bots minting throwaway accounts to farm trials) and credential-sharing at scale (handled by concurrency/device binding below, not by blocking):

Threat Control Where Signal / action
Credential stuffing Breached-password check (k-anonymity), per-account velocity Edge + B2C Block known-breached; lock after N fails with backoff
Bot sign-up / trial abuse Bot management + device fingerprint + CAPTCHA step-up Edge WAF Challenge/deny non-human; cap sign-ups per IP/ASN
Account takeover (ATO) Risk-based step-up, impossible-travel, new-device email B2C risk + edge Step-up MFA; notify + optional session revoke
Volumetric / L7 DDoS Per-IP/per-ASN rate limits, edge DDoS Multi-CDN edge Shed load before it reaches /authorize and /token
Token replay Short TTL + audience/asset binding + edge verification Token service Expired/replayed playback token rejected at edge

Rate limits are tiered — global, per-ASN, per-IP and per-account — so a legitimate spike (a live final sign-in surge) is absorbed while a stuffing run from a narrow ASN is throttled. The /authorize and /token endpoints are treated as the most valuable surface in the estate and are never exposed without the edge in front.

MFA, passwordless and account-change protection

Consumers are not asked to MFA on every play — that would wreck conversion and start-time. Instead, friction is reserved for sensitive account operations, and everything else stays frictionless. The rule: viewing is low-friction, changing the account is high-friction.

Operation Requirement Rationale
Watch content Session token + entitlement only Zero added friction; protects QoE/VST
Change password / email Step-up: OTP or passkey Blocks ATO from a hijacked session
Add/remove a device Step-up + email confirmation Enforces the device-binding control
Change payment method Step-up MFA (PCI-adjacent) Payment fraud + chargeback defence
Change plan / cancel Step-up Prevents malicious downgrade/cancel
View full PII / download data Step-up + identity verification GDPR/CCPA DSAR safety

Consent, DSAR and device/concurrency binding

Two population-scale governance duties fall on CIAM. First, consent and data-subject rights under GDPR and CCPA. Lumina captures granular consent at sign-up and in a self-service preference centre, stores it versioned in the consent store, and propagates it downstream (recommendations, ad personalization, analytics). Ad personalization is the sensitive one — CCPA “Do Not Sell/Share” and GDPR/ePrivacy consent must gate the ad-decision path, expressed as IAB TCF/GPP signals:

Consent category Default Gates Signal
Essential (playback, billing) On (contractual) Nothing to opt out of
Analytics / QoE Opt-in (EU) / opt-out (US) Product analytics events Internal flag
Personalization (reco) Opt-in (EU) Recommendation engine Internal flag
Advertising Opt-in (EU) / DNS-honoured (US) SSAI / ad-decision server IAB TCF/GPP
DSAR type Regulation SLA Verification Downstream action
Access / portability GDPR Art.15/20, CCPA 30 / 45 days Step-up + identity check Export profile + watch history
Erasure (“delete me”) GDPR Art.17, CCPA 30 / 45 days Step-up Delete/anonymise across profile, analytics, backups per retention
Rectification GDPR Art.16 30 days Step-up Update profile store
Opt-out of sale/share CCPA/CPRA Immediate Session-level Set GPP; cut ad-personalization

Second, device and concurrency binding — the credential-sharing control and the reason a leaked password does not become free unlimited streaming. Each account has a registered-device list and a plan-based concurrency cap; the entitlement service enforces both on every playback-token mint:

Plan Concurrent streams Registered devices Geo binding Sharing signal
Basic (AVOD) 1 2 Home region 3rd stream denied
Standard (SVOD) 2 4 Home + travel window Impossible-geo velocity flag
Premium (SVOD) 4 6 Home + travel window Device-churn + geo spread flag
Live-sports add-on Per base plan Per base plan Blackout-aware Geo/blackout enforced at token

Creating a B2C sign-up/sign-in user flow and federating a Cognito pool to B2C — the two moves that stand up the account authority and its AWS-edge bridge:

# 1) Azure AD B2C: create a sign-up/sign-in user flow (Graph beta, in the B2C tenant context)
az rest --method POST \
  --url "https://graph.microsoft.com/beta/identity/b2cUserFlows" \
  --headers "Content-Type=application/json" \
  --body '{
    "id": "signupsignin_consumer",
    "userFlowType": "signUpOrSignIn",
    "userFlowTypeVersion": 3,
    "isLanguageCustomizationEnabled": true
  }'

# 2) Amazon Cognito: register B2C as the OIDC IdP so B2C stays the single account authority
aws cognito-idp create-identity-provider \
  --user-pool-id "us-east-1_LuminaViewers" \
  --provider-name "AzureADB2C" --provider-type "OIDC" \
  --provider-details '{
    "client_id": "<cognito-app-client-id>",
    "oidc_issuer": "https://luminab2c.b2clogin.com/<tenant>/v2.0/",
    "authorize_scopes": "openid profile email",
    "attributes_request_method": "GET"
  }' \
  --attribute-mapping '{"email":"email","username":"sub"}'

Employee and operations-centre access

Lumina’s workforce is not one population but ten personas, each with a different identity source, application set, device reality and acceptable authentication friction. A platform engineer with cloud-admin reach, an editor cutting an unreleased title, a Media Operations Centre engineer handing off a shift on a shared desk, an ad-sales rep in a booking tool, and a care agent taking subscriber calls from home cannot be served by one access policy — and the entire Conditional Access and Intune model downstream is keyed to these personas. Personas are materialised as dynamic groups so movement between roles re-buckets a user automatically. The productivity base is M365 E5 (Exchange, Teams, SharePoint/OneDrive), all SSO from Entra; the media estate is fronted by Okta (delegating auth to Entra).

This persona/access matrix is the backbone the rest of the part references:

Persona Identity source Primary applications Device class Primary auth CA persona group
Platform engineer AD DS (separate admin acct) Azure/AWS portals, IaC, K8s, observability PAW FIDO2 + PIM JIT ca-persona-platform
Content-ops / MOC AD DS Playout, encode farm, MAM, QoE Shared MCR WS Passkey tap (shared mode) ca-persona-contentops
Editorial / creative AD DS NLE (Avid/Premiere), MAM/DAM, review Editorial WS (remote) FIDO2 / Hello ca-persona-editorial
Producer AD DS Scheduling, rights, MAM, dailies review Corp laptop + mobile Hello / authenticator ca-persona-producer
Ad-sales AD DS Ad order mgmt, DSP/SSP, SSAI console Corp laptop Authenticator MFA ca-persona-adsales
Finance AD DS SAP, billing/PCI, revenue, BI Corp laptop Phishing-resistant (PCI) ca-persona-finance
Customer care AD DS CRM (Zendesk/Salesforce), account tools Corp laptop (remote) Authenticator MFA ca-persona-care
Contractor / freelancer Access package Scoped project apps only Corp or MAM BYOD MFA, time-boxed ca-persona-contractor
Studio partner B2B guest + TPN Content exchange, review, specific apps Their device Cross-tenant MFA ca-persona-partner
Admin (Tier-0) AD DS (separate admin acct) Entra, Intune, security, cloud root PAW FIDO2 + PIM JIT ca-persona-admin

Fast, secure access on shared operations-centre workstations

The defining workplace problem in a 24×7 Media Operations Centre is the shared workstation: an MCR/NOC desk used by successive shift engineers, where during a live event seconds matter — a full username/password sign-in per handoff is operationally unacceptable — yet a generic mcr1 login shared by everyone destroys the per-engineer audit trail that TPN, SOC 2 and any post-incident review demand. Lumina resolves this the same way healthcare resolves shared clinical carts: Entra shared-device mode + passkey/FIDO2 tap, so a ~2-second tap hands the desk to the next engineer, but the identity asserted to Entra and to playout is always the individual engineer’s UPN. Walk away and the session locks; the next engineer taps and gets their session and their scoped access.

Ops auth pattern Speed Audit fidelity Entra mechanism Best fit
Passkey/FIDO2 tap on shared-device mode ~2s handoff Per-individual UPN Entra SharedDeviceMode + FIDO2 MCR/NOC shared desks
Windows Hello for Business PIN/biometric Per-individual, strong Cert/key on device Assigned editorial/producer WS
FIDO2 security key Tap key Per-individual, phishing-resistant Native Entra FIDO2 PAW, platform-eng, admin
Live-event fast-path (pre-approved PIM) Seconds to elevate Full PIM audit + Sentinel PIM eligible + auto-approve Playout admin during an event

The rule that keeps this both fast and safe: even the live-event fast-path is still PIM — during a final, the playout-admin role is pre-approved and eligible so activation takes seconds, but it is never standing, always MFA-gated, always alerted to Sentinel, and time-boxed to the event window with a review afterward. Speed comes from pre-approval, not from leaving privilege switched on.

Every media application is fronted by SSO (Okta delegating to Entra, or Entra directly) with automated provisioning, so a single JML event propagates everywhere:

Application SSO Provisioning CA authentication context Notes
MAM / DAM SAML via Okta → Entra SCIM c1 content (phishing-resistant) Premium assets; no local download on unmanaged
Editorial (Avid/Premiere) SAML via Okta → Entra Group-based c1 content Remote workstation; pixels not files
Playout / encode farm consoles SAML/OIDC → Entra Manual, privileged c2 ops (PIM) Ops segment only; internet-dark
Ad order mgmt + SSAI console SAML via Okta → Entra SCIM Baseline + step-up Ad path separate from editorial
Care CRM (Zendesk/Salesforce) SAML → Entra SCIM Baseline Agent tooling; no CIAM directory access
SAP (finance/billing) SAML → Entra SCIM/manual c3 PCI (phishing-resistant) Billing/PCI scope
Azure / AWS portals Entra / IAM IC c2 admin (PIM) PAW + FIDO2 only

An ops-centre engineer or on-call SRE proves device compliance and a fast passkey, clears a scoped ops Conditional Access context (admins add PIM just-in-time), reaches the internet-dark content-ops, encoding and live-control systems, and every action lands in the audit + QoE plane:

Lumina Media operations-centre access: MOC engineers on shared LA/London desks and remote on-call SREs prove device posture (PAW/compliant + passkey), clear a scoped ops Conditional Access context with PIM just-in-time for privileged actions, reach the internet-dark content-ops, encoding and live-control systems, and have every action correlated in the QoE and audit plane

Conditional Access and PIM policy model

Conditional Access is where zero trust becomes concrete: every workforce sign-in — including the ones delegated in from Okta — is scored on signals (user risk, sign-in risk, device state, application sensitivity, network location) and the policy set converts that score into grant controls (MFA, compliant device, authentication strength) and session controls (sign-in frequency, persistent browser, download limits). Lumina runs CA as a numbered, persona-targeted set using authentication context, following Conditional Access at scale with personas and authentication context, and every policy ships report-only first — the discipline from deploy baseline CA policies in report-only — so a misfire is caught in What If and the sign-in logs before it locks out a live control room.

Signals in, grant and session controls out, and a separate stricter path for administrators who hold no standing privilege and must activate just-in-time:

Lumina Media Conditional Access and PIM: user, device, app and network signals feed the numbered CA policy set (CA001-CA016), which emits grant and session controls; content-production and PCI-billing apps require phishing-resistant strength, while the privileged path routes admins to PIM eligible roles activated just-in-time with MFA, approval and a 4-hour cap, audited to Sentinel

The Conditional Access policy set

This is the enforced set — persona-scoped, authentication-context-aware, and layered so controls are additive:

ID Policy Target Key condition Grant Session
CA001 Baseline MFA all users All users, all apps Exclude break-glass Require MFA
CA002 Block legacy authentication All users Legacy auth clients Block
CA003 Require compliant/hybrid device All users, all apps Compliant OR hybrid-joined
CA004 Content apps — phishing-resistant c1 content (MAM, editorial) Editorial/content-ops/producer Auth strength: phishing-resistant Sign-in freq 8h; no persistent browser
CA005 Content on unmanaged — no download Content apps, unmanaged device Device not compliant Web-only App-enforced: block download
CA006 Admin/ops portals — phishing-resistant c2 admin/ops (Azure/AWS/Intune/playout) ca-persona-platform, -admin, -contentops Phishing-resistant MFA Sign-in freq 4h; no persist
CA007 PIM activation — auth context c2 role-activation context Privileged roles FIDO2 auth strength
CA008 PCI billing/finance c3 PCI (SAP, billing) ca-persona-finance Phishing-resistant + compliant Sign-in freq 4h
CA009 Sign-in risk All users Risk = high MFA + password change or block
CA010 User risk All users User risk = high Require secure password change
CA011 Mobile — app protection iOS/Android, mail + media apps BYOD Require approved app + APP policy
CA012 Named/blocked locations All users Sign-in from blocked country Block
CA013 EU data residency EU staff, EU-resident apps Access to EU data Compliant + EU region
CA014 Studio-partner B2B ca-persona-partner guests External identities Cross-tenant MFA + TPN clearance Sign-in freq per session
CA015 Contractors time-boxed ca-persona-contractor Scoped apps only MFA + compliant/MAM Sign-in freq 4h
CA016 Live-event control c2 ops, playout apps Live-event window Phishing-resistant + PIM Sign-in freq 4h; no persist

Authentication strength is the mechanism behind the phishing-resistant rows — it maps a resource’s sensitivity to an allowed method list, so content, privileged and PCI access simply cannot be reached with a phishable factor; the rollout baseline is FIDO2 passwordless with Windows Hello and security keys:

Authentication strength Allowed methods Applied to Personas
Phishing-resistant MFA FIDO2, Windows Hello, cert-based Content (c1), admin/ops (c2), PCI (c3), PIM Platform-eng, admin, editorial, content-ops, finance
MFA (standard) Authenticator (number match), FIDO2, Hello Baseline all-user, corp apps Ad-sales, care, producer, contractor
Passwordless Authenticator phone sign-in, FIDO2, Hello Corp modern Opt-in corporate

Assigning the built-in phishing-resistant strength to the content policy via Graph — landing in report-only first, then validated with CA sign-in log troubleshooting:

az rest --method POST \
  --url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" \
  --headers "Content-Type=application/json" \
  --body '{
    "displayName": "CA004 - Content apps require phishing-resistant MFA",
    "state": "enabledForReportingButNotEnforced",
    "conditions": {
      "users": { "includeGroups": ["<ca-persona-editorial>","<ca-persona-contentops>"],
                 "excludeGroups": ["<break-glass-group>"] },
      "applications": { "includeApplications": ["<mam-app-id>","<nle-app-id>"] }
    },
    "grantControls": {
      "operator": "OR",
      "authenticationStrength": { "id": "00000000-0000-0000-0000-000000000004" }
    },
    "sessionControls": {
      "signInFrequency": { "value": 8, "type": "hours", "isEnabled": true },
      "persistentBrowser": { "mode": "never", "isEnabled": true }
    }
  }'

Identity Protection risk and PIM just-in-time

Rows CA009/010 are driven by Entra ID Protection, tuned per ID Protection risk-based policies, so risk becomes an automated response rather than a report:

Risk signal Level Automated response Rationale
Sign-in risk High Block or phishing-resistant MFA + fresh session Stolen-token/impossible-travel on content or cloud is contained live
User risk High Secure password change at next sign-in Compromised credential remediated before broad access
Leaked credentials Any Force reset + revoke sessions Known-bad password cannot ride existing tokens
Anomalous privileged sign-in Medium+ Step-up + Sentinel P1 Admin accounts get zero benefit of the doubt

Privileged access uses no standing admin — every high-privilege role (Global Admin, Privileged Role Admin, Intune Admin, Security Admin, the AWS-infra roles federated via IAM Identity Center, and the live-playout/content-security roles) is eligible only through PIM, activated just-in-time with MFA, justification, approval for Tier-0, and a hard time cap; the model follows PIM for roles with approval workflows inside the broader PIM/PAM architecture. Activation is gated by CA007 (FIDO2):

Role Eligible group Max activation Approval Requires Review
Global Administrator pim-global-admin 4h Yes (2 approvers) FIDO2 + justification Monthly
Privileged Role Admin pim-priv-role-admin 4h Yes FIDO2 + justification Monthly
Security Administrator pim-sec-admin 8h No FIDO2 + justification Quarterly
Intune Administrator pim-intune-admin 8h No FIDO2 + justification Quarterly
AWS infra (via IAM IC) pim-aws-infra 8h Yes for prod FIDO2 + ticket Quarterly
Live-playout admin pim-playout-admin 4h Auto-approve in event window FIDO2 + event ref Per event

Creating an eligible (not active) assignment and tightening its activation policy via Graph:

# Make a user ELIGIBLE for Intune Administrator (no standing access):
az rest --method POST \
  --url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleEligibilityScheduleRequests" \
  --headers "Content-Type=application/json" \
  --body '{
    "action": "adminAssign",
    "principalId": "<admin-account-objectId>",
    "roleDefinitionId": "3a2c62db-5318-420d-8d74-23affee5d9d5",
    "directoryScopeId": "/",
    "scheduleInfo": { "startDateTime": "2026-07-09T00:00:00Z",
                      "expiration": { "type": "afterDuration", "duration": "P365D" } }
  }'
# roleManagementPolicy rules then enforce: activation ≤ PT8H, require FIDO2 (auth context c2),
# require justification, and alert on activation → routed to Sentinel.

Endpoint, UEM and workstation management

A device is a first-class zero-trust signal: Conditional Access rows CA003/005/011 all consume device compliance, so endpoint management is not a productivity nicety — it is the gate that keeps unmanaged, unhealthy or personal hardware away from production content and cloud consoles. Lumina manages endpoints with Microsoft Intune, one enrolment/compliance profile per device class, each emitting a compliance fact CA turns into allow/deny — the linkage is endpoint Conditional Access with device-compliance filters. The media-specific twist mirrors healthcare’s unpatchable-device problem, but inverted: the fragile thing is not the endpoint, it is the premium pre-release content, which must never land on a workstation at all.

Each device class, how it enrols, and where its compliance verdict lands:

Device class OS Enrolment Ownership Management profile Compliance → CA
Corp laptop Windows 11 Autopilot (zero-touch) Corporate Full MDM, BitLocker, Defender + CrowdStrike Compliant → all apps
Corp laptop macOS ABM/ADE auto-enrol Corporate Full MDM, FileVault, platform SSO Compliant → all apps
Editorial / creative WS Windows/macOS Autopilot/ADE + remote (AVD/Teradici) Corporate MDM + remote-session; content stays in enclave Compliant → content apps
Shared ops WS (MCR/NOC) Windows 11 Autopilot + shared-device mode Corporate Multi-user, passkey tap, auto-lock Compliant → ops apps
Mobile (corp) iOS/iPadOS ADE (supervised) Corporate Full MDM, per-app VPN Compliant → mobile apps
Mobile (BYOD) iOS/Android MAM-WE (no enrolment) Personal App-protection policy only Protected app → mail/media container
PAW (admins) Windows 11 Autopilot, hardened baseline Corporate Locked-down, no browsing, FIDO2 only Compliant → admin/cloud consoles

Corp Windows uses Autopilot for zero-touch (Intune Autopilot zero-touch); macs auto-enrol through Apple Business Manager with FileVault and platform SSO (Intune macOS management); personal phones use MAM app-protection without enrolment (Intune MAM for BYOD) so a freelancer’s own phone gets a protected, encrypted, remotely-wipeable container around Teams and the media apps — Lumina never takes the personal device, only the corporate data on it. Editorial workstations are delivered as remote sessions over AVD/Teradici, the pattern in Conditional Access for AVD and Windows 365, precisely so the content never touches the endpoint.

Compliance policies as the CA signal

Compliance is a small set of hard facts per class; fail any and the device flips non-compliant and CA cuts it from content and cloud automatically — the CrowdStrike Falcon and Defender signals feed the verdict:

Compliance setting Corp laptop Editorial WS Shared ops WS BYOD (MAM)
Disk encryption BitLocker BitLocker BitLocker App-level
Min OS version Patch ring Patch ring Enforced App-enforced
EDR (CrowdStrike + Defender) Required Required Required
Threat level (MTD/EDR) ≤ Medium ≤ Low ≤ Low ≤ Low
Jailbreak/root N/A N/A N/A Blocked
Firewall / secure boot Required Required Required N/A
PIN/complexity Required Required Session PIN App PIN
Non-compliance action Retire after grace Block content Immediate block Block container

Defining a Windows compliance policy that becomes the CA signal, via the Intune Graph API:

# Compliance policy: BitLocker + secure boot + Defender + min OS → emits "compliant" for CA003.
az rest --method POST \
  --url "https://graph.microsoft.com/v1.0/deviceManagement/deviceCompliancePolicies" \
  --headers "Content-Type=application/json" \
  --body '{
    "@odata.type": "#microsoft.graph.windows10CompliancePolicy",
    "displayName": "Win11 Content - Compliant baseline",
    "bitLockerEnabled": true,
    "secureBootEnabled": true,
    "defenderEnabled": true,
    "osMinimumVersion": "10.0.22631.0",
    "deviceThreatProtectionEnabled": true,
    "deviceThreatProtectionRequiredSecurityLevel": "medium",
    "scheduledActionsForRule": [{
      "ruleName": "PasswordRequired",
      "scheduledActionConfigurations": [ { "actionType": "block", "gracePeriodHours": 24 } ]
    }]
  }'

Device classes on the left, one enrolment and compliance path in the middle, the device CA gate, and the protected content and cloud targets — with premium content held in a remote enclave that never reaches the endpoint:

Lumina Media endpoint and UEM: device classes (corp laptops, editorial workstations, shared ops workstations) enrol via Intune Autopilot/ADE/shared-mode, prove compliance and CrowdStrike/Defender EDR posture, then pass a device Conditional Access gate to reach production content and Azure/AWS/PCI targets, while premium pre-release content stays in a remote AVD/Teradici enclave under TPN/MovieLabs controls and never lands on the endpoint

Premium content: the endpoint that never holds the file

The most media-specific endpoint control is the one that protects unreleased premium content, and it exists because the content, not the device, is the crown jewel. Editorial and colour work on pre-release titles is done on remote workstations — the editor sees pixels streamed from a segmented post enclave over AVD/Teradici PCoIP, and the media file, project and proxies stay in the enclave. On top of that sit the TPN / MovieLabs Enhanced Content Protection controls, and the rule is absolute: a stolen or unmanaged laptop, even one belonging to a legitimate editor, can render a review session but can never copy a frame out:

Control Mechanism What it enforces
Remote-only editing AVD/Teradici PCoIP; content in the post enclave No media/project files ever on the endpoint
Peripheral lockdown Session policy: no USB/clipboard/print/screen-capture Nothing leaves the pixel stream
Forensic watermarking Per-session visible + forensic marks A leaked frame traces to a user + session
Enclave egress control Deny-by-default on the media-processing segment Content cannot route to the internet
Studio-partner isolation Per-title B2B access package + TPN clearance A partner reaches only the one production they are cleared for

The parallel to healthcare’s unpatchable medical devices is exact: there, the network — not an endpoint agent — is the control that keeps a fragile device from reaching patient data; here, the remote enclave and its egress control — not the endpoint — are what keep a fragile-because-valuable asset from reaching an untrusted device. In both cases the sensitive thing is engineered so it can never be where it must not be.

Streaming platform architecture

Everything the previous sections built — the landing zones, the two identity planes, the network — exists to serve one transaction 45 million times at once: a viewer presses Play and video appears in under two seconds. The streaming platform is the consumer-facing plane of Lumina Media: the ~40 microservices that turn a signed-in device into an authorized, entitled, personalised playback session. It is where the paywall lives, where the catalog is browsed, where continue-watching remembers where you stopped, and where the concurrency ceiling that separates a Basic plan from a Premium one is enforced. It is almost entirely Tier-1 — a viewer must never see an outage — so it runs active/active across all three Azure regions (East US 2, West Europe, Southeast Asia) and the three AWS regions, with region-local reads and short-lived state.

The organising principle is a hard separation between the control path (which decides whether you may watch, in ≤150 ms) and the data path (the manifests and segments the CDN delivers, which the platform never touches per-request). The control path is stateless request/response over the customer-API segment; the data path is signed URLs the player redeems directly at the multi-CDN edge. That split is what lets a handful of entitlement pods authorize tens of millions of concurrent viewers — the platform authorizes the session, not every segment.

The consumer plane reads left→right as client → edge/API → entitlement/session → platform services → data, with concurrency enforced in the middle band.

Lumina streaming platform: client apps and CIAM token flow through Front Door and the playback API into the entitlement, session and concurrency services, then to catalog, recommendation and watch-history services backed by Cosmos/DynamoDB, PCI-isolated billing SQL and a Redis entitlement cache

The services inventory

Every consumer-plane service, its tier, how it is made highly available, the data classification it touches and the scale it must sustain. The scale column is the design point, not a wish — the playback API and entitlement service are sized for a live-final surge, everything else for steady-state plus headroom.

Service Tier HA model Data class Design scale
Registration / profile Tier-1 Active/active, region-local writes, Cosmos multi-region PII (GDPR/CCPA) 120M accounts, ~300M profiles
Subscription / billing Tier-1 Active/active reads; single-writer ledger per region + async recon PCI-DSS (cardholder) 120M subs, ~5M txns/day
Entitlement Tier-1 Stateless pods + Redis cache, region-local Derived (plan/geo flags) 8–10M authz/min at peak
Playback API + session Tier-1 Stateless AKS/EKS, HPA on RPS, per-region Session token 45M concurrent sessions
Catalog / metadata Tier-1 (read) Read-replicated + CDN-cached; single publish origin Public + embargoed ~500K titles, >1M req/s reads
Search / discovery Tier-2 OpenSearch / Azure AI Search cluster per region Public ~200K queries/s peak
Recommendation Tier-2 Precomputed rows + online serving; degrades to editorial Behavioural (PII) 120M profiles, rows refreshed daily
Watch history / continue-watching Tier-1 write High-write Cosmos/DynamoDB, last-write-wins per profile Behavioural (PII) ~2M position writes/s
Device management + concurrency Tier-1 Redis session registry, region-local, global fraud rollup Device fingerprint 45M active + registered devices
Notifications Tier-2 Event Hubs / SNS fan-out, at-least-once PII (contact) ~50M pushes/day
Customer care / agent tools Tier-2 ServiceNow + read APIs, RTO ≤4h PII + case data ~2M contacts/mo
QoE / playback telemetry Tier-2 High-scale event ingest (covered in the analytics core) Behavioural 10s of M beacons/min

Three inventory rules keep this honest. First, billing is the only PCI-scoped service in the consumer plane — it lives in its own subscription (lm-lz-ciam for auth, a separate PCI-scoped billing enclave for the ledger), and the playback path never reads the card ledger; it reads a cached entitlement flag that billing publishes. Second, recommendation and search are Tier-2 by deliberate choice: if either is degraded the home screen still renders from editorial rows and a cached catalog, because a viewer who cannot get a personalised row must still be able to watch. Third, watch-history is Tier-1 for writes but tolerant of eventual consistency — losing the last few seconds of a resume position is acceptable; losing the write path is not, because “continue watching” is the single most-used row on every home screen.

The playback session — the money path

This is the sequence that must complete in the sub-2-second video-start budget. Read it as a strict latency contract: every hop has a budget, and the sum of the p95s must leave room for the player to buffer its first segments.

  1. App start — the device already holds a CIAM JWT from Azure AD B2C / Amazon Cognito (workforce identity is never involved). The access token is short-lived (~15 min) with a rotating refresh token.
  2. Browse — the home screen renders from the catalog API (CDN-cached metadata) and precomputed recommendation rows. No entitlement call yet; browsing is free.
  3. Play pressed — the app calls POST /play on the playback API with the content id, a device id and the JWT.
  4. Authorize — the playback API calls entitlement synchronously: is the subscription active, is the title licensed in this geo, does the plan include it (e.g. 4K only on Premium), is the device type allowed, and — critically — is a concurrency slot free.
  5. Session issued — entitlement returns a decision; the session service mints a short-lived, per-session playback token and registers a concurrency slot keyed to the account.
  6. Manifest + license URLs — the playback API returns a signed, per-session manifest URL (SSAI-personalised for AVOD/live), the DRM license URL, and the CDN steering hint.
  7. Fetch manifest — the player redeems the signed URL at the multi-CDN edge (token validated at the edge, not the origin) and pulls the CMAF manifest.
  8. License — the player requests a DRM license, presenting the playback token; the license service validates the token, applies HDCP/output rules and returns the keys.
  9. Play + heartbeat — the player decrypts and renders, sends a heartbeat to the session service every ~60 s to hold the concurrency slot, and streams QoE beacons to analytics.
  10. Teardown — on stop, or after a missed-heartbeat timeout, the session slot is released.

Written as a latency contract, the same path with budgets and the failure that each hop must not cause:

# Step Service Call p95 budget Failure it must not cause
1 Token present CIAM (cached JWT) 0 ms Expired token → silent 401, no play
2 Browse Catalog + reco GET /home ≤200 ms (edge) Reco down blanks the screen
3 Play Playback API POST /play ≤50 ms API 5xx on the play tap
4 Authorize Entitlement POST /authz ≤120 ms Ledger read on hot path
5 Session Session svc mint token + slot ≤40 ms Double-count a slot
6 URLs Playback API build signed URLs ≤30 ms Un-signed / long-lived URL
7 Manifest Multi-CDN edge GET manifest ≤150 ms Origin hit on every start
8 License DRM license svc license request ≤200 ms License issued without token
9 First buffer Player + CDN fetch segments ≤800 ms Rebuffer at start (>0.4%)

The POST /play request and response make the contract concrete. The response carries everything the player needs and nothing it must re-derive — the manifest URL is already CDN-steered and signed, the license URL is bound to the session, and the heartbeat interval is server-controlled so concurrency policy can change without a client release:

// POST /v3/play  (Authorization: Bearer <B2C/Cognito JWT>)
{
  "contentId": "ttl_90f3a2",
  "deviceId": "dev_7c1e-appletv-4k",
  "capabilities": { "codecs": ["hvc1", "avc1"], "hdcp": "2.2", "hdr": ["hdr10", "dv"] }
}

// 200 OK  — session authorized
{
  "sessionId": "sess_b81c…",
  "playbackToken": "eyJ… (300 s TTL, per-session)",
  "manifest": "https://cdn-steer.luminamedia.net/v/ttl_90f3a2/master.m3u8?sig=…&exp=…",
  "drm": {
    "licenseUrl": "https://lic.luminamedia.net/cenc",
    "schemes": ["com.apple.fps", "com.widevine.alpha", "com.microsoft.playready"]
  },
  "heartbeatSec": 60,
  "cdnHint": "cdn-a",           // client SDK may re-steer on QoE
  "ssai": { "enabled": true, "manifestType": "personalised" }
}

// 403 — denied, with a machine-readable reason the app can act on
{ "error": "concurrency_limit", "plan": "standard", "activeStreams": 2, "maxStreams": 2 }

Device management and concurrency control

Concurrency is where the plan tiers become real money and where credential-sharing fraud is fought. The rule is simple to state — a plan permits N simultaneous streams — and unforgiving to implement at 45M concurrent, because the count must be globally correct, cheap to check on the hot path, and self-healing when a device dies without saying goodbye. Lumina keeps a region-local Redis session registry for the fast check and an async global rollup for cross-region correctness and fraud analytics.

Plan Max streams 4K / HDR Enforcement point Signal that frees a slot
Basic (AVOD) 1 No Entitlement at /play Heartbeat timeout (180 s) or explicit stop
Standard 2 1080p Entitlement + session registry Heartbeat timeout or LRU evict on 3rd play
Premium 4 Yes (4K/HDR) Entitlement + session registry Heartbeat timeout or LRU evict on 5th play
Household add-on +2 Per base plan Registry + household fingerprint As above, within household device set

When the ceiling is hit the platform can either deny the new play (403 concurrency_limit, shown above) or LRU-evict the oldest active stream and admit the new one — Lumina evicts for the account owner’s own devices and denies across a suspected-shared boundary. The fraud controls layer on top of the raw count: device fingerprinting, IP/geo clustering, and impossible-travel detection (the same account streaming from two continents inside an hour) feed a risk score that tightens enforcement (step-up re-auth, household verification) without blocking legitimate travel. None of this touches billing — it reads the cached plan flag entitlement already holds.

Data-store selection

The consumer plane is polyglot on purpose: each workload gets the store whose consistency and scale model fits, and the multi-region model is chosen per store so no single global write bottleneck exists on the hot path.

Workload Store Multi-region model Consistency Why
Profile / preferences Cosmos DB / DynamoDB Multi-region writes Session Region-local writes, low read latency
Entitlement cache Redis (region-local) Independent per region Best-effort + TTL Sub-ms authz reads, rebuildable
Session / concurrency Redis + async rollup Region-local + global merge Eventual (rollup) Fast slot check, global fraud view
Watch history Cosmos / DynamoDB Multi-region, LWW Eventual High write, tolerant of small loss
Catalog / metadata Cosmos + search index + CDN Single publish → fan-out Read-your-publish One source of truth, cached everywhere
Billing ledger Azure SQL / RDS (PCI enclave) Single-writer region + async recon Strong (per region) Financial integrity, PCI scope

The load-bearing decision is that entitlement reads never cross a region and never hit the billing database. Billing publishes a compact entitlement document (plan, status, geo rights, renewal) into the region-local Redis cache on every change; the hot path reads that cache. A regional Redis loss is a rebuild-from-source event, not an outage — entitlement fails closed on paid content and open on free-tier content while the cache warms, so the worst case is a brief period where paid titles ask for a retry, not a global paywall failure.

Content ingest, processing and origin

If the streaming platform is how a viewer starts watching, this is how the bits they watch are made. The supply side of Lumina Media takes two very different inputs — a file (a VOD mezzanine master arriving from a studio, a post house or the on-prem archive) and a stream (a live contribution feed from a stadium or the LA/London MOC) — and converts both into the same output: a set of adaptive-bitrate, multi-DRM, CMAF-packaged renditions sitting on a durable origin behind a shield, ready for the multi-CDN to deliver. The governing philosophy is encode-once, package-efficiently: spend CPU once to produce an optimal bitrate ladder, package that single set of media segments once as CMAF, encrypt it once with cbcs, and let HLS and DASH both reference the same bytes. Doing this wrong — re-encoding per format, re-encrypting per DRM, or transcoding on demand — multiplies both cost and the surface where premium content can leak.

The pipeline is a strict left→right sequence with a gate at every stage: ingest → validate/QC → transcode → package + DRM → origin + shield. Premium content (pre-release films, live sports) runs the same shape on an isolated pipeline with its own keys, its own quarantine and TPN/MovieLabs controls.

Lumina ingest and processing: VOD and live contribution enter, pass QC and malware/TPN quarantine, transcode to a per-title ABR ladder on the encode fleet, package once as CMAF with cbcs multi-DRM keys via SPEKE, and land on a durable versioned origin behind an origin shield feeding the multi-CDN

The ingest interfaces

Two front doors, four real protocols, each with its own security posture and SLA. VOD ingest is a durable object drop plus an API; live contribution is a low-latency transport with forward error correction and 1+1 path redundancy for anything premium.

Path Protocol Typical source Security SLA / redundancy
VOD file ingest S3 Multipart / Blob (accelerated) + REST Studios, post houses, archive Signed upload URL, per-partner key, VPC/PE Durable 11-nines; retry idempotent
Aspera / signed transfer FASP over UDP Large mezzanine masters Token auth, encrypted at rest on land High-throughput WAN, resumable
Live contribution (standard) SRT (caller/listener) Remote encoders, MOC AES-128/256, stream id auth Single leg for non-premium
Live contribution (open std) RIST (TR-06) Interop / third-party feeds DTLS, SRP auth ARQ FEC, bonded paths
Live contribution (premium) Zixi Sports, tentpole live Encrypted, SST error correction 1+1 hitless, dual-region ingest

For premium live the contribution is dual-region and 1+1 hitless: the same feed arrives on SRT into one region and Zixi into another, and the encoder switches legs on packet loss without a visible glitch. That is the single most important reliability decision on the supply side — a fibre cut or a stadium uplink failure during a final cannot be allowed to black out 45 million viewers.

QC, validation and malware — the deny-by-default gate

Nothing transcodes until it passes. Every asset — file or stream sample — runs a battery of automated checks, and premium files additionally sit in a no-public quarantine bucket until a malware verdict clears them. This is both a quality gate (a broadcaster cannot ship clipped audio or the wrong aspect ratio) and a content-security gate (a compromised upload must never reach the origin).

Gate Check Tool / method Fail action
Container / codec Valid MP4/MXF, expected codec & profile MediaInfo / probe Reject, notify partner
A/V integrity Sync, dropped frames, black/freeze Automated QC (Baton/Vidchecker-class) Quarantine, human review
Loudness EBU R128 / ATSC A/85 compliance Loudness meter in QC Auto-correct or reject
Photosensitivity Harding / PSE flash test PSE analyser Block until cleared (regulatory)
Subtitle / caption Presence, timing, language coverage Caption validator Warn; block if contractually required
Malware Signature + heuristic scan EDR/AV on quarantine bucket Hard block, isolate, alert SecOps
Content-security Watermark/fingerprint, key class MovieLabs/TPN premium path Route to isolated pipeline

The per-title ABR ladder

The transcode stage is where cost and quality are decided. Lumina does not use one fixed bitrate ladder for every title — it uses per-title encoding: a complexity analysis pass builds a convex hull of quality-vs-bitrate across candidate resolutions and QP settings, and the ladder is trimmed to the points that actually improve VMAF. A simple animated title might top out at 1080p/3.5 Mbps at target quality; a grainy action film needs the full 4K/16 Mbps rung. The result is 20–30% less egress at equal perceptual quality — at Lumina’s scale, that is the difference between viable and ruinous CDN bills.

Rendition Resolution Video codec Bitrate (target) Notes
2160p (4K) 3840×2160 HEVC (hvc1) 14–16 Mbps (HDR ~18) Premium plan only; HDR10/DV
1080p high 1920×1080 HEVC / AVC 5.0–6.0 Mbps AVC fallback for older devices
720p 1280×720 AVC (avc1) 3.0 Mbps Broad device baseline
540p 960×540 AVC 1.6 Mbps Mobile / constrained Wi-Fi
480p 854×480 AVC 1.1 Mbps Cellular
360p 640×360 AVC 0.65 Mbps Low-bandwidth floor
240p 426×240 AVC 0.30 Mbps Emergency rung, keeps play alive

A representative per-title encode job — the ladder is derived, not hand-set; the complexity pass sets the top rung and the packaging is CMAF fMP4 so the same segments serve HLS and DASH:

{
  "job": "encode_ttl_90f3a2",
  "input": "s3://lm-ingest-premium/masters/ttl_90f3a2.mxf",
  "perTitle": { "enabled": true, "metric": "vmaf", "targetVmaf": 93, "maxRes": "2160p" },
  "renditions": [
    { "name": "2160p", "codec": "hevc", "bitrate": 15000, "gop": 2, "bframes": 3, "hdr": "hdr10" },
    { "name": "1080p", "codec": "hevc", "bitrate": 5500, "gop": 2 },
    { "name": "720p",  "codec": "h264", "bitrate": 3000, "gop": 2 },
    { "name": "540p",  "codec": "h264", "bitrate": 1600, "gop": 2 },
    { "name": "360p",  "codec": "h264", "bitrate": 650,  "gop": 2 }
  ],
  "segmenting": { "container": "cmaf", "segmentSec": 4, "gopAligned": true },
  "audio": [ { "codec": "aac", "bitrate": 128 }, { "codec": "eac3", "bitrate": 384, "layout": "5.1" } ]
}

Packaging and multi-DRM — encode-once made real

The packaging stage produces one set of CMAF fMP4 segments and encrypts them once with CENC cbcs. The reason cbcs matters is history collapsing into simplicity: Apple FairPlay requires AES-CBC (cbcs), while Widevine and PlayReady historically used AES-CTR (cenc) — but modern Widevine and PlayReady now accept cbcs, so a single cbcs ciphertext, delivered as CMAF, can be decrypted by all three DRMs. That is the whole payoff of the encode-once philosophy: one media set, one encryption, three license paths. Keys are fetched from the DRM key service over SPEKE (Secure Packager and Encoder Key Exchange), the standard packager↔key-provider API that a multi-DRM SaaS or AWS SPEKE implements.

Delivery format Container DRM(s) CENC scheme Target devices
HLS (fMP4) CMAF FairPlay (+ WV/PR) cbcs Apple, Safari, tvOS, AirPlay
DASH CMAF Widevine + PlayReady cbcs (cenc legacy) Android, Chrome, Edge, smart-TV
HLS (legacy TS) MPEG-TS FairPlay cbcs Old iOS / STB fallback
Low-latency live CMAF (chunked) All three cbcs LL-HLS + LL-DASH players

Alongside the media, packaging emits the manifests (an HLS master .m3u8 and a DASH .mpd) plus thumbnails/trickplay (a sprite grid or I-frame playlist for scrubbing), subtitles/captions (WebVTT/IMSC1, multi-language), and audio renditions (stereo + 5.1/Atmos where licensed). The manifests are thin pointers into the shared CMAF segment set — the DASH SegmentTemplate and the HLS EXT-X-MAP/#EXT-X-STREAM-INF both address the same init and media segments, which is exactly why encode-once works:

#EXTM3U
#EXT-X-VERSION:7
#EXT-X-STREAM-INF:BANDWIDTH=5500000,CODECS="hvc1.2.4.L153.90,mp4a.40.2",RESOLUTION=1920x1080
1080p/stream.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3000000,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1280x720
720p/stream.m3u8
#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES,KEYFORMAT="com.apple.streamingkeydelivery",URI="skd://ttl_90f3a2"

Origin, shield and orchestration

The packaged output lands on a durable, versioned origin (Blob / S3, object-locked for premium) and is fronted by an origin shield — a single caching tier per region that collapses concurrent cache-miss requests into one origin fetch. When a new release unlocks and ten million players ask for the same first segment inside a second, the shield answers all but one from a single origin read; without it, a launch is a self-inflicted DDoS on your own origin. The whole pipeline is driven by a queue-based orchestrator (Step Functions / a durable workflow over SQS/Service Bus) where every stage is idempotent and keyed so a retry re-runs a stage safely rather than duplicating output — essential when a transcode of a two-hour master can fail at 90% and must resume without paying for the first 90% again. This is the classic event-driven, at-least-once pipeline pattern applied to media: every hop assumes redelivery and defends with an idempotency key.

Stage Queue / trigger Idempotency key Retry policy On repeated failure
Ingest register Object-created event assetId + etag 3× exp backoff DLQ + partner notify
QC / scan qc-jobs queue assetId + qcVersion Quarantine, human review
Transcode encode-jobs queue assetId + ladderHash 3× (checkpointed) DLQ + alert, keep master
Package + DRM package-jobs queue assetId + drmKeyId DLQ, block publish
Publish to origin publish queue assetId + renditionSet Alarm; origin never partial
Cache warm scheduled / launch event titleId + region best-effort Log; delivery still works

Live streaming and channel operations

Live is where every weakness in the supply chain becomes visible in real time to millions of people at once, with no second take. A VOD encode that fails can be retried; a dropped live final is a headline. Lumina runs two distinct live shapes on shared infrastructure — linear channels (24×7 FAST and scheduled channels with a playout schedule) and live events (sports and news, spun up per event, torn down after) — and both are engineered around one uncompromising rule: on event day, nothing scales from cold. Contribution is redundant, encoders are hot in two availability zones, origins are active/active, and the multi-CDN is pre-warmed. The latency target is glass-to-glass under 8 seconds using LL-CMAF, which is aggressive enough that every stage’s delay contribution is budgeted.

The live path reads: dual contribution → multi-zone encode/package → playout + SSAI → origin redundancy → multi-CDN.

Lumina live operations: dual SRT/Zixi contribution from the LA and London MOCs feeds encoders running hot in two availability zones producing LL-CMAF chunks, through linear playout with SCTE-35 markers and SSAI ad insertion, into active/active dual origins behind a shield, delivered by three CDNs to 45M concurrent viewers under an 8-second latency budget

Contribution redundancy

The first leg of a live event is the one you least control — it rides public or leased networks from a venue. Lumina engineers it as 1+1 hitless: two independent transports, diverse carriers, diverse ingest regions, with the downstream encoder reconstructing a clean signal by switching between them packet by packet.

Leg Protocol Path Ingest region Role
Primary SRT (listener) Venue → LA MOC → cloud us-east-1 / East US 2 Active
Backup Zixi Venue → London MOC → cloud eu-west-1 / West Europe Hitless standby
Tertiary (marquee) RIST bonded Cellular bond from venue Nearest region Break-glass

The rule enforced by alarm: the moment the channel runs on a single leg, page. Redundancy that silently degrades to a single point of failure is worse than none, because it hides the risk until the second leg also fails.

The low-latency budget

Sub-8-second live is not one trick; it is a chain of small delays kept individually small. Chunked CMAF (a 4 s segment delivered as eight 500 ms chunks over chunked-transfer encoding) lets the player start rendering a segment before the encoder has finished writing it. LL-HLS adds partial segments (parts) with preload hints and blocking playlist reloads; LL-DASH uses availabilityTimeOffset and chunked SegmentTemplate.

Stage Delay contribution Technique to hold it
Contribution + de-jitter ~1.0–1.5 s SRT/Zixi buffer sized to path jitter
Encode ~1.0 s Low-latency GOP, look-ahead capped
Package (chunked CMAF) ~0.5–1.0 s 500 ms chunks, chunked transfer
Origin + shield ~0.3–0.5 s Shield co-located, request collapse
CDN edge ~0.5–1.0 s LL-HLS parts / LL-DASH ATO passthrough
Player buffer ~2.0–2.5 s Tuned live-edge target, small buffer
Glass-to-glass < 8 s End-to-end probe alarms on breach

Playout, SCTE-35 and blackout

Linear channels are assembled by a playout service that follows a schedule (the electronic program guide), splices content and ads, and inserts SCTE-35 markers at ad and program boundaries. Those markers are the nervous system of monetization and rights enforcement: SSAI reads them to stitch personalised ads into each viewer’s manifest (VAST/VMAP creative from the ad-decision server), and blackout/geo rules read them to replace content with a slate in territories where a rights window forbids it. The markers travel from the transport stream into the manifests as HLS EXT-X-DATERANGE and DASH emsg/EventStream so the packager and SSAI stay frame-accurate.

Event SCTE-35 signal Manifest carriage Action
Ad break start splice_insert (out) HLS EXT-X-DATERANGE / DASH emsg SSAI splices personalised pod
Ad break end splice_insert (in) matching range close Return to content
Program boundary time_signal + segmentation DATERANGE with segmentation type EPG update, chapter mark
Blackout start time_signal (blackout) edge geo-rule + slate Replace with slate in territory
Blackout end time_signal (restore) edge rule lifts Resume live content

A representative ad-avail marker as it appears in an HLS manifest — SSAI keys off exactly this to decide where the pod goes and how long it runs:

#EXT-X-DATERANGE:ID="ad-8842",CLASS="com.lumina.ad",START-DATE="2026-07-09T20:15:00Z",\
  DURATION=90.0,SCTE35-OUT=0xFC30200000000000...   # 90 s avail, SSAI fills per viewer
#EXT-X-CUE-OUT:90.000
...ad segments spliced per-viewer by SSAI...
#EXT-X-CUE-IN

Event-day design

A live final is run to a script. The design freezes change, pre-provisions capacity, and rehearses failover before a single viewer arrives — because active/active multi-region only protects you if the standby is genuinely warm and the runbook has been walked.

Phase Action Owner
T-7 days Change freeze on all Tier-1 live services; capacity + quota review Live eng + platform
T-3 days Game-day: force contribution failover, origin failover, CDN drain Live ops + SRE
T-1 day Pre-warm CDN caches, pre-scale encoders/packagers hot in 2 AZ Live ops
T-2 h War room open; multi-CDN steering to lowest-VST config; monitors up Live ops + NOC
Live Watch per-leg health, VST, rebuffer, per-CDN QoE; single-leg = page NOC
Post Unfreeze; retro on any switch/failover; archive the event to VOD Live eng

VOD architecture

The VOD library is Lumina’s largest and oldest asset: ~400 PB of masters and renditions spanning half a million titles, most of which are watched rarely and a few of which are watched by tens of millions the week they launch. The architecture is shaped by that brutal distribution — a small hot head and an enormous cold tail — and by one economic fact: you cannot afford to keep 400 PB on hot storage, and you cannot afford to re-encode on demand. So VOD is built on two disciplines: transcode-once (every title is encoded to its full ABR ladder exactly once, at ingest, and the packaged CMAF is stored and reused forever) and tiered storage with lifecycle (renditions and masters migrate across hot/cool/cold/archive tiers by age and popularity, and are promoted back on demand).

The VOD path reads: upload → transcode-once → tiered storage → cache-warm → delivery. It reuses the ingest pipeline from earlier and adds the storage-economics and catalog-sync layers that make a library this size affordable. Lumina’s design here mirrors the reference VOD multi-DRM streaming architecture, extended to two clouds and six regions.

Lumina VOD architecture: mezzanine masters upload and register in the catalog, transcode once to a per-title ABR ladder packaged as CMAF with cbcs DRM, land across hot origin, cool/cold and archive tiers by age and popularity, are warmed to the origin shield and edge before launch, and are delivered long-tail by the multi-CDN with signed URLs

Tiered storage lifecycle

The single most important cost lever in the whole media estate. Masters go straight to the coldest tier — they exist only to re-encode from, so hours-long retrieval is fine. Renditions live where their audience is: new and popular titles Hot, mid-catalog Cool/Cold, long-tail Archive, promoted back the moment demand returns. This is the S3 storage-class lifecycle discipline applied at 400 PB, with the Azure Blob equivalents in lockstep.

Tier Access pattern Azure Blob AWS S3 class Retrieval What lives here
Hot New + popular renditions Hot S3 Standard Instant This week’s launches, top catalog
Cool Mid catalog, occasional Cool S3 Standard-IA Instant (fee) Titles cooling after launch window
Cold Long-tail renditions Cold Glacier Instant Retrieval ms–instant Rarely-watched but must play now
Archive Mezzanine masters Archive Glacier Deep Archive Hours (rehydrate) 400 PB of masters, re-encode source

The lifecycle is policy-driven, not manual. A representative object-lifecycle policy demotes renditions by age and archives masters immediately, while a promotion job (triggered by a demand signal or a scheduled re-release) rehydrates on the way back up:

// S3 lifecycle — renditions bucket (masters bucket sends everything to Deep Archive on day 0)
{ "Rules": [
  { "ID": "renditions-cooldown", "Filter": { "Prefix": "renditions/" }, "Status": "Enabled",
    "Transitions": [
      { "Days": 30,  "StorageClass": "STANDARD_IA" },
      { "Days": 90,  "StorageClass": "GLACIER_IR" },
      { "Days": 365, "StorageClass": "DEEP_ARCHIVE" } ] },
  { "ID": "masters-archive", "Filter": { "Prefix": "masters/" }, "Status": "Enabled",
    "Transitions": [ { "Days": 0, "StorageClass": "DEEP_ARCHIVE" } ] }
] }
// Azure Blob lifecycle management — equivalent tiers
{ "rules": [ { "enabled": true, "name": "renditions-cooldown", "type": "Lifecycle",
  "definition": { "filters": { "blobTypes": ["blockBlob"], "prefixMatch": ["renditions/"] },
    "actions": { "baseBlob": {
      "tierToCool":    { "daysAfterModificationGreaterThan": 30 },
      "tierToCold":    { "daysAfterModificationGreaterThan": 90 },
      "tierToArchive": { "daysAfterModificationGreaterThan": 365 } } } } } ] }

Renditions, trailers and optimization

Not every asset gets the same treatment. A flagship series carries the full ladder plus HDR and Atmos; a catalog filler carries a trimmed ladder; trailers and previews are tiny, always-hot, and aggressively cached because they front the discovery experience and must start instantly.

Asset class Rendition set Storage posture Notes
Tentpole / new release Full per-title ladder + 4K HDR + Atmos Hot, cache-warmed Highest quality, pre-warmed at launch
Standard catalog Per-title ladder to 1080p Hot → Cool by age Bulk of the library
Long-tail Trimmed ladder (≤720p common) Cold, promote on demand Rarely watched; keep it playable
Trailers / previews 1080p + mobile rungs Always Hot, long TTL Discovery surface, instant start
Masters (mezzanine) N/A (source) Archive / Deep Archive Re-encode source only

Cache warming and the long tail

The multi-CDN handles delivery, but for VOD the platform actively warms caches ahead of predictable demand rather than waiting for the first viewer to pay the origin round-trip. A tentpole premiere is a scheduled event: top renditions are pushed to the origin shield and key edge PoPs before the regional unlock, so the T0 surge is served from cache, not from a cold origin. The long tail is the opposite problem — millions of titles too rare to keep hot in every PoP — and there the origin shield does the heavy lifting, collapsing the occasional request and accepting a lower edge hit-ratio on obscure content while protecting the origin.

Trigger Warm strategy Scope
Scheduled premiere / launch Pre-push top renditions to shield + edge before unlock Per-region, staged by unlock time
Trending detection Reactive warm as plays ramp Popular PoPs
Editorial feature (home row) Warm trailer + first segments Global
Long-tail request Shield request-collapse, no proactive warm On-demand fill
New device/app launch Warm onboarding + demo content Targeted

Catalog and metadata sync

Metadata is the connective tissue: entitlement reads it to know rights windows, search indexes it, recommendation features off it, and the client renders it. Because it is read everywhere and written rarely, Lumina treats it as a single-publish, fan-out system — one authoritative publish per change, versioned, replicated asynchronously to region-local read replicas, so every one of the six regions serves a consistent view without a global write bottleneck.

Entity Source of truth Sync mechanism Region model
Title metadata Catalog master (single publish) Versioned doc → async fan-out Region-local read replicas
Rights / availability windows Rights management system Event on change → entitlement cache Region-local, embargo-aware
Artwork / images Asset service + CDN Immutable URLs, long TTL Global CDN cache
Search index Derived from catalog Reindex on publish Per-region cluster
Recommendation features Derived from catalog + behaviour Batch + streaming refresh Per-region serving store

The rule that ties the VOD library back to the streaming platform: catalog is the source of truth, and it is versioned. A title’s availability, its rendition set, its rights window and its artwork all carry a version; entitlement, search and the client all resolve against that version, so a change (a new territory unlock, a pulled title, a re-encoded rendition) propagates cleanly instead of leaving regions disagreeing about what exists. At half a million titles across six regions, version drift is the failure mode you design against from day one.

DRM, entitlement and content protection

This is where the money lives, so this is where the attackers, the password-sharers and the studios all point at once. Lumina’s studio partners contractually require MovieLabs Enhanced Content Protection and a TPN/CDSA-audited chain for premium and pre-window titles; the finance team wants password-sharing crushed; the viewer wants a play button that never argues. Those three pressures collapse into one subsystem: a DRM + entitlement plane that decides — for this play, on this device, in this region, right now — whether a decryption key is allowed to reach the player, and under what output rules. It is a Tier-1 service (RTO ≤15m, RPO ≤1m): if entitlement or the license service is down, 45M concurrent viewers see a spinner, so it runs active/active across Azure East US 2 / West Europe / Southeast Asia and AWS us-east-1 / eu-west-1 / ap-southeast-1.

The single most important architectural decision is to encrypt the content once and license it three ways. Lumina must reach Widevine (Android, Chrome, most smart-TVs), FairPlay (Apple/Safari) and PlayReady (Windows, Xbox, many STBs/TVs). Naïvely that is three encodes and three storage copies of a 400 PB library — unaffordable. The escape hatch is CENC (Common Encryption): one CMAF asset, encrypted once, that all three DRMs can license. The following table is the reason the whole pipeline is economically viable.

Below is the multi-DRM landscape Lumina actually ships against — the client reach, the security levels that gate resolution, and the output rules each system enforces.

DRM system Platforms it owns Security levels Container / scheme HDCP / output control Offline license
Google Widevine Android, Chrome, Chromecast, most smart-TVs, Fire TV L1 (HW TEE), L2, L3 (SW) CMAF/fMP4, cenc + cbcs HDCP version + type via policy Yes (persistent license)
Apple FairPlay (FPS) Safari, iOS, iPadOS, tvOS, macOS Hardware SVE (single tier) CMAF/fMP4, cbcs only HDCP enforced by platform Yes (persistent key)
Microsoft PlayReady Windows, Xbox, many STB/TV, Edge SL150 (dev), SL2000 (SW), SL3000 (HW) CMAF/fMP4, cenc + cbcs HDCP + output protection levels Yes (persistent license)
Single-asset target all of the above floor set per content tier one CMAF, cbcs policy set at license time policy-gated

The bottom row is the whole game: cbcs (AES-128 CBC with a pattern, 1:9 for video) is the one scheme all three systems support in CMAF, so a single cbcs-encrypted CMAF asset licenses to Widevine, FairPlay and PlayReady. cenc (AES-128 CTR) is retained only for legacy DASH clients that predate cbcs. Package cbcs for the modern estate and you carry one encrypted copy, not three.

Encryption scheme Cipher Pattern DRMs that accept it Use it for Avoid when
cbcs AES-128 CBC 1:9 (crypt:skip) Widevine, FairPlay, PlayReady Default — LL-CMAF, modern apps/TVs Very old DASH-only clients
cenc AES-128 CTR full-sample Widevine, PlayReady (not FPS) Legacy DASH fallback catalogue FairPlay in scope (it can’t)
Mixed asset both n/a don’t — doubles storage + keys Always avoid on new content

The license-acquisition flow

The player never holds a key it can inspect. The Content Decryption Module (CDM) inside the device — Widevine L1, FairPlay SVE, PlayReady SL3000 — generates a license challenge that only the license server (and the DRM root of trust) can answer. The playback control plane sits in front of the license server: the player first exchanges its session/playback token for a green light, then the license request is decorated with that entitlement so the license server issues a key only for the KIDs the viewer is allowed. The path is short and every hop is a checkpoint.

The steps below map exactly to the diagram; keep them side by side.

# Step Actor What is checked / carried Failure =
1 Get playback token Player → Playback API valid CIAM JWT, title id, device id 401/403, no play
2 Entitlement decision Playback API → Entitlement sub status, plan, rights window, geo, device, concurrency 403, blocked
3 Issue playback token Entitlement → Player short-lived JWT: KIDs, policy, exp
4 License challenge Player CDM → License svc DRM challenge + playback token invalid → denied
5 Key + usage rules License svc → Player content key(s) under output/HDCP policy non-compliant sink → SD/black
6 Decrypt + play Player CDM key held in secure world only

Here is the DRM and entitlement plane end to end — the player asks, entitlement decides, the multi-DRM service issues a policy-bound key, and every issuance is logged for anti-fraud.

Multi-DRM license and entitlement flow: a CMAF player requests a license, the entitlement service checks subscription status, concurrency, geo and device, the multi-DRM service issues a CENC content key under an HDCP and output policy, and every issuance is written to a fraud/audit store.

Keys, SPEKE and rotation

The packager and the license server must agree on content keys without a human ever touching plaintext key material. Lumina uses the SPEKE (Secure Packager and Encoder Key Exchange) contract — the packager POSTs a CPIX (Content Protection Information eXchange) document naming the key ids and DRM systems it wants; the key provider returns keys and the per-DRM PSSH/pssh and license-URL data. For VOD the keys are stable for the asset; for live channels the key rotates per period so a leaked key is worthless within minutes. Content keys are wrapped by a KEK in AWS KMS / Azure Key Vault (HSM) and never persisted in plaintext — the license service fetches and unwraps per request. See Azure Key Vault: secrets, keys and certificates for the HSM-backed key custody model this reuses.

A SPEKE/CPIX request from the packager to the key service looks like this (abridged CPIX — the packager asks for one cbcs key covering all three DRMs):

<!-- Packager → SPEKE key server (CPIX 2.3, cbcs, KID = content key id) -->
<cpix:CPIX id="asset-9f21-uhd" xmlns:cpix="urn:dashif:org:cpix"
           xmlns:speke="urn:aws:amazon:com:speke">
  <cpix:ContentKeyList>
    <cpix:ContentKey kid="2b6f0cc9-04e9-4c73-8f2a-9a1d4c8b1e77"
                     commonEncryptionScheme="cbcs"/>
  </cpix:ContentKeyList>
  <cpix:DRMSystemList>
    <!-- one KID, three systemIds → Widevine, FairPlay, PlayReady -->
    <cpix:DRMSystem kid="2b6f0cc9-04e9-4c73-8f2a-9a1d4c8b1e77"
                    systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"/> <!-- Widevine -->
    <cpix:DRMSystem kid="2b6f0cc9-04e9-4c73-8f2a-9a1d4c8b1e77"
                    systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2"/> <!-- FairPlay -->
    <cpix:DRMSystem kid="2b6f0cc9-04e9-4c73-8f2a-9a1d4c8b1e77"
                    systemId="9a04f079-9840-4286-ab92-e65be0885f95"/> <!-- PlayReady -->
  </cpix:DRMSystemList>
</cpix:CPIX>

Entitlement rules — the decision that matters

Entitlement is where “do you have Netflix” becomes “can this frame decrypt.” It is a server-side decision — the client is never trusted — evaluated on every license acquisition and re-validated on long plays via the token exp. The rule set is a conjunction: all must pass. Each rule below states the signal it reads, where the source of truth lives, and the deny action a viewer actually sees.

Rule Signal / source of truth Evaluated where Deny action
Subscription status active/grace/cancelled — billing + CIAM claim Entitlement API 403, “renew to watch”
Plan / tier SD vs HD vs UHD, add-on packs Entitlement API downgrade ladder or block
Concurrency active streams vs plan cap (2/4/…) Concurrency ledger block newest / evict oldest
Geo / territory client IP geo + account country + license rights Edge + Entitlement 403, geo-block screen
Rights window title availability (start/end), blackout Rights service “not available in your region/now”
Device class approved device, min security level Entitlement + license SD-only or block
Content tier floor premium/4K → HW DRM required License policy refuse HW-incapable CDM
Blackout (live) sports/event territorial blackout Live event control alt feed or block

The playback token that carries this decision to the license server is a tight JWT — short-lived, audience-scoped, and stripped of anything a shared link could reuse. These are the claims Lumina signs.

Claim Example Purpose Notes
sub acct_8c1a… (pairwise) account id (pseudonymous) never the email/PII
did dev_5f2b… device binding token bound to one device
kids ["2b6f0cc9…"] keys this play may license scopes the license request
plan uhd resolution/output ceiling drives HDCP floor
geo GB territory at issue time re-checked at edge
cc 2 concurrency lease id ties to the ledger slot
exp +120s (license) short expiry license token ≠ session token
pol hdcp22-type1 output policy ref resolves to usage rules

A denied decision is explicit and cheap — the license server is never reached:

// Entitlement API → player (concurrency exceeded)
{ "decision": "deny", "reason": "concurrency_limit",
  "plan_cap": 2, "active_streams": 2,
  "message": "You're watching on the maximum number of devices for your plan.",
  "retry_after_s": 30 }

HDCP, robustness and premium/4K

Studios do not accept “we encrypted it.” They require that a 4K/UHD frame can only ever reach a hardware-protected output. That is enforced by two knobs baked into the license: the DRM security-level floor (Widevine L1, PlayReady SL3000, FairPlay hardware) and the HDCP requirement on the display link. A device with a software CDM, or an HDMI capture path without HDCP 2.2, is stepped down to SD or refused — not blocked with an error, just quietly denied the UHD key. This is the MovieLabs ECP contract in practice.

Content tier DRM level floor HDCP required Max resolution if not met Extra controls
Pre-theatrical / early window WV L1 · PR SL3000 · FPS HW HDCP 2.2 Type-1 refuse (no play) forensic watermark, TPN chain
UHD / 4K / HDR WV L1 · PR SL3000 · FPS HW HDCP 2.2 Type-1 step down to HD/SD disallow analogue out
HD WV L1/L3 · PR SL2000 HDCP 1.4+ step down to SD
SD / catalogue WV L3 · PR SL2000 none play
Live premium sport WV L1 · PR SL3000 HDCP 2.2 step down + blackout key rotation, low-latency

Anti-fraud: credential sharing and token abuse

Password-sharing is not a DRM break — the keys are fine — it is an entitlement problem, and Lumina fights it in the concurrency ledger and the anti-share scorer, not the CDM. The controls below turn “one login, ten households” into an economically pointless exercise without punishing a legitimate family on holiday.

Threat Signal Control Response
Password sharing plays from many distinct home networks/geos device + household clustering, concurrency cap cap streams, prompt “add a member”
Concurrency abuse streams > plan cap strongly-consistent stream ledger + lease TTL block/evict oldest heartbeat
Token replay same playback token from many IPs/ASNs bind token to did, tight exp, one-time nonce reject, force re-auth
License farming high license-request velocity per account rate-limit + anomaly score throttle, step-up challenge
Impossible travel GB→SG in 60s on one account geo-velocity model challenge / soft-block
Manifest/link scraping signed-URL reuse off-device edge token bound to session + IP edge 403 at expiry

Two rules keep this from becoming a support nightmare. First, the license token is short (≈120s) and device-bound; the session token is longer but only unlocks requesting a license, never a key. Second, denials are graded — a first anomaly gets a step-up challenge, not a lockout, so churn stays low. Every issuance is logged (account, KID, device, geo, resolution, decision) into the audit store, which does triple duty: fraud model training, TPN forensic evidence, and windowing/royalty reporting.

Offline playback (download-to-go) gets its own policy because a persistent license is a key sitting on a device. Lumina scopes it hard.

Offline control Policy Why
Persistent license TTL 30-day validity, 48h play window once started limits key exposure
Renewal requires online entitlement re-check catches cancellations
Max downloads per-plan device/title cap limits sharing
Security level HW CDM only for HD+ offline studio requirement
Territory licensed at download, re-checked on renew rights compliance

CDN, multi-CDN and edge delivery

Bytes-to-eyeballs is 95%+ of Lumina’s egress and the single biggest driver of both QoE and cloud/CDN spend. One CDN is a single point of failure at 45M concurrent, a single pricing negotiation, and a single set of regional weak spots. So Lumina runs three commercial CDNs (CDN-A / CDN-B / CDN-C) in front of a shielded origin, and steers traffic between them on live quality and cost signals. The targets are non-negotiable: VST p95 < 2s, rebuffer ratio < 0.4%, playback-failure < 0.5%, live latency < 8s (LL-CMAF), 99.99% availability for Tier-1 delivery. Hitting those at scale, across three CDNs, is an engineering discipline, not a purchase.

Here is the delivery path — viewer and client steering pick a CDN from live scores, DNS steering covers clients that can’t, the edge authenticates and filters, and an origin shield protects the packager.

Multi-CDN edge delivery: a viewer's player and client-side steering SDK pick a CDN using RUM and synthetic scores, DNS steering resolves to CDN-A, CDN-B or CDN-C, an edge WAF and signed-token check gate the request, and an origin shield collapses cache misses onto the packager origin.

Steering: how a session picks a CDN

Steering happens in two layers. The client-side SDK in the player holds a ranked CDN list and can switch mid-stream, on the next segment, when it sees rebuffering or 5xx — this is the fast loop that saves a live final. DNS steering is the slow, coarse loop for clients that can’t run the SDK (some TVs/STBs), routing by geo/latency at resolve time. Both are fed by a decision service that scores each CDN per region/ASN from RUM beacons and synthetic probes. The signals below are what actually move traffic.

Signal Source Cadence Steering effect
Rebuffer ratio RUM (player beacon) seconds down-weight CDN in region/ASN
VST (start time) RUM seconds prefer faster CDN for cold start
HTTP error rate (4xx/5xx) RUM + edge logs seconds fast fail-away on browning CDN
Throughput / goodput RUM seconds pick highest sustainable ladder
Synthetic availability probes per POP/region ~1 min pull CDN from rotation on outage
Cost / commit tier billing model daily spill to cheaper CDN once QoE-safe
Region / ASN affinity geo-IP per session keep last-mile short
Live vs VOD class request context per session route live to lowest-latency CDN

The three CDNs are not interchangeable clones — Lumina assigns them roles so steering has a sane default and a clear failover order.

CDN Primary role Steered by Notes
CDN-A primary for Tier-1 live + top VOD QoE-first best low-latency footprint, origin-shield tuned
CDN-B co-primary / regional strength QoE + geo strongest in EU/APAC pockets, failover for A
CDN-C cost spill + burst cost-weighted absorbs long-tail VOD + overflow once QoE safe

Per-asset cache policy — the highest-leverage config

The most common self-inflicted outage in streaming is a wrong TTL on a live manifest: cache a low-latency playlist for 10s and every player replays a stale segment list and stalls. Each asset type has a different correctness contract, and the cache policy must match it exactly. This table is the reference the CDN configs are generated from.

Asset type Path pattern Edge TTL Cache key Notes
Live manifest (LL-CMAF) *.m3u8 / *.mpd (live) 1-2 s path + session-agnostic must refresh each segment period
VOD manifest *.m3u8 / *.mpd (vod) 30-60 s path rarely changes post-publish
Media segment *.cmf* / *.m4s / *.ts long / immutable (7-30 d) path (+ no query) content-addressed, never mutates
Init / key manifest init.mp4, *.pssh long path stable per rendition
Image / artwork *.jpg *.webp (/img/*) 7-30 d path + resize params poster/thumb, heavy long-tail
Playback API /api/*, /entitlement/* no-store dynamic, per-user, never cache
DRM license /license/* no-store key material, never cache
SSAI manifest /ssai/*/manifest no-store (per-session) session token unique per viewer

Two rules make this safe: media segments are immutable and versioned in the URL so a long TTL is always correct (a new encode = a new path), and anything per-user or key-bearing is no-store so a shared cache can never leak one viewer’s entitlement or key to another.

Edge security: signed URLs, tokens, WAF

Every media URL is signed and every edge validates it before touching origin. Lumina uses short-lived signed URLs / edge JWTs bound to path, expiry and (where possible) client IP/ASN, so a leaked manifest link dies at exp and can’t be farmed. On CDNs that support edge compute, a small function verifies the token; the CloudFront-class pattern is a signed URL with a canned/custom policy, e.g.:

# Signed segment URL — expires in 300s, path-scoped, one session
https://cdn-a.lumina.tv/vod/asset-9f21/uhd/seg_0421.cmfv
  ?Expires=1751990400
  &Signature=Jx8f...~redacted~...q2
  &Key-Pair-Id=K2LUMINA9F21
  &tok=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...   # session JWT: exp, path, ip, did

And the edge check (edge-function pseudocode, run before origin fetch):

// Edge token validation (runs at every CDN POP, pre-origin)
function onViewerRequest(req) {
  const t = verifyJwt(req.qs.tok, EDGE_PUBKEY);          // ES256, rotated
  if (!t || t.exp < now())        return deny(403, "expired");
  if (!req.uri.startsWith(t.path)) return deny(403, "path");
  if (t.ip && t.ip !== req.clientIp && !sameAsn(t.ip, req.clientIp))
                                   return deny(403, "ip");
  if (isBlockedGeo(req.geo, t.rights)) return deny(451, "geo");
  return allow(req);   // WAF/bot/DDoS rules run in front of this
}

The full edge security stack layers on top of token validation, and it all runs at the edge so attacks never reach origin. The WAF/bot layer reuses the same pattern as Azure Application Gateway with WAF and mTLS at L7.

Edge control What it stops Where Config note
Signed URL / JWT link sharing, hotlinking every POP, pre-origin ES256, exp ≤ 300s, path-scoped
Token→session binding token replay/farming edge function bind to did + IP/ASN
WAF (L7) injection, bad requests edge managed + custom rules on /api
Bot management credential stuffing, scraping edge challenge on /login, /manifest
DDoS (L3/4 + L7) volumetric + app floods edge/scrubbing always-on, per-CDN
Rate limiting license/API abuse edge per-token + per-IP buckets
Geo / rights block territorial violations edge 451 on blacked-out territory

Origin shield, warmup and monitoring

Behind the three CDNs is one logical shielded origin. Each CDN points at a single origin-shield POP that collapses concurrent misses — so a live scale-up or a Friday launch that spikes 10M cold requests hits the packager once per segment, not 10M times. Tiered caching plus request collapsing is what keeps origin CPU flat during a drop. For launches and big live events, Lumina pre-warms: it pushes the bitrate ladder + init/key manifests into every CDN before the doors open, so the first wave hits warm edges. This is the CDN sibling of the origin design in Azure Front Door: routing and caching.

Finally, you cannot steer what you cannot see. Lumina measures per-CDN, per-region, per-ASN, continuously, and both the QoE and the cost sides drive steering.

Metric Target / watch Drives Source
Cache hit ratio (offload) > 95% segment, > 90% edge shield/cost tuning CDN logs
VST p95 < 2 s steering weight RUM
Rebuffer ratio < 0.4% steering, fail-away RUM
Playback failure < 0.5% incident + steering RUM
Edge 5xx rate < 0.1% pull CDN from rotation edge logs
Origin egress / offload maximise offload shield health, cost origin + CDN
$ per GB / commit burn vs contract tier cost-spill steering billing
Live latency (glass-to-glass) < 8 s live encode/CDN tuning RUM + synthetic

Ad-tech and monetization

Lumina makes money five ways at once — SVOD subscriptions, AVOD ad-supported tiers, TVOD rentals/buys, FAST free ad channels, and live sports with premium ads — and the streaming architecture has to serve all of them from the same content without letting the ad plane ever endanger editorial delivery or premium content security. The centrepiece is SSAI (server-side ad insertion): instead of the player calling an ad SDK (which ad-blockers strip and which breaks on TVs), the ad is stitched into the video stream server-side so the player sees one seamless, un-blockable CMAF playlist.

Here is the ad path — the player asks for one manifest, SSAI reads the SCTE-35 markers and calls the ad-decision server, editorial and ad content stay on separate origins, and the stitched manifest ships through the same multi-CDN with server-side impression tracking.

SSAI ad-insertion flow: the player requests a personalised manifest, the SSAI stitcher reads SCTE-35 markers and calls the VAST/VMAP ad-decision server, editorial and ad segments are kept on separate origins, the stitched manifest is delivered through the multi-CDN, and impressions are tracked server-side.

How SSAI stitches an ad break

For live, the encoder emits SCTE-35 markers (splice_insert / time_signal) at the exact PTS where a break may open; the SSAI service turns those into ad opportunities and, per session, fills them from the ad decision. For VOD, break positions come from a VMAP document (pre/mid/post-roll cue points). The critical constraint: the ad decision call must never stall playback — it is capped, and on timeout the stitcher inserts a slate or house ad. Ads and content share one CMAF encoding profile so the splice is seamless (mismatched codecs = a stall at the boundary). The flow, step by step:

# Step Component Detail / guardrail
1 Manifest request Player → SSAI per-session personalised manifest URL
2 Read cue points SSAI SCTE-35 (live) / VMAP (VOD) → break plan
3 Ad decision SSAI → ADS (VAST/VMAP) user + context macros, timeout ≤ ~1s
4 Fill or fallback SSAI creatives returned, else slate/house ad
5 Transcode-match Ad origin ads pre-conditioned to content ABR profile
6 Stitch manifest SSAI insert ad segments at PTS boundary
7 Deliver Multi-CDN stitched segments, per-asset cache
8 Track impression SSAI (server-side) quartile beacons, immune to blockers

The ad-decision request carries targeting without leaking PII — the VAST tag URL is built with macros the ADS/SSP resolves:

# SSAI → Ad Decision Server (VAST 4.x tag, macros resolved per session)
https://ads.lumina.tv/vast?
  pod=preroll&dur=90&                 # break duration to fill (s)
  content=asset-9f21&genre=sport&livestream=1&
  ip=[CLIENT_IP]&ua=[USER_AGENT]&
  gdpr=1&gdpr_consent=[CONSENT]&us_privacy=[USP]&   # consent passed through
  ppid=[PSEUDO_ID]&                   # pseudonymous, never account/email
  cb=[CACHEBUSTER]&maxpods=3

SCTE-35 conditioning deserves its own note: a marker that arrives late clips the programme, and one that isn’t frame-accurate causes a visible glitch at the join. Lumina honours the pre-roll delay in the splice and conditions live content to frame-accurate IDR boundaries so the stitch is invisible.

Editorial and ad path separation

Premium content carries a TPN/MovieLabs chain of custody; ad creatives come from dozens of third parties and change constantly. Mixing them in one pipeline would let an ad-ops mistake corrupt editorial delivery or contaminate the content-security chain. So Lumina keeps them on separate origins and pipelines, joined only at the stitched manifest.

Concern Editorial path Ad path Why separate
Origin packager/editorial origin dedicated ad origin blast-radius isolation
Security DRM/CENC, TPN chain standard, no premium keys don’t expose keys to ad flow
Change cadence slow, governed release fast, per-campaign ad-ops can’t break content
Transcode full ABR ladder, per-title conditioned to match profile seamless splice
Failure mode Tier-1, must not drop fallback to slate/house ad ads degrade gracefully
Caching per-asset (segments long) creatives cacheable across sessions dedupe at edge

Ad delivery has its own QoE that Lumina measures separately from content QoE — an ad that buffers is a lost impression and a viewer annoyance, but it must never be confused with a content stall in the dashboards.

Ad QoE / quality metric Target Meaning
Ad VST (start time) < 1 s ad begins without a gap at the splice
Ad fill rate high, per pod % of break filled vs slate fallback
Ad error / stall rate < content threshold broken/timed-out creatives
Impression discrepancy low ADS-vs-server delta billing integrity
Break completion high viewer watched the pod through

Monetization models and settlement

The same catalogue is sold five ways, and each model has a different billing event, a different rights posture, and a different partner-settlement rule. Subscription billing runs under PCI-DSS (a separate, tokenized payments plane), while ad revenue flows through impression counts and partner splits.

Model Revenue event Content posture Ads Settlement / billing
SVOD recurring subscription (PCI) full library per plan none payment processor, dunning, proration
AVOD ad impressions ad-supported catalogue SSAI SSP/DSP revenue, CPM × verified impressions
TVOD one-off rent/buy (PCI) title unlock, rights window none per-transaction, studio revenue share
FAST ad impressions linear free channels SSAI, high load ad-network settlement, per-channel
Live sports/events sub + premium ads (± PPV) blackout + rights window SSAI, dynamic rights-holder settlement, PPV recon

Two monetization mechanics ride on top of the models. Promotions and entitlements — free trials, bundle/partner grants (telco, device OEM), win-back offers — resolve into the same entitlement decision the DRM plane already enforces, so a “3 months free” promo is just a rights window on the account, not a separate code path. Partner settlement — studios (TVOD/subscription share), sports rights-holders (event revenue), ad networks (impression revenue), and distribution partners (bundle splits) — is reconciled from the license-issuance audit log and the server-side impression log, the two authoritative records of what was actually watched and which ads actually played. Because SSAI fires impressions server-side, those counts are blocker-proof and audit-clean, which is exactly what an ad buyer and a rights-holder need before they pay.

Analytics, QoE and personalization

From the data team’s chair, Lumina is a firehose that never closes. With 45M concurrent sessions during a live final and a player that emits a QoE heartbeat roughly every 10 seconds, the analytics plane must absorb on the order of 4–5 million events per second — and the events that matter most (a rebuffer, a fatal playback error, an ad that failed to stitch) arrive in bursts exactly when load is highest. The design goal is never “store everything.” It is to make three very different consumers — the Media Operations Centre’s real-time dashboard, the growth team’s subscription funnel, and the ML feature pipeline — read from one honest event stream without any of them taking the others down, and without a single un-consented byte of PII leaking into a place it was never allowed to be.

Lumina’s players emit a beacon into a high-scale bus, a stream processor enriches and windows it, and the result forks into a hot store (seconds-fresh) and a cold lake (years-deep), with the same curated events feeding ML. Read the path left to right:

Lumina QoE analytics: 45M players emit beacons into Event Hubs/Kinesis, a Flink/Stream-Analytics job windows them, results split into an ADX/Druid hot store and a Delta cold lake, then serve QoE dashboards and ML features

The single most important measurement discipline is agreeing what each QoE metric means before arguing about its value. A “rebuffer” counted at the player, the CDN, and the origin are three different numbers; the player’s is the only one the viewer felt. This is the metric catalog the whole company aligns on, with the Tier-1 targets from the QoE budget:

Metric Definition (player-side) Tier-1 target Reporting grain Primary source
VST p95 (video-start-time) Play intent → first frame rendered < 2.0 s session · title · device · CDN player SDK beacon
Rebuffer ratio Σ rebuffer time ÷ Σ playing time < 0.4 % session · CDN · geo player SDK beacon
EBVS (exit-before-video-start) Sessions abandoned before first frame < 1.5 % title · device · CDN player SDK beacon
VPF (video-playback-failure) Fatal errors ÷ play attempts < 0.5 % error code · CDN · DRM player SDK beacon
Live latency (glass-to-glass) Encoder input → player render < 8 s (LL-CMAF) event · CDN player + encoder correlation
Completion rate Sessions ended ≥ 90 % ÷ starts title-dependent title · profile player SDK beacon
Average bitrate Delivered kbps (ABR sustained) ladder-dependent title · device · network player SDK beacon
License-fail rate DRM license denials ÷ requests < 0.2 % DRM · geo · plan license service + player

Ingestion is a capacity-planning problem, not a code problem. The trap is sizing for the daily average and getting shredded at kickoff. You size for the peak — 4–5M events/s — and you make ordering a per-session property, never a global one, so the bus can shard horizontally. The two clouds run the same shape with different primitives, and Lumina runs both (Azure East US 2/West Europe/Southeast Asia, AWS us-east-1/eu-west-1/ap-southeast-1) for regional locality and failover:

Concern Azure path AWS path Sizing rule at Lumina scale
Scale unit Event Hubs Dedicated (Processing Units) Kinesis Data Streams on-demand (shards) provision to peak/s, not mean; headroom ≥ 40 %
Ordering partition (32–100+) keyed on sessionId shard keyed on sessionId one session → one partition, ordered per session
Ingress guard throttle + 429 at collector ProvisionedThroughputExceeded backoff client SDK batches + retries with jitter
Retention 1–7 days on the bus 24 h–7 days bus is a buffer, not the store of record
Fan-out consumer groups enhanced fan-out consumers separate group per consumer (hot / cold / ML)
Capture to lake Event Hubs Capture → Parquet Kinesis Firehose → Parquet land query-ready, buffer to 128 MB / 300 s

The events themselves are a versioned contract. If the SDK, the stream job, and the lake disagree on the shape of a beacon, every downstream number quietly forks. This is the QoE beacon Lumina’s players emit — pseudonymous by construction, consent-carrying, and partition-keyed on sessionId:

{
  "$schema": "lumina/qoe-beacon/v3",
  "eventType": "rebuffer_end",              // first_frame|rebuffer_start|rebuffer_end|bitrate_switch|error|ad|heartbeat|ended
  "ts": "2026-08-14T20:11:07.412Z",
  "sessionId": "9f3c-...-e21",              // PARTITION KEY — ordering is per-session
  "subscriberHash": "sha256:7b12...",       // pseudonymous; reversible ONLY in the DSAR vault
  "profileId": "p2",
  "device":  { "class": "ctv", "model": "Samsung Q80", "os": "Tizen 7.0", "app": "8.4.1" },
  "content": { "titleId": "EIDR:10.5240/1A2B", "type": "vod", "drm": "widevine", "cdn": "CDN-B", "packaging": "cmaf" },
  "playback":{ "vstMs": 1840, "bitrateKbps": 7200, "bufferedMs": 12000, "rebufferMs": 640, "droppedFrames": 3, "positionS": 512 },
  "ad":      { "breakId": "mid-2", "creativeId": "cr-8841", "ssai": true, "stitchOk": true },
  "error":   null,                          // { "code": "3.2.1", "fatal": false, "message": "license renew" }
  "geo":     { "country": "DE", "asn": 3320 },
  "consent": { "tcf": "CPx...v2", "analytics": true, "ccpaOptOut": false },
  "experiments": { "abr_v4": "treatment" }
}

The stream processor is where correctness is won or lost. A tumbling window turns the firehose into rollups the NOC can read, but late beacons — a phone that buffered offline in a tunnel and flushed five minutes later — must be handled deliberately, not dropped. Lumina uses a bounded watermark and routes stragglers to a reconciliation path; the mechanics of watermarks and late-arriving events are their own discipline, covered in Stream Analytics watermarks and late events. This is the 1-minute QoE rollup that feeds the hot store, Azure Stream Analytics / Flink-SQL style:

-- 1-minute tumbling QoE rollup per CDN × title → hot store; consent-gated at the source
SELECT
  System.Timestamp()                              AS window_end,
  content.cdn,
  content.titleId,
  COUNT(DISTINCT sessionId)                        AS sessions,
  SUM(playback.rebufferMs) * 1.0
      / NULLIF(SUM(playback.bufferedMs), 0)        AS rebuffer_ratio,     -- target < 0.004
  PERCENTILE_CONT(0.95)
      WITHIN GROUP (ORDER BY playback.vstMs)       AS vst_p95_ms,         -- target < 2000
  SUM(CASE WHEN error.fatal = 1 THEN 1 ELSE 0 END) * 1.0
      / COUNT(*)                                    AS vpf_ratio          -- target < 0.005
INTO   [adx-qoe-hot]
FROM   [eh-qoe-beacons] TIMESTAMP BY ts
WHERE  consent.analytics = 1
GROUP BY content.cdn, content.titleId, TumblingWindow(minute, 1);

Not every question wants the same store. The NOC needs a five-second answer for the last hour; the growth team needs a 90-day cohort; the ML team needs three years of raw history to reprocess a feature. Forcing all three into one engine is either ruinously expensive or uselessly slow. Lumina splits by latency-of-question:

Tier Store Freshness Retention Query pattern Cost posture Canonical use
Hot Azure Data Explorer / Druid 5–30 s 7–14 days dashboards, alerting $$$ /TB live rebuffer by CDN during a final
Warm Synapse / Redshift / Databricks SQL minutes–hours 90–400 days interactive, funnels $$ /TB 30-day churn cohort
Cold ADLS Gen2 / S3 (Delta/Parquet) hours years batch, ML, replay $ /TB 3-year QoE history reprocess

Beyond raw playback, the same pipeline powers four other analytics domains, each with a different grain and owner. Keeping them on one event backbone (not five bespoke pipelines) is what lets the reco model and the ad-yield model see the same definition of “completion”:

Domain Grain Hot use Cold use Key metrics Owner
QoE / playback session · 1-min NOC live board encode QA, trends VST, rebuffer, VPF Video eng
CDN & origin edge PoP · 1-min multi-CDN steering offload, $/GB hit ratio, edge RTT, origin 5xx Delivery
Subscription funnel user · event signup live board cohort, LTV, churn signup → trial → paid, MRR Growth
Engagement title · day trending row reco training plays, completion, dwell Product
Ad delivery (SSAI) impression fill-rate board yield, revenue share fill %, VAST error, stitch latency Ad ops

Finally, two cross-cutting disciplines sit over all of it. A/B testing must be deterministic and sticky — a viewer who flip-flops between arms mid-session poisons the result — and consent is not a footnote but a partition of the data itself. Under GDPR and CCPA, a beacon that arrived without analytics consent may be counted for aggregate operational health but must never be joined to identity or used to train a personalization model. Lumina enforces this at write time, at the consent gate, not with a hopeful WHERE clause at query time:

Concern Mechanism Where enforced Note
Experiment assignment deterministic hash(subscriberHash + expId) edge SDK + config service sticky; no mid-session flip
Metric analysis guardrail (VST, rebuffer) + goal (D30 retention) warehouse CUPED for variance reduction
Consent capture IAB TCF v2 string on every beacon ingest consent gate no analytics consent → pseudonymised, restricted prefix, never joined to PII
CCPA opt-out ccpaOptOut flag consent gate excluded from personalization features
DSAR erasure subscriberHash re-identifiable only in the vault governance erase / export within 30 days

The hot-path dashboards themselves are just queries over the hot store — KQL over Azure Monitor / Data Explorer drives the NOC’s “rebuffer ratio by CDN, last 15 minutes” board — but the discipline that makes them trustworthy lives upstream, in the schema, the windowing, and the consent gate. Get those right and the number on the wall is the number the viewer felt.

Data platform and AI

QoE analytics answer “is playback healthy right now.” The data platform answers everything slower and deeper — “why did this cohort churn,” “what should we recommend,” “which titles are worth relicensing” — and it does so by turning the same event streams, plus billing, catalog, and rights data, into governed tables that BI and ML can trust. Lumina runs a medallion lakehouse on both clouds: data lands once in an immutable raw zone, is conformed into curated Delta, and only governed, tagged, masked data is ever exposed broadly. The architecture, sources through models:

Lumina data platform: viewer events and OLTP/CDC feed streaming and batch ingest, land in a raw→curated→governed medallion lake, then serve a Synapse/Redshift warehouse and an AML/SageMaker ML workspace with a feature store

The zone model is the backbone, and the discipline that separates a lake from a swamp is that each zone has one job and one access posture. Lumina implements the identical pattern on Azure and AWS so a workload can run in whichever region is closest to the data; the medallion pattern itself is detailed in ADLS Gen2 medallion bronze/silver/gold and medallion streaming/batch unification:

Zone Purpose Azure AWS Format Who reads it
Raw (bronze) Immutable landing, replay source of record ADLS Gen2 (lm-lz-data) S3 raw bucket (Data OU) Avro/JSON → Parquet ingest identity only
Curated (silver) Conformed, deduped, typed, dimensions resolved ADLS + Delta / Databricks S3 + Delta / Glue Delta / Iceberg data engineering
Governed (gold) Tagged, masked, serving-ready Unity Catalog / Synapse Lake Formation / Redshift Delta / Parquet broad, by tag policy
Sandbox Exploratory, time-boxed per-team schema per-team prefix any data science, no PII

Data reaches those zones through two pipeline styles that must converge on one landing contract. If streaming lands JSON with one key layout and batch lands CSV with another, the curated layer forks and every metric downstream inherits the split. Lumina pins a single Avro/Parquet schema at ingest and lets the engine differ:

Pipeline Engine (Azure / AWS) Latency Typical use Correctness mechanism Trigger
Streaming Stream Analytics · Flink / Kinesis + Spark Structured Streaming seconds QoE rollups, live ad, fraud checkpoint + exactly-once sink continuous
Micro-batch Databricks Auto Loader / DLT 1–5 min catalog CDC, near-real-time marts Delta transaction log file arrival
Batch ELT ADF · Synapse / Glue · dbt hourly–daily funnels, finance, reco training idempotent MERGE schedule

Governance is the feature, not the paperwork. At 120M subscribers under GDPR + CCPA, the platform’s value is bounded by how confidently you can expose it. A single catalog gives every engine one schema; lineage lets you trace a churn dashboard field back to the raw beacon that produced it; quality gates reject bad partitions before they poison a model; and tag-based access control means a new PII column is masked by policy the moment it’s classified, not by a grant somebody remembers to add. The tooling maps cleanly across clouds, and the deeper pattern is in enterprise data catalog, lineage and governance and data mesh domain products & federated governance:

Capability Azure AWS What it buys you
Catalog Microsoft Purview / Unity Catalog Glue Data Catalog / Lake Formation one schema, discovery, ownership
Lineage Purview / Unity Glue + OpenLineage trace a dashboard field to source, impact analysis
Quality Great Expectations / DLT expectations Deequ / Glue Data Quality reject bad partitions at the gate
Classification Purview sensitivity labels Macie + LF-tags auto-find PII (email, payment)
Access Unity grants / Purview policies Lake Formation tag-based column masking, row filters, no per-table grant rot

On top of governed gold sit the ML workspaces — Azure Machine Learning and SageMaker — where recommendation, churn, and content-intelligence models are built, registered, and served. The workspace anatomy (compute, datastores, jobs, registry) is covered in Azure ML workspace anatomy; what matters at the platform level is that models are versioned, approval-gated for Tier-1, and fed from the same curated events as the warehouse. Here is the nightly churn pipeline — a point-in-time feature join into a registry-pinned model, so training and scoring never disagree:

# aml-churn-pipeline.yml — nightly churn scoring; point-in-time features, approval-gated model
$schema: https://azuremlschemas.azureedge.net/latest/pipelineJob.schema.json
type: pipeline
display_name: churn-nightly
settings: { default_compute: azureml:cpu-cluster-d8 }
inputs:  { as_of_date: "2026-08-14" }
jobs:
  build_features:
    type: command
    component: azureml:feature_join@1.4.0          # AS-OF join — no label leakage
    inputs:
      events:  azureml:qoe_curated@latest
      billing: azureml:billing_curated@latest
      as_of:   ${{parent.inputs.as_of_date}}
    outputs: { feature_set: { type: uri_folder } }
  score:
    type: command
    component: azureml:churn_score@2.1.0
    inputs:
      model:    azureml:churn_gbm:7                 # from registry; promotion is approval-gated
      features: ${{parent.jobs.build_features.outputs.feature_set}}
    outputs:
      scores:
        type: uri_folder
        path: azureml://datastores/gold/paths/churn/${{parent.inputs.as_of_date}}/

The model portfolio is deliberately small and each model earns its place. Recommendation is the flagship — a two-tower retrieval stage feeding a ranker, the pattern in two-tower recommendation at scale — but content intelligence (multimodal tagging of scenes, thumbnails, and chapters) and QoE anomaly detection are equally load-bearing:

Use case Model family Key features Output Refresh Consumer
Recommendation two-tower retrieval + ranker watch history, embeddings, context ranked rows candidates hourly / rank real-time app home
Churn / retention gradient-boosted + survival tenure, QoE, engagement, billing churn score daily growth / CRM
Content intelligence multimodal (video/audio/text) scenes, subtitles, thumbnails tags, chapters, thumbs on-ingest catalog / search
QoE anomaly streaming detection (robust z / ESD) rebuffer, error by CDN/geo incident alert real-time NOC
Ad yield contextual bandit context, fill, floor price ad decision near-real-time SSAI / ADS
Sharing / fraud graph + rules device, geo, concurrency risk score real-time entitlement

The connective tissue under all of these is the feature store, and its non-negotiables are freshness and point-in-time correctness. A reco model that trains on features computed after the label leaked will look brilliant offline and fail in production. The feature store serves an online copy (Redis / DynamoDB) for inference and an offline copy (Delta) for training from one definition, so “watch_completion” means exactly one thing in both — the discipline detailed in MLOps platform with a feature store:

Control Mechanism Applies to
Feature freshness online store (Redis / DynamoDB) + offline (Delta) reco, fraud
Point-in-time correctness as-of joins on event_time all training
Consent propagation consent flag rides the feature row all
PII minimization hashed subscriber key; no raw email in features all
Model governance MLflow / registry, approval gate, model cards Tier-1 models
Bias & eval offline eval + online guardrail metrics reco, ranking

Partner distribution and B2B integration

Not everyone who consumes Lumina’s platform is a viewer with a login. Partners — cable/MVPD operators, smart-TV and console OEMs, FAST channel aggregators, and white-label operators who resell a Lumina-powered service under their own brand — integrate machine-to-machine, at scale, over APIs. The security model is different in kind from consumer CIAM: there is no interactive login, no password, and the thing you are protecting is not a viewer’s PII but a studio’s licensing rights. One partner serving one title one day outside its licensed window is a contract breach with a rights holder, not a bug ticket. So every partner enters through a single hardened gateway that authenticates them, enforces the rights window centrally, and serves only their isolated tenant slice:

Lumina partner distribution: a partner app enters through Front Door + APIM with mTLS and OAuth2, passes a rights-window and entitlement policy check, reaches scoped metadata/entitlement/content APIs, then tenant isolation and per-partner audit

The partner API surface is deliberately narrow and each API has a distinct security posture. Metadata is cacheable and low-risk; entitlement mints playback tokens and is the highest-traffic, highest-sensitivity call; content delivery never proxies bytes, only signed URLs:

API Purpose Shape AuthZ scope Rate posture Returns
Metadata / catalog title feed, artwork, avails REST/GraphQL + bulk feed partner scope high, cacheable in-window catalog subset
Entitlement / playback verify session, mint playback token REST per-session very high DRM token + manifest URL
Content delivery signed URLs / manifests REST per-title high short-TTL signed URL
Reporting consumption, QoE, revenue share REST + bulk export partner scope low aggregates only, no PII
Provisioning account link / SSO federation SCIM / OIDC admin low link status

The heart of the gateway is the policy that runs before any partner request touches a backend. It validates a pinned mutual-TLS certificate and a client-credentials JWT, pulls the licensing window for the requested title from the rights service, and refuses with 451 Unavailable For Legal Reasons if the title is out of window for that partner — then stamps a tenant header that drives row-level security downstream. This is Lumina’s Azure API Management inbound policy (the policy primitives — validate-jwt, send-request, rate-limit-by-key — are covered in API Management policy fundamentals):

<!-- inbound: authenticate partner, enforce rights window, tag tenant for downstream RLS -->
<inbound>
  <validate-client-certificate />                              <!-- pinned mTLS, per partner -->
  <validate-jwt header-name="Authorization" require-scheme="Bearer">
    <openid-config url="https://login.lumina.tv/partners/.well-known/openid-configuration" />
    <required-claims>
      <claim name="azp" match="any"><value>{{partner_id}}</value></claim>
    </required-claims>
  </validate-jwt>
  <!-- pull the licensing window for the requested title from the rights service -->
  <send-request mode="new" response-variable-name="rights" timeout="2">
    <set-url>@($"https://rights.internal.lumina.tv/v1/avails/{context.Request.MatchedParameters["titleId"]}?partner={context.Variables["partnerId"]}")</set-url>
    <set-method>GET</set-method>
    <authentication-managed-identity resource="api://rights" />
  </send-request>
  <choose>
    <when condition="@(!(bool)((IResponse)context.Variables["rights"]).Body.As<JObject>()["inWindow"])">
      <return-response><set-status code="451" reason="Unavailable For Legal Reasons" /></return-response>
    </when>
  </choose>
  <rate-limit-by-key calls="20000" renewal-period="1"
      counter-key="@(context.Request.Headers.GetValueOrDefault("X-Partner-Id"))" />
  <set-header name="X-Tenant" exists-action="override">
    <value>@(context.Variables.GetValueOrDefault<string>("partnerId"))</value>   <!-- drives RLS -->
  </set-header>
</inbound>

Rights-window enforcement is the reason this whole tier exists. A title is only distributable to a given partner inside its licensed window, territory, device class, and quality ceiling — and those constraints come from the rights/avails system, never from a partner’s promise to honor them. Critically, rights (may Lumina offer this to this partner) and entitlement (may this end-user’s session play it) are separate checks that must both pass; conflating them is precisely how out-of-window content leaks to an otherwise-valid subscriber:

Attribute Example Source Enforcement point
Window start/end 2026-08-01 → 2027-01-31 rights / avails service APIM policy + catalog feed filter
Territory licensed countries only rights service geo check at gateway + CDN edge
Device / platform class CTV only, no mobile rights service token claims + license policy
Exclusivity / holdback no FAST syndication until +90d rights service catalog feed filter
Max quality / HDCP UHD requires HDCP 2.2 rights + DRM policy license server
Blackout regional live-sports blackout live control manifest + SSAI

Onboarding a partner is a governed lifecycle, not a config change — it starts at the contract and ends with revocable, audited access. Federated partner identity (B2B) is set up per Entra External ID B2B collaboration:

Stage Action Tooling Control gate
Contract & avails rights + revenue terms loaded rights service legal sign-off
Identity client-credentials app + mTLS cert issued Entra / Cognito app reg short-lived secrets in Key Vault
Scoping partner_id, allowed APIs, quotas APIM products / subscriptions least privilege
Sandbox test tenant + synthetic catalog non-prod OU no real PII
Certification conformance + security review TPN / CDSA checklist gate to prod
Go-live prod subscription enabled APIM audit baseline captured
Offboarding revoke keys, expire windows automation evidence retained

White-label is where isolation gets strict: multiple brands share the platform’s compute but must never share each other’s data, keys, or configuration. Lumina isolates per tenant on the axes that matter and shares only the stateless gateway:

Layer Shared? Isolation mechanism
Gateway / compute shared partner_id claim + policy enforcement
Data (catalog, entitlement) shared store row-level security keyed on partner_id
Secrets / DRM keys isolated per-tenant Key Vault / KMS key
Branding / config isolated per-brand config + theme + domain
Reporting storage isolated per-tenant container/prefix + scoped SAS
Observability isolated view log partition + RBAC-scoped dashboards

Finally, every partner interaction is evidence. Rights holders and CDSA/TPN audits ask “who accessed what, when” — and Lumina answers from immutable, per-partner logs, not memory:

Audit question Source of truth Retention
Who called which API, when APIM + gateway access logs 400 d hot, years cold
Which titles served (and any out-of-window denials) rights decision log immutable, years
Token mint / redemption entitlement service log 90 d+
What data was exported reporting export log per contract
Key / cert rotation Key Vault / IAM audit years

Production, archive and media supply chain

Everything upstream of playback is the media supply chain: how a master arrives from a studio or Lumina’s own production, gets checked, described, localized, preserved for decades, and finally published. This is where MovieLabs Enhanced Content Protection and TPN/CDSA obligations bite hardest, because pre-release masters are the single most valuable, most leak-sensitive asset the company touches — and where ~400 PB of archive lives. The pipeline runs from secure contribution through QC, metadata, and localization into deep archive, then out to the VOD origin:

Lumina media supply chain: studio/production masters arrive over Aspera/TPN transfer, pass automated QC and MovieLabs metadata normalization, get localized, land in Glacier Deep Archive under rights control, then are packaged with CMAF/CENC and published to the multi-CDN VOD origin

Each stage has a clear input, action, and output, and the sequencing matters: you QC before you spend money transcoding a 4K master, and you normalize metadata before it reaches five systems that would each guess differently:

Stage Input Action Output Tooling
Contribution mezzanine / IMF from studio secure transfer, watermark, virus scan ingest master Aspera / Signiant, MediaConnect
Ingest & QC master automated + eyeball QC pass/fail + report Baton / Vidchecker
Normalize master + sidecars conform, proxy, checksum canonical asset IMF, xxHash / MD5
Metadata asset enrich, EIDR / OMC mapping catalog record Purview + MovieLabs OMC, EIDR
Localize asset subtitles, dub, audio, art localized renditions vendors + ASR
Archive master + renditions tier + preserve archived object Glacier DA / Azure Archive
Package & publish approved asset transcode, DRM, package CMAF + CENC on origin MediaConvert / packager

Archive is preservation, not backup — and the tier is a cost decision measured in retrieval hours. A mezzanine master that may not be touched for a decade belongs in the deepest, cheapest tier that still guarantees WORM immutability, checksums, and geo-redundancy; an active rendition being packaged this week belongs in hot. Lumina drives this with lifecycle policy across all three clouds (values are order-of-magnitude and region-dependent), building on Azure Blob access tiers and S3 storage classes & lifecycle:

Tier Azure AWS GCP Retrieval ~$/GB-mo Use
Hot Blob Hot S3 Standard Standard instant ~$0.020 active packaging
Cool Blob Cool S3 Standard-IA Nearline instant ~$0.010 recent renditions
Cold Blob Cold S3 Glacier Instant Coldline ms–instant ~$0.004 long-tail masters
Deep archive Blob Archive S3 Glacier Deep Archive Archive hours (rehydrate) ~$0.001 preservation masters

The lifecycle rules that move masters down the tiers are declarative and identical in intent on both clouds — tier the immutable IMF masters to the deepest tier after they age out of active use, while proxies stay warm:

// Azure Blob lifecycle policy — masters to Archive, proxies stay Cool
{ "rules": [ {
  "name": "masters-to-deep-archive",
  "type": "Lifecycle",
  "definition": {
    "filters": { "blobTypes": ["blockBlob"], "prefixMatch": ["masters/imf/"] },
    "actions": { "baseBlob": {
      "tierToCool":    { "daysAfterModificationGreaterThan": 30 },
      "tierToArchive": { "daysAfterLastAccessTimeGreaterThan": 90 }
    } }
  }
} ] }
// AWS S3 lifecycle — same intent, Glacier Deep Archive for preservation
{ "Rules": [ {
  "ID": "masters-preservation",
  "Filter": { "Prefix": "masters/imf/" },
  "Status": "Enabled",
  "Transitions": [
    { "Days": 30,  "StorageClass": "STANDARD_IA" },
    { "Days": 90,  "StorageClass": "GLACIER_IR" },
    { "Days": 180, "StorageClass": "DEEP_ARCHIVE" }
  ],
  "NoncurrentVersionTransitions": [ { "NoncurrentDays": 30, "StorageClass": "GLACIER" } ]
} ] }

Localization is a supply chain of its own, and for a global service it is the difference between a title that travels and one that dies at a border. Each rendition is produced, standards-conformed, and QC’d before it can be attached to a publish. Lumina blends specialist vendors with AI-assisted first passes (ASR for captions, AI dubbing) under mandatory human review for premium tiers:

Rendition Produced by Standard / format QC focus
Subtitles / captions ASR (Whisper-class) + human review IMSC 1.1 / WebVTT / TTML timing, reading speed, compliance
Dubbed audio studio + AI-assisted separate track, −24 LKFS loudness lip-sync spot check
Audio description vendor AD track coverage, placement
Forced narratives editorial soft-subs / burned correctness, placement
Localized art & metadata localization team per-territory assets rights + territory match

Underpinning contribution and any partner exchange of masters is the content-security regime that TPN Gold and CDSA assessments actually test. Pre-release material moves only over encrypted, resumable, audited transfer into a segregated, watermarked enclave — never an open FTP, never an emailed link — and every hand that touches a master is logged. The publish itself follows encode-once/package-efficiently: one CMAF/CENC package with cenc + cbcs serves Widevine, FairPlay, and PlayReady, which is what keeps 400 PB from ballooning into an exabyte of format duplicates (the multi-DRM packaging path is detailed in VOD streaming with multi-DRM on AWS):

Control TPN / MovieLabs requirement Lumina implementation
Secure transfer encrypted, resumable, no open FTP Aspera / Signiant, signed-URL pull, PrivateLink
Forensic watermarking trace a leak to its source session + distribution watermark (Nexguard-class)
Access control least privilege, MFA, no shared creds Entra PIM, per-vendor scoped roles
Segregation pre-release enclave isolated from GA dedicated subnet / OU, no lateral path
Logging full audit of who touched masters immutable logs, retained as TPN evidence
Assessment annual TPN Gold / CDSA control mapping + tracked remediation

The through-line across the whole supply chain is the same one that runs through partner distribution: the rights service is the single source of truth for what may go live, where, and when — and Lumina’s own VOD publish is gated by it just as strictly as any affiliate’s feed. Package once, protect always, preserve deliberately, and let one rights decision govern every path a title can take to a screen.

Multi-layer security model

A global streaming operator cannot draw a perimeter around its estate, because the estate has no edge left to draw a line at: a viewer authenticates from a smart-TV in a living room, a colourist grades a pre-release feature from a home suite, a partner affiliate pulls a linear feed across a distribution link, and a decade-old set-top box in a hotel requests a DRM licence three hops from the key service. The model Lumina Media builds therefore rests on one uncomfortable assumption — every layer must hold after the layer outside it has already failed — sharpened by a fact that finance and healthcare do not share: the primary asset is not a record but the content itself, and the studios that license it do not merely prefer that you guard it, their contracts prescribe the controls and the notification clock when you do not. MovieLabs Enhanced Content Protection, a TPN (Trusted Partner Network) assessment and CDSA oblige Lumina to evidence a zero-trust content workflow — hardware-rooted keys, encryption in flight and at rest, forensic watermarking, least-privilege on every pre-release asset — while PCI-DSS guards the billing plane and GDPR/CCPA guard 120M viewers’ PII and consent. So the seven layers below are engineered so a gap is a missing metric an assessor can see, not a matter of opinion.

The 7-layer zero-trust security model for a streaming operator, identity through DRM/data to monitoring

The identity layer is the new perimeter, and streaming is unusual in carrying two identity planes that must never touch. Workforce identity — editors, live-control operators, DRM-key admins — authenticates at Microsoft Entra ID federated with Okta, with Conditional Access weighing user, device, location and sign-in risk on every call, phishing-resistant FIDO2 for all privileged and content-touching roles, and zero standing privilege through PIM. Customer identity (CIAM) is a wholly separate plane — Azure AD B2C / Amazon Cognito at 120M-user scale — and the two are never merged, because a compromise of the consumer token service must never reach a mezzanine asset. The consumer plane’s dominant threats are not phishing but credential stuffing, account-takeover and account-sharing fraud, defended by bot management, device binding and concurrency limits rather than MFA prompts a viewer would abandon. The device layer refuses to trust a credential alone: Intune + CrowdStrike Falcon posture gate workforce access and a Privileged Access Workstation is mandatory for the live-control and key-management paths, while the vast consumer device fleet (smart-TV, console, STB, mobile) is attested by a wholly different mechanism — the DRM device certificate and HDCP capability, checked at licence issuance, not workforce compliance.

The network layer assumes a foothold exists and denies lateral movement: hub-and-spoke with default-deny, Azure Firewall Premium and AWS Network Firewall for IDPS and TLS inspection, and the standing rule that control-plane, customer-API, media-processing, analytics, corporate and management are separate segments — the origin and the DRM key service are never on a public route. The workload layer constrains a compromised runtime on the encode, package, origin and playback fleets: Defender for Containers / GuardDuty gate images and behaviour, Wiz admission control blocks non-compliant pods, and pre-release content pipelines run only signed, attested images as MovieLabs requires. The application/API layer protects each service at its own front door — WAF in OWASP-prevention mode, bot management and per-endpoint rate limits on the playback, entitlement, CIAM and SSAI APIs, plus signed-URL / JWT token authentication at the CDN edge so a scraped manifest URL is useless without a valid, short-lived token. The DRM/data layer is the streaming crown jewel and the industry’s real “last line”: multi-DRM (Widevine + FairPlay + PlayReady, CENC cenc+cbcs) encrypts every segment, content keys live hardware-rooted in Key Vault Managed HSM / AWS CloudHSM / KMS, keys rotate on a cadence, HDCP and output protection are enforced per title, and forensic (session) watermarking makes a leaked stream traceable to the account that ripped it. Across all six sits the monitoring layer: Microsoft Sentinel correlates signal from both clouds, Defender XDR and GuardDuty feed it, Wiz holds a CSPM posture score ≥ 85, and an anti-piracy analytics stream watches concurrency, geo and token anomalies alongside Conviva-class QoE.

The control model below is the operational heart of the posture. Every row names what the layer assumes, the concrete Azure and AWS control that answers it, the media-specific requirement that makes streaming stricter than a generic enterprise, and the telemetry the SOC watches — so no layer is aspirational and none is unmonitored.

Layer Assume-breach premise Azure control AWS control Media-specific requirement Telemetry signal
1 · Identity The network is hostile Entra ID + Okta, CA, PIM JIT, FIDO2 IAM Identity Center federated via Entra, permission sets Workforce and CIAM planes never merged; break-glass for live-control room Risky sign-in, PIM activation, break-glass event
2 · Device The credential is stolen Intune + Defender/Falcon, PAW for key admins Verified-access posture, SSM-managed Consumer devices attested by DRM cert + HDCP, not workforce trust Device-compliance %, non-compliant blocks, HDCP downgrade
3 · Network A foothold exists inside Firewall Premium (IDPS/TLS), NSG/ASG deny, Private Link Network Firewall (Suricata), SG/NACL deny, VPC endpoints Origin + DRM key service never public; segment isolation provable Firewall IDPS hits, flow logs, quarantine events
4 · Workload Malicious code is running Defender for Containers, Wiz admission, patch 7d/30d GuardDuty EKS + Inspector, ECR scan-on-push Content pipeline images signed + attested (MovieLabs) Admission denials, runtime findings, unpatched-critical count
5 · App / API Network controls were bypassed App Gateway WAF v2, APIM, bot + rate limit AWS WAF on ALB/API GW, Bot Control Edge signed-URL/JWT token auth; manifest/segment schema checks WAF blocks, token-reject rate, rate-limit trips
6 · DRM / Data Everything above has fallen Multi-DRM CENC, CMK in Key Vault MHSM, Private Endpoint Multi-DRM/SPEKE, CMK in KMS/CloudHSM, PrivateLink Hardware-rooted content keys, key rotation, HDCP, forensic watermark Key-access logs, licence-issue anomalies, watermark hits
7 · Monitoring Prevention is eventually defeated Sentinel SIEM/SOAR, Defender XDR, Wiz CSPM ≥ 85 GuardDuty + Security Hub + Detective, Macie Anti-piracy analytics (concurrency/geo/token) + QoE fused MTTD/MTTR, CSPM score, piracy-signal count

The tool estate that realises the monitoring and workload layers spans both clouds and is deliberately not symmetrical service-for-service — each provider’s native stack is used where it is strongest, and Sentinel is the single pane both feed. The mapping below is what a new analyst learns on day one, and what the Defender for Cloud CSPM rollout plugs into.

Capability Azure AWS What it catches for Lumina
CSPM / posture Defender for Cloud + Wiz + Secure Score Security Hub (FSBP) + Wiz + Config Public origin/mezzanine bucket, unencrypted disk, disabled Private Endpoint
CWPP / server + container Defender for Servers/Containers GuardDuty Runtime + Inspector Crypto-miner in an encode pod, vuln in a packager image
Threat detection (cloud) Defender XDR + Defender for Cloud alerts GuardDuty (VPC/DNS/S3/EKS/Malware) Exfil from a content store, C2 beaconing, anomalous S3 read of mezzanine
SIEM / SOAR Microsoft Sentinel (both clouds’ logs) (feeds Sentinel via connector) Cross-cloud correlation, DRM-key-svc abuse, impossible-travel on admin
EDR / XDR endpoint Defender for Endpoint / CrowdStrike Falcon CrowdStrike / SSM Ransomware precursor on an editor / live-control workstation
Data discovery / DLP Purview + Defender for Storage Macie + Access Analyzer Unclassified PII in an analytics lake, over-shared pre-release asset
Vuln management Defender vuln assessment (MDVM) Inspector (EC2/ECR/Lambda) Log4Shell-class CVE in an SSAI or entitlement adapter
Investigation graph Sentinel entity behaviour + hunting Detective Lateral-movement path from playback API to the key service

Posture is a set of standards mapped to the frameworks Lumina answers to, so a failing control has a named consequence rather than a red tile. PCI-DSS, MovieLabs ECP / TPN, SOC 2 and GDPR/CCPA resolve down to concrete cloud standards that Defender, Wiz and Security Hub evaluate continuously.

Posture control Azure Defender / Wiz AWS standard Framework driver Auto-remediation
Content store not publicly reachable “Storage public access disabled” S3 FSBP public-access controls MovieLabs ECP; TPN Deny policy + Config auto-fix
Encryption with CMK enforced “CMK required” recommendation KMS-CMK required controls MovieLabs ECP; PCI 3.5 Azure Policy DeployIfNotExists
Cardholder data scope isolated “SQL/network isolation” recs PCI DSS standard (Security Hub) PCI-DSS 1.x / 3.x SCP + NSG guardrail
Audit logging immutable + retained “Diagnostic settings to immutable” CloudTrail + Object Lock controls SOC 2 CC7; PCI 10.5 Enable + lock at vend time
DRM key material HSM-backed “Key Vault MHSM required for keys” CloudHSM/KMS-origin controls MovieLabs ECP (hardware root) Policy audit + pipeline gate
Vulnerable images blocked pre-deploy Defender for Containers gate Inspector + ECR scan-on-push SOC 2 CC8; MovieLabs Admission deny / pipeline fail

Secrets and keys are the sharpest expression of the DRM/data layer, because in streaming the answer to “who can decrypt this title” must be only a validly entitled player, achieved by holding the content-key root of trust yourself. Every content key and every PCI datastore key uses a customer-managed key, and the most sensitive classes — pre-release / early-window titles under studio contract — escalate to a dedicated Managed HSM key with a narrower access policy and its own rotation cadence, following the Key Vault secrets, keys and certificates model. Application secrets never live in config; they resolve at runtime from Key Vault or Secrets Manager against a managed identity, so a leaked deployment artefact contains a reference, not a credential.

Secret / key type Azure AWS Rotation CMK / key ownership
DRM content keys (CENC) Key Vault Managed HSM (RSA/AES-HSM) KMS CMK / CloudHSM via SPEKE Per-title + periodic Customer-managed, HSM-held
Pre-release / early-window keys Dedicated MHSM key, scoped RBAC CloudHSM cluster key Accelerated (per window) Customer-managed, isolated
Billing / PCI database keys Key Vault MHSM (TDE CMK) KMS CMK (PCI scope) Annual + on-demand Customer-managed
App / service credentials Key Vault secret + managed identity Secrets Manager + IAM role 30–90 day auto Platform store, CMK-encrypted
CDN edge token signing keys Key Vault key + Managed Identity KMS asymmetric CMK 24–72 h rotation Customer-managed
TLS / mTLS partner certs Key Vault certificate + ACME ACM / Secrets Manager Auto (ACM) / 1-yr Customer-managed
Break-glass account credentials Key Vault + PIM-gated, sealed Secrets Manager + isolated account On use + re-seal Customer-managed, offline copy

The edge is the seventh place content and identity are protected before a request reaches an application, and it does four jobs that overlap but are not the same: absorb volumetric attacks (which spike hardest at live-event kickoff), enforce OWASP and bot rules, stop the content-theft and credential-abuse patterns unique to streaming, and rate-limit the abusable endpoints a fraud campaign targets. The DDoS protection tiering and Akamai App & API Protector / Bot Manager patterns underpin this layer.

Threat class Azure edge control AWS edge control Streaming endpoint / asset it protects
Volumetric / L3-4 DDoS (event spike) DDoS Protection (Network) + Front Door Shield Advanced + CloudFront Live ingress, playback API, CIAM login
OWASP L7 (injection/XSS) App Gateway WAF v2 (CRS 3.2) AWS WAF managed rules Playback/entitlement API, SSAI, portal
Content theft / stream ripping Signed-URL/JWT token at edge + geo/concurrency CloudFront signed URLs + WAF Manifest, segment, DRM-licence request
Credential stuffing / ATO WAF bot protection + rate limit + device binding WAF Bot Control + rate rules CIAM login, token endpoint, sign-up
Account-sharing / concurrency abuse Entitlement concurrency + geo rules Cognito + entitlement service Entitlement check, licence issuance
Payment / TVOD fraud WAF + APIM policy + risk scoring WAF + API Gateway + fraud rules Checkout, TVOD purchase, promo redemption

Multi-layer network security

Network security inherits the same assumption-of-breach discipline and expresses it as six independent controls, no one of which is trusted to be sufficient. First, the multi-CDN global edge — three commercial CDNs with performance/cost steering, plus authoritative DNS and edge WAF/bot/DDoS — absorbs volumetric and scraping traffic and cloaks the regional origins behind origin-shield and signed tokens. Second, the regional WAF at the cloud edge (Application Gateway WAF v2 / AWS WAF on the ALB) re-checks every request closer to the workload. Third, central inspection (Azure Firewall Premium / AWS Network Firewall) forces all north-south and inter-spoke traffic through IDPS and TLS inspection. Fourth, micro-segmentation with NSG/ASG and security-group/NACL default-deny contains a compromise to one tier. Fifth, private connectivity (Private Endpoint / PrivateLink) removes the entitlement store, DRM key service and billing data from the public internet entirely. Sixth, host and workload controls hold if every layer above is crossed. Two rules bind the design: there is no unrestricted east-west traffic anywhere, and all egress is policy-controlled and logged through the central firewalls.

The segmentation map is where streaming diverges from a generic enterprise, because the segments are content-sensitivity and regulatory boundaries, not just tiers. The IPAM plan is Azure 10.20.0.0/12 (East US 2 shown), AWS 10.40.0.0/12 (us-east-1 shown) and on-prem 10.0.0.0/12, with a hub /20 per region, spokes /22 and PE subnets /26 — no overlaps, so crossing a boundary is always an explicit, inspected, logged event.

Segment Azure CIDR (East US 2) AWS CIDR (us-east-1) Contains Crosses boundary via Data class
Control-plane 10.20.16.0/22 10.40.16.0/22 Live-event control, playout orchestration, playback-session broker Firewall + Private Endpoint Restricted (control)
Customer-API 10.20.20.0/22 10.40.20.0/22 Entitlement, playback API, CIAM token svc, DRM-licence proxy Firewall + WAF Confidential (PII + entitlement)
Media-processing 10.20.24.0/22 10.40.24.0/22 Encode/transcode, packager, origin, SSAI stitcher Firewall + private origin Restricted (premium content)
Analytics 10.20.28.0/22 10.40.28.0/22 QoE ingest, reco, ad-reporting, lakehouse Firewall + de-ID gateway Confidential (behavioural)
Corporate 10.20.32.0/22 10.40.32.0/22 SAP back office, production/post, archive management Firewall Confidential
Management 10.20.0.0/24 10.40.0.0/24 Bastion, jump, agents, PE subnets Bastion-only inbound Internal

Azure network security (deep dive)

On Azure the controls compose around the Virtual WAN secured hub. Inbound viewer traffic terminates at Front Door (WAF, bot, DDoS, token check) and, for regional APIs, Application Gateway WAF v2 in the dedicated AppGatewaySubnet, running OWASP CRS 3.2 in Prevention mode with per-URI rate limits — an application is never the first thing to see a raw request. Everything beyond it, north-south and east-west, is routed by User-Defined Routes through Azure Firewall Premium, whose IDPS and TLS inspection decrypt, inspect and re-encrypt lateral traffic rather than waving it through; egress is governed by FQDN application rules so a packager reaches only its named CDN origin-pull and multi-DRM licence endpoints. Within and between spokes, NSGs and Application Security Groups enforce default-deny: tiers are expressed as ASGs so rules reference intent (asg-play-api) rather than fragile CIDRs, and the DRM licence tier is provably isolated from the public playback tier — exactly the segmentation a TPN assessor asks you to evidence.

The customer-API spoke’s inbound NSG reads top-to-bottom as an explicit allow-list with a deny backstop; the priorities and ASG references below are the real shape.

Priority Name Dir Source Destination Port Action
100 allow-fd-web In AzureFrontDoor.Backend asg-play-web 443 Allow
110 allow-web-api In asg-play-web asg-play-api 8443 Allow
120 allow-api-drm In asg-play-api asg-drm-license 8443 Allow
130 allow-api-ciam In asg-play-api asg-ciam 8443 Allow
140 allow-api-data In asg-play-api asg-play-data (Cosmos PE) 443 Allow
150 allow-bastion-mgmt In AzureBastionSubnet asg-play-* 22, 3389 Allow
4000 deny-vnet-inbound In VirtualNetwork VirtualNetwork * Deny
4096 deny-all-inbound In * * * Deny

The ASGs make the rules readable and portable across every customer-API spoke — membership is what a NIC joins, and the NSG never mentions an IP.

ASG Membership Purpose
asg-play-web Playback/portal front-ends, AKS ingress NICs Accepts only from Front Door / App Gateway
asg-play-api Entitlement, playback, concurrency services East-west from web tier only
asg-drm-license DRM licence proxy / SPEKE front, key-svc callers Reachable from playback API only
asg-ciam B2C token endpoints, custom-policy hosts API tier + Front Door only
asg-mgmt Jumpboxes, monitoring/backup agents Bastion-only inbound

Defining an ASG-referencing rule in Terraform keeps the intent legible and is the pattern every spoke module reuses:

resource "azurerm_network_security_rule" "api_to_drm" {
  name                        = "allow-api-drm"
  priority                    = 120
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_application_security_group_ids      = [azurerm_application_security_group.play_api.id]
  destination_application_security_group_ids = [azurerm_application_security_group.drm_license.id]
  source_port_range           = "*"
  destination_port_range      = "8443"
  resource_group_name         = azurerm_resource_group.customer_api.name
  network_security_group_name = azurerm_network_security_group.customer_api.name
}

Central inspection is a Firewall Policy whose rule-collection groups separate threat control from egress control, so adding a new CDN origin-pull FQDN never touches the IDPS baseline. Threat-intel and IDPS run in Deny mode for high/critical signatures; egress is FQDN-scoped per segment.

Rule collection group Type Priority Action Example rule
rcg-threat (IDPS / TI) 100 Deny IDPS high/critical + threat-intel Deny, TLS inspect on
rcg-media-egress Application 200 Allow asg-media → CDN origin-pull + multi-DRM SaaS licence + SPEKE FQDNs :443
rcg-ciam-egress Application 250 Allow asg-ciam → social IdP + payment-gateway (PCI) FQDNs :443
rcg-default 65000 Deny implicit deny-all egress (logged)

The entitlement store, DRM key service and billing PaaS are reached only through Private Endpoints with public network access disabled, so a stolen connection string resolves to a private address inside Lumina’s network and no public route exists. The private DNS zones must be linked to the hub or resolution silently falls back to the public name — the classic failure the Key Vault firewall/RBAC recovery playbook walks through.

PaaS service PE subnet Private DNS zone Data class
Cosmos DB (entitlement) snet-pe-customer /26 privatelink.documents.azure.com Confidential
Azure SQL (billing/PCI) snet-pe-customer /26 privatelink.database.windows.net PCI
Storage (VOD origin/blob) snet-pe-media /26 privatelink.blob.core.windows.net Restricted content
Event Hubs (QoE telemetry) snet-pe-analytics /26 privatelink.servicebus.windows.net Confidential
Key Vault Managed HSM (DRM keys) snet-pe-shared /26 privatelink.managedhsm.azure.net Restricted (keys)

Azure network security around the media customer-API spoke

Administrative access uses Azure Bastion in its own subnet, so no RDP or SSH port is ever exposed to a network the workload teams can route to, and every session is brokered and logged. The whole design is validated the way an auditor validates it: NSG flow logs, Firewall logs and VNet flow logs stream to Sentinel, and Traffic Analytics is used to prove there is no media-processing-to-corporate or analytics-to-DRM flow that policy did not intend.

AWS network security (deep dive)

AWS realises the identical intent in its own primitives. Viewer traffic enters through CloudFront fronted by AWS WAF (and, behind it, an Application Load Balancer with AWS WAF) running the same OWASP-prevention rule set and bot rules as the Azure edge so both clouds present an equal bar. All traffic between VPCs and to the internet is steered by the regional Transit Gateway into a dedicated inspection VPC, where AWS Network Firewall runs in appliance mode — the mode that guarantees symmetric, stateful inspection so a long segment transfer’s return traffic is examined by the same engine that saw the request instead of dying asymmetrically mid-stream. Inside each VPC, security groups and NACLs enforce default-deny: security groups are stateful tier allow-lists that reference each other rather than CIDRs, and NACLs add a stateless subnet backstop. Playback, DRM and origin are separate VPCs, not subnets, so a blast radius stops at an account boundary.

SG Dir Source / Dest Port Purpose
sg-play-web In sg-alb 8443 Playback/portal ingress from ALB only
sg-play-api In sg-play-web 8080 Entitlement/playback API from web only
sg-drm-license In sg-play-api 8443 SPEKE / licence proxy from API tier only
sg-ssai In sg-play-api 8080 SSAI stitcher from playback API
all workload SGs Out (no 0.0.0.0/0) Egress only via TGW → Network Firewall

Referencing a source security group instead of a CIDR is the control that keeps the allow-list honest as instances churn:

resource "aws_security_group_rule" "drm_from_api" {
  type                     = "ingress"
  security_group_id        = aws_security_group.drm_license.id
  source_security_group_id = aws_security_group.play_api.id
  from_port                = 8443
  to_port                  = 8443
  protocol                 = "tcp"
  description              = "DRM licence proxy accepts only the playback API tier"
}

The NACL is the stateless subnet backstop — deliberately coarse, denying by default, allowing only intra-estate and post-inspection egress:

Rule Dir CIDR Port Action
100 In 10.40.0.0/12 8443 Allow (intra-AWS estate)
110 In 10.0.0.0/12 8443 Allow (on-prem MOC control)
32767 In 0.0.0.0/0 * Deny
100 Out 10.0.0.0/8 * Allow (to hub / inspection)
110 Out 0.0.0.0/0 443 Allow (post-inspection egress)

Network Firewall runs a STRICT_ORDER stateful policy — the ordering that stops a Suricata pass from beating a drop and letting a denied domain leak — with HOME_NET pinned to the workload supernet and SNI/TLS allow-rules only for the DRM, payment and CDN partners.

Type Rule Action
Stateless Fragmented / malformed packets drop
Stateful (managed) AWS threat-intel + abused-domains group drop
Stateful (custom) TLS SNI ∈ {multi-DRM SaaS, SPEKE, payment gw, CDN origin} pass
Stateful (default) HOME_NET → any :443 not allow-listed drop_established

Detection is continuous and org-wide: GuardDuty (VPC flow, DNS, S3, EKS, Malware) and Inspector feed Security Hub with the FSBP and PCI-DSS standards, and findings route to the delegated-admin Security account, then on to Sentinel. GuardDuty findings are wired to graded automated responses so a high-severity content or key event does not wait for a human to notice — and an anomalous read of a mezzanine bucket is treated as a potential TPN/MovieLabs content-security incident, not just an alert.

GuardDuty finding Severity Automated response
Exfiltration:S3/ObjectRead.Unusual (origin/mezzanine bucket) High S3 public-block + page Content Security (TPN incident assessment)
CredentialAccess:IAMUser/AnomalousBehavior (DRM key-svc role) High Revoke session, rotate SPEKE key, review CloudTrail
UnauthorizedAccess:EC2/SSHBruteForce High SSM swaps to isolation SG, snapshots for forensics
Backdoor:EC2/C&CActivity!DNS High Quarantine ENI, page SOC, capture memory
Policy:S3/BucketPublicAccessGranted (VOD/origin) High Config rule auto-remediates public-access-block

AWS network security around the media workload VPC

Administrative access uses AWS Systems Manager Session Manager rather than bastion hosts or open SSH — no inbound management ports, every session brokered, recorded and shipped to the immutable Log Archive. Enabling GuardDuty for the whole organisation from the delegated admin is a one-time control that auto-enrolls every new workload account at vend time:

aws guardduty update-organization-configuration \
  --detector-id "$DETECTOR" --auto-enable-organization-members ALL \
  --features '[{"Name":"S3_DATA_EVENTS","Status":"ENABLED"},
               {"Name":"EKS_RUNTIME_MONITORING","Status":"ENABLED"},
               {"Name":"EBS_MALWARE_PROTECTION","Status":"ENABLED"}]'

As on Azure, the two binding rules hold: no unrestricted east-west traffic between accounts or tiers, and all egress is policy-controlled and logged through the central Network Firewall.

Zero-downtime release patterns

The mandate is unambiguous: viewers never see a maintenance window. There is no 2 a.m. Sunday when a live channel goes dark for a deploy, no hour when the playback API may return “try again,” no window when a subscriber cannot resume a film. Zero-downtime is therefore a property the estate is engineered to preserve, not a deployment convenience, and it rests on two load-bearing ideas. The first is that deployment is not release: new code reaches production long before any viewer traffic exercises it, decoupled by feature flags in Azure App Configuration / AWS AppConfig, so a new player capability or ad format ships dark and is activated by a runtime flag flip for one cohort before it widens. The second is that every release is reversible: no change reaches a Tier-1 service unless it can be withdrawn in seconds — a flag off, traffic shifted back, a canary aborted — so reversibility is an entry condition, not an improvised contingency.

Zero-downtime release patterns and event-freeze for a Tier-1 live streaming service

These principles are realised through a constrained set of patterns, each matched to a workload class and a QoE risk profile. The decisive streaming nuance is that the health gate is viewer-experience telemetry, not just HTTP status: a canary that keeps returning 200 but pushes video-start-time past 2s or rebuffer past 0.4% is failing and must roll back, because a degraded stream is an outage the viewer feels even when the API says fine.

Pattern Mechanism Azure implementation AWS implementation Best-fit streaming service Rollback trigger
Blue-green Parallel env, cutover App Service slots / AKS + AGIC CodeDeploy blue-green, ALB target groups Playback API, entitlement, portal Swap back / shift target group
Canary Weighted ramp + QoE gates Front Door weights, Argo Rollouts + Flagger CodeDeploy canary, ALB weighted Reco, SSAI, CIAM token svc QoE breach → auto-abort
Rolling Incremental replace AKS/VMSS rolling, maxUnavailable EKS/ECS rolling update Encode/package workers, QoE ingest Probe fail → halt + revert
Feature flags Runtime toggle Azure App Configuration AppConfig / LaunchDarkly New player feature, ad format, ABR ladder Flag off
Expand-contract Parallel schema pgroll / EF Core additive Liquibase + Aurora Entitlement store, billing SoR Stop dual-write, drop new
Health traffic shift Probe-gated weight Front Door / AGW health probe ALB health + Route 53 Every Tier-1 service Unhealthy probe → drain
Event-freeze Change moratorium CAB freeze + pipeline lock CAB freeze + pipeline lock Live finals / premium events No change permitted; break-glass only

Blue-green governs the viewer-facing control services: a fully provisioned green slot runs the new version alongside live blue, smoke-tested in isolation against a synthetic playback of a reference title, an entitlement round-trip and a DRM licence issuance before any viewer traffic, then cut over by a swap that carries no cold start because the slot is warmed first. The App Service swap-with-preview is the concrete mechanism, and blue is kept warm as an instant rollback target:

az webapp deployment slot swap -g lm-lz-vod-prod \
  -n lm-eus2-playback-api-prod --slot green --target-slot production --action preview

az webapp deployment slot swap -g lm-lz-vod-prod \
  -n lm-eus2-playback-api-prod --slot green --target-slot production --action swap

Canary and progressive delivery apply where traffic can be split by weight — reco, SSAI and CIAM ramp 5→25→50→100 with a bake per step, each gated on the QoE and business KPIs that actually matter. The gate is automatic and refuses to promote unless Dynatrace and the Conviva-class QoE plane hold VST p95 < 2s, rebuffer ratio < 0.4%, playback-failure < 0.5% and (for SSAI) ad-fill rate within budget for the whole bake window; a drop in ad-fill means the stitcher is failing calls to the ad-decision server and the release holds. A guardrail breach shifts weight back with no human in the critical path. The per-service strategy below is the operational contract each streaming team signs.

Service Tier Pattern Traffic shift Gate KPI Rollback Window
Playback API / entitlement T1 Blue-green + dark + long bake 5→25→50→100 over 12h Playback-failure, VST p95, entitlement 2xx Swap to blue None (dark)
DRM licence service T1 Blue-green 10→100 Licence-issue success, licence latency Swap None
CIAM token service T1 Canary 5→50→100 over 2h Login success, token latency, ATO rate Shift to blue None
SSAI stitcher T1 Canary + session drain 10→50→100 Ad-fill %, manifest errors, rebuffer Drain + shift None
Live origin / packager T1 Rolling + redundant origin Node-by-node Segment-publish lag, LL-CMAF latency Halt node None (never mid-event)
Encode / transcode T2 Rolling n/a Job success, ABR-ladder parity Redeploy prev Off-peak OK
Reco / ad-reporting T2 Canary / rolling n/a Job success, model KPIs Redeploy Off-hours OK

Underpinning every stateful service is the discipline that most often defeats zero-downtime ambition: schema change. Lumina never takes a lock-acquiring migration against a live entitlement or billing store. Instead it applies expand/contract (parallel-change): expand the schema additively, dual-write old and new shapes while backfilling history, run both in parallel until the new path is proven, then contract by removing the old structure once nothing reads it. Each step is an additive, backwards-compatible, sub-50ms metadata operation, so application code and schema deploy independently and each tolerates the other’s previous version.

Phase Action Application behaviour Reversible?
Expand Add nullable column / table / index additively Reads and writes old shape Yes — drop the new object
Dual-write Write both old and new shapes Both populated, old authoritative Yes — stop writing new
Backfill Batch historical rows into new shape (idempotent) Old still authoritative Yes — rerun or discard
Migrate reads Flip read path to new shape behind a flag New authoritative Yes — flag back to old
Contract Drop old column after a bake period New shape only No — final, one-way

The pattern menu above is the default regime. For a premium live event — a sports final peaking at 45M concurrent — the regime inverts: the safest change is no change at all. Lumina runs a formal event-freeze (change-freeze) around every marquee live window, because a routine deploy that would be a shrug on a Tuesday becomes an unrecoverable on-air failure when 45M viewers are watching a single stream that cannot be re-run. The freeze is not a vague “be careful” — it is scoped, enforced by the pipeline, and has exactly one exception path.

Freeze scope Enforced by During the freeze Exception path
Tier-1 live path (origin, packager, playback, DRM, SSAI) Pipeline lock + CAB freeze flag on the release gate No deploy, config, schema or IaC apply Break-glass CAB, two-person, live-incident only
Multi-CDN steering + edge config Steering weights pinned to the rehearsed profile Locked to pre-event profile Emergency re-steer runbook (pre-approved)
Autoscale / capacity Pre-scaled to N× the finals peak before freeze Scale-out allowed; scale-in disabled Automatic only, never manual
CIAM / entitlement Rate-limits + bot rules pre-tuned No policy or rule change Break-glass CAB
Non-Tier-1 (VOD encode, analytics, corp) Not frozen Normal CI/CD continues n/a

The freeze has a timeline as well as a scope, so every team knows when the door closes and when it reopens. The window widens with the stakes: a world final freezes earlier and thaws later than a mid-season fixture.

Phase Window (relative to event) What happens Gate to advance
Readiness & prep T-72h → T-24h Final canaries land; caches pre-warmed; capacity pre-scaled; DR rehearsal run Readiness review sign-off
Change freeze T-24h → event start Zero change to the Tier-1 live path; pipeline locked CAB declares freeze active
Event window Live Monitoring + autoscale-out only; war-room staffed Break-glass CAB for any manual act
Post-event thaw T+2h after end Staged resume of normal CI/CD; retrospective Incident review clean

The common thread across the whole regime is traffic shifting plus health-based rollback for every major release — and, for the events that cannot tolerate even that, a disciplined freeze. Whether the unit of change is a blue-green slot, a canary weight or an active-active region, the release advances by moving a controlled proportion of traffic against a QoE gate and retreats automatically the moment a viewer-experience signal degrades; and because deployment is separated from release by flags, even an already-deployed change is neutralised in seconds without redeploying anything.

Active-active multi-region data topology

Zero-downtime release depends on a data topology that contains failure as readily as it contains change, and streaming forces a harder problem than most because the datastores have irreconcilable consistency needs. The billing and subscription system of record (PCI scope) demands that a committed charge is durable and correct — a silently dropped or duplicated write is a chargeback or a lost renewal; entitlement, playback-session and watchlist state can tolerate eventual convergence for the sake of low-latency multi-region writes so a viewer near any region gets an instant answer; and the 400 PB VOD library is write-once and simply needs to arrive. So the topology splits by consistency need rather than treating storage as one homogeneous thing, and the organising rule for anything money-like is region-pin the writer: each account’s billing writes go to its home region, so two regions never post to the same subscription at once. Lumina runs this across the Azure East US 2 / West Europe / Southeast Asia set and the AWS us-east-1 / eu-west-1 / ap-southeast-1 set, mirroring the patterns in Azure multi-region active-active design and AWS multi-region active-active.

Active-active data topology: Region A to Region B datastores and conflict handling

Each datastore is matched to its need, and the table below is the load-bearing reference: what replicates, how, in which topology, at what RPO, and — the question people forget until an incident — how a conflict is resolved.

Datastore Data domain Replication mechanism Topology RPO Conflict handling Consistency
Cosmos DB (multi-write) Entitlement, watchlist, resume-point, concurrency Multi-region write Active-active ~0 LWW on _ts (prefs) / stored-proc merge (entitlement) Session
DynamoDB global tables CIAM session, device registration, notif counters Stream-based replication Active-active Sub-second LWW per item Eventual
Azure SQL / Aurora Global Billing / subscription SoR (PCI), TVOD purchases Auto-failover group / Aurora Global DB Active-passive (RW region-pinned) ≤5m (0 planned switchover) Single-writer, region-pin Strong in-region
Event Hubs / Kinesis + SB/SQS geo-DR QoE telemetry, ad/beacon events, SCTE signals Geo-DR / cross-region Active-passive (paired) Near-0 Idempotent replay by event id At-least-once
Blob GRS / S3 CRR VOD library (400 PB), origin, mezzanine Async object replication Active-passive Minutes (RA-GRS) Immutable objects — no conflict Eventual + reconciliation
Key Vault MHSM / KMS MRK DRM content keys, edge signing keys HSM replication / multi-region keys Active-active 0 n/a (same key material) Strong
Azure AD B2C / Cognito CIAM identity (120M) Native multi-region Active-active 0 Multi-master convergence Eventual → strong

The billing path deserves its own detail because it is the one that touches money and PCI scope. Azure SQL auto-failover groups and Aurora Global Database replicate the billing/subscription store to the secondary region; a planned switchover is RPO 0, an unplanned failover loses at most the replication lag, which is why lag is alarmed at RPO/2. The read-write listener is what the application connects to so a failover is transparent to connection strings:

az sql failover-group create -n lm-billing-fog -g lm-lz-data-prod \
  --server lm-eus2-billing-sql --partner-server lm-weu-billing-sql \
  --failover-policy Manual --grace-period 1 \
  --add-db lm-billing lm-tvod-purchases
# app connects to lm-billing-fog.database.windows.net (RW listener) — never the raw server

The engagement stores are genuinely active-active, and the discipline there is knowing exactly where last-writer-wins is safe and where it is lethal. For Cosmos multi-region write, LWW on _ts is fine for a watchlist add or a resume-point, but entitlement uses a stored-procedure custom-merge policy and the conflicts feed is monitored for silent drops — because a lost entitlement write shows a paying subscriber a “not entitled” error, and a wrongly-merged concurrency counter lets one shared password stream on ten devices at once. DynamoDB global tables are LWW per item — perfect for a CIAM session token or a device registration, and categorically never the billing ledger. The per-domain routing contract makes the boundary explicit.

Domain Store Region model Write routing Conflict rule RPO
Entitlement / concurrency Cosmos multi-write Active-active Nearest region Custom merge (entitlement) ~0
Playback session / resume DynamoDB global Active-active Nearest region LWW per item Sub-second
Billing / TVOD (PCI) Azure SQL failover group A-active / B-standby Region-pin by account None (single writer) ≤5m
CIAM identity B2C / Cognito Active-active Nearest region Multi-master converge ~0
VOD / origin objects Blob GRS / S3 CRR Active-passive Primary ingest region Immutable, reconciled Minutes
QoE / ad telemetry Event Hubs / Kinesis geo-DR Active-passive Primary namespace Idempotent replay ~0
DRM content keys Key Vault MHSM / KMS MRK Active-active Any region Same key material 0

The VOD library is the outlier and by far the largest: it replicates by Blob GRS / S3 CRR, which copies new objects only, asynchronously, and never replicates a lifecycle transition or a delete marker by default. So a reconciliation job compares per-title object counts and manifest integrity between regions on a schedule, surfacing a missed segment before a viewer in the secondary region hits a gap in a title. Across every store, the governing rule is the one covered in depth in multi-region data replication strategies: replication lag is your live RPO, and if lag breaches the tier objective, failing over means losing committed data — a trade to be decided before the incident, not during it.

Active-passive and warm-standby topology

The engagement tier of this platform is deliberately active-active — Cosmos and DynamoDB multi-write, health-based edge steering and CRDT-style counters let entitlement and session state take writes in more than one region at once, as the topology section established. The billing and subscription system of record cannot join it, and understanding why is the whole of this section. A subscription balance has exactly one correct value at any instant; the moment two regions accept a charge or a plan change to the same account concurrently you are running not one ledger but two divergent ones, with no mathematically safe merge after the fact — last-write-wins silently double-charges or drops a renewal. So for the strongly-consistent billing core, the PCI-scoped stores that hang off it, and the SAP back office an auditor expects to see fail over on command, the correct topology is active-passive with a warm standby: one region is the sole writer, a paired region in the same legal geography stands warm and continuously fed, and failover is a deliberate, fenced, one-way act.

Active-active is the default only for stateless, eventually-consistent, or per-account-partitioned work. For everything with a single authoritative writer, active-passive is not a weaker choice — it is the correct one, and treating it as a fallback is how operators end up with split books. The properties that decide it, with the streaming example for each:

Property of the workload Active-active is right when… Active-passive / warm standby is right when… Lumina example
Consistency model Eventually consistent; conflicts converge Strongly consistent; one correct value per key Billing/TVOD ledger → passive
Write ownership Any region may own any write One writer authoritative at a time Subscription state machine
Merge correctness Concurrent writes merge without loss No safe merge of concurrent writes exists Double-charge is unrecoverable
Latency budget Local-region write latency required A quorum/replication delay is tolerable SAP HANA, payment state
Regulatory expectation Continuous availability suffices Auditor expects a demonstrable failover PCI-DSS, SOC 2 exit test
Blast radius of a bug A bad write is contained per region A bad write corrupts the single source Schema/config error on billing SoR

Recovery is not one posture but a ladder of four, each trading cost for recovery speed. Lumina places every workload on a rung deliberately; a flat posture either over-insures a sandbox or, far worse, under-insures the playback path.

Strategy What runs in the DR region Typical RTO Typical RPO Rel. monthly cost Recovery action
Backup & restore Nothing but immutable backups 4–24 h+ Hours (last backup) ~1.05× baseline Provision from IaC, restore, cut over
Pilot light Core data replicating; compute scaled-to-zero 30 min – 4 h Minutes ~1.15–1.25× Scale up the dormant core, cut over
Warm standby Scaled-down but functional copy, data live 2–30 min Seconds – minutes ~1.4–1.6× Scale out, promote data, cut over
Active-active Full-capacity peer taking live traffic Seconds (traffic shift) ≈0 ~2.0×+ Shift traffic weight; no promote/restore

The four recovery tiers already defined for this estate map cleanly onto the ladder. The mapping is the operational contract: it names which primitive delivers each tier’s RTO/RPO.

Tier Representative workloads DR strategy Primary primitive RTO / RPO target
T0 Identity, DNS, network + security control plane Active-active (identity itself multi-region) Front Door + Traffic Manager / Route 53, global tables ≤15 min / ≈0
T1 Entitlement, playback API, DRM-licence, origin, live-control, SSAI-core, CIAM-auth Warm standby (edge active-active) SQL failover group · Aurora Global · Cosmos/DDB multi-write · geo origin ≤15 min / ≤1 min
T2 Business apps, analytics, support, ad-reporting Pilot light Data replicated, compute scaled-to-zero ≤4 h / ≤1 h
T3 Dev, sandbox, non-critical Backup & restore Cross-region immutable backup ≤24 h / ≤24 h

A warm standby is only as good as the mechanism that promotes it, and the unifying discipline is that promotion of a system of record is manual and one-way: no automatic failover on billing, because a false positive — a transient partition mistaken for a region loss — promotes a second writer and splits the ledger. Automation prepares, humans decide, and the decision is gated on a change record. Each data platform exposes its own primitive:

Primitive Platform Replication Promotion type Split-brain guard Failover gotcha
SQL failover group Azure SQL DB / MI Async continuous Manual planned / forced Manual RW policy --allow-data-loss = the RPO you accept
Aurora Global Database AWS Aurora Storage-level, ~1 s lag Managed (0 RPO) / detach-promote Managed failover only Detach-promote costs the in-flight lag
Cosmos multi-write Azure Multi-master Automatic per region Conflict feed monitored LWW silently drops — merge policy for entitlement
Traffic Manager Azure DNS n/a (control plane) Priority endpoint flip Health-probe threshold DNS TTL ≥30 s slows cutover
Route 53 failover AWS DNS n/a (control plane) Health-check record flip Health-check threshold Client IP-pinning caches past TTL

The unplanned billing failover forces the RPO decision into the open — the flag is the data-loss you accept:

# PLANNED (0 RPO): reverses roles, waits for the secondary to catch up
az sql failover-group set-primary --name lm-billing-fog \
  --resource-group lm-lz-data-prod --server lm-weu-billing-sql

# UNPLANNED (primary region gone): forced, accepts the in-flight replication lag as RPO
az sql failover-group set-primary --name lm-billing-fog \
  --resource-group lm-lz-data-prod --server lm-weu-billing-sql --allow-data-loss

Split-brain — two nodes both believing they are primary and both accepting writes — is the failure mode that turns a routine failover into an unreconcilable ledger, and it is defended in depth rather than by a single control. Failback is not a mirror-image auto-return: once the standby is promoted it is the primary, and the recovered region is rebuilt as the new warm standby and re-synchronised from the current writer before any future failover considers it.

Control What it prevents Implementation
Manual-only RW failover Auto-promotion on a transient partition Manual policy; managed-failover-only on Aurora
Quorum witness / fence Both nodes claiming primary Odd-node witness; revoke write identity
Write-identity revocation Old primary accepting a late write Rotate/revoke the primary’s DB credential on fence
One-way promotion Ambiguous “who is primary” after failback Old primary rebuilt as the new standby, never re-attached live
Idempotency keys Double-charging an in-flight TVOD purchase Every purchase carries a key; replay is a no-op

Disaster recovery and resiliency

Resiliency is engineered, not assumed, and the engineering begins by classifying every service into a recovery tier and designing each tier to a stated objective drawn straight from the operating model. A flat DR posture either over-invests in a sandbox that can tolerate a day’s recovery or, far worse, under-protects the playback path. Each strategic region is paired with a DR region in the same geography and cloud — East US 2 ↔ West Europe for the Americas/EU pairing, Southeast Asia held for APAC-in-APAC residency, us-east-1 ↔ eu-west-1, ap-southeast-1 local — so a regional loss has a pre-built, in-jurisdiction destination that respects GDPR/CCPA data-residency boundaries. The tier definitions are the contract, and streaming’s numbers are tight at the top because a 15-minute playback outage during a live final is a brand event measured in millions of abandoned sessions.

Disaster recovery tiers 0-3 with RTO/RPO and failover mechanism per tier

Tier Scope / examples RTO RPO DR strategy Cost posture
T0 Identity, DNS, network + security control plane, privileged access ≤15 min ≈0 Active-active across regions High (always-on)
T1 Entitlement, playback API, DRM-licence, origin, live-event control, SSAI-core, CIAM-auth ≤15 min ≤1 min Warm standby + active-active edge High
T2 Business apps, analytics, support, ad-reporting ≤4 h ≤1 h Pilot-light Medium
T3 Dev, sandbox, non-critical reporting ≤24 h ≤24 h Backup-restore Low

The recovery strategy is differentiated by tier rather than applied uniformly, because the cheapest posture that meets the objective is the right one — the RTO/RPO fundamentals behind these choices are laid out in BC/DR RTO/RPO fundamentals. Tier-0 and the Tier-1 edge run active-active and recover by traffic shift; the Tier-1 control core runs warm standby with continuous replication so promotion is a fast, controlled switchover; Tier-2 runs pilot-light; Tier-3 restores from immutable backup.

Strategy How it recovers Azure AWS Tiers
Active-active Traffic shift, no restore Front Door + Traffic Manager, multi-write stores Route 53 + global tables T0, T1 edge
Warm standby Promote a running replica SQL failover group + ASR-replicated compute Aurora Global + pre-scaled ECS/EKS T1 core
Pilot-light Scale minimal footprint on failover ASR minimal + replicated data AMI + replicated RDS, scale-out T2
Backup-restore Restore from vault Azure Backup + LTR AWS Backup T3

A recovery objective is only as credible as the last test that proved it, so DR testing is a standing obligation, not an annual ceremony. Lumina runs a layered cadence — automated fault injection continuously, per-tier failover quarterly, a full-region game-day annually — and adds a streaming-specific drill the other industries lack: a live-event failover rehearsal run before every marquee event, where origin, DRM and CDN-steering failover are exercised under synthetic concurrency so a real failover during the final does not produce a black screen.

Test Scope Frequency Tool Pass criteria
Chaos / fault injection Dependency, AZ, instance loss Monthly Azure Chaos Studio / AWS FIS Graceful degrade, no cascade
Per-tier DR failover One tier to DR region Quarterly ASR / AWS Backup + runbook Meets tier RTO/RPO
Live-event failover rehearsal Origin / DRM / steering failover under synthetic load Before each premium event FIS + load generator QoE holds; LL latency < 8s; no black screen
Backup restore test Random workloads Monthly Backup restore Integrity + boot pass
Full-region game-day Lose an entire region Annual FIS + ASR orchestration Tier-0/1 within target
Ransomware recovery Immutable restore, clean rebuild Semi-annual Vault-lock restore + IaC Clean-state attestation

Disaster recovery runbooks

A recovery target nobody has rehearsed is a guess dressed as a commitment. RTO/RPO numbers are meaningful only when a named owner has executed the procedure that meets them against a realistic scenario, recently enough that the runbook reflects the current estate. Lumina’s orchestration builds on the pattern in DR orchestration with Site Recovery and ServiceNow: each runbook names its trigger, its steps, its single accountable owner, the tier target it must meet, and — the step most often omitted — how recovery is validated before the incident is declared closed. Recovery always proceeds in tier order, because nothing else can come back until identity, DNS and the network control plane are up.

The Tier-0 runbooks run first and fastest; their failure blocks every runbook below them, which is why identity and connectivity are active-active with RPO≈0. The AD forest recovery runbook covers the deepest workforce-identity-loss case behind this.

Scenario Trigger Procedure steps Owner Target Validation
Identity / access-plane failover Entra / Okta or CA plane degraded in primary Confirm break-glass path; fail CA + PIM plane to secondary region; promote private DNS resolver; verify Entra-to-AWS federation intact; confirm CIAM plane (separate) healthy Identity & Security T0 · 15m / ≈0 Workforce + admin auth succeed in DR; CA/PIM enforced; break-glass re-sealed
Network / security control failover Loss of hub, firewall, or an ExpressRoute / Direct Connect on-ramp Verify BGP withdrew the failed path; confirm second circuit carrying load; shift vWAN / TGW hub; confirm firewall policy active in secondary Network & Connectivity T0 · 15m / ≈0 End-to-end reachability on surviving circuits; IDPS active; no SPOF remaining

The Tier-1 runbooks are the ones a viewer feels, and each streaming domain fails over differently — entitlement/playback shifts edge weight and reconciles concurrency, the DRM service promotes a key-service replica, live origin re-points to a redundant packager, and CIAM (already active-active) simply drains a region. The unifying validation is no viewer sees an outage and no protected title becomes unplayable.

Scenario Trigger Procedure steps Owner Target Validation
Entitlement + playback-API failover Loss of the region hosting playback control Confirm Cosmos multi-write + billing SQL failover-group synced; shift Front Door / Route 53 weight to survivor; scale hot playback + entitlement; reconcile concurrency counters by idempotent merge Playback Platform T1 · 15m / ≤1m Playback resumes; VST p95 < 2s; no false “not entitled”; concurrency correct
DRM-licence failover Loss of the DRM / key-service region Promote multi-DRM SaaS / SPEKE secondary; re-point licence proxy; confirm MHSM / KMS multi-region key material present; verify Widevine + FairPlay + PlayReady issuance and HDCP Content Security T1 · 15m / ≈0 Licences issue in DR; protected content plays; HDCP enforced; no key gap
Live origin + event-control failover Loss of the live encode / package / origin region Fail to the redundant origin/packager (second zone/region); re-point CDN origin-pull; confirm dual contribution (SRT / Zixi) still landing; resume SCTE-35 + SSAI stitching Live Operations T1 · 15m / ≤1m Segments publishing; LL-CMAF latency < 8s; no black screen; ad markers intact
CIAM-auth failover Loss of a CIAM region (B2C / Cognito) Drain the failed region (both active-active); confirm social/email login on survivor; verify rate-limit + bot rules active; reconcile session/device state CIAM Platform T1 · 15m / ~0 Login + token issuance succeed; ATO defences active; no lockout storm

Failing over is only half the discipline; failing back is where estates that never rehearse it corrupt data by reversing replication carelessly. Failback is a deliberate, off-peak operation gated on the primary being healthy and attested clean — never re-entering a compromised environment.

Step Action Guard / gate
1 Confirm primary region healthy, patched, and attested clean No reintroduced compromise (ransomware case)
2 Reverse-replicate the delta accumulated in DR back to primary Lag < tier RPO before proceeding
3 Switch writers back during an off-peak, non-event window No live premium event in flight
4 Validate reconciliation across billing, entitlement, catalogue Charge / entitlement / object parity clean
5 Re-seal break-glass, restore normal steering, close incident Full audit trail complete; lessons logged

Finally, a runbook is executed by people under pressure, so the roles and communications are pre-assigned — a streaming DR incident carries content-security and viewer-privacy dimensions that make the content-security and privacy roles as load-bearing as the platform ones.

Role Responsibility Escalates to
Incident Commander Declares DR, owns the go/no-go, runs the bridge VP Platform / CTO
Content Security Officer Owns TPN/MovieLabs incident assessment, piracy/leak response CISO
Live Event Director Protects on-air continuity; owns break-glass during events Incident Commander
Privacy Officer Runs GDPR/CCPA breach assessment (72-hour clock) Legal / Compliance
Platform / Network / Identity leads Execute the per-domain runbooks in tier order Incident Commander
Communications lead Viewer status page, partner/affiliate liaison, regulator Executive sponsor

Backup, immutability and ransomware recovery

Disaster recovery and backup solve different problems and it is dangerous to conflate them. The active-passive topology above protects against a region going away with its data intact; backup protects against the data itself being wrong — a bad migration, a fat-fingered DELETE against the catalogue, and above all ransomware, where an adversary who has reached production admin does not knock a region over but encrypts the live estate and, first, tries to destroy the very backups you would recover from. For a streaming operator ransomware has a second, industry-specific edge: the attacker may exfiltrate the 400 PB content library or the mezzanine masters (a MovieLabs/TPN content-security incident with studio notification obligations), and if they encrypt the DRM content keys, every protected title in the catalogue becomes unplayable at once even though the media objects are intact. A warm standby is no defence, because the corruption replicates to it in seconds. The backup architecture is therefore engineered around one unflattering assumption: the attacker will, at some point, hold production administrator rights, and recovery must survive that.

That assumption is what the industry codifies as 3-2-1-1-0, and every element of the design below exists to satisfy one of its digits, following the pattern in multicloud backup and ransomware recovery.

Digit Rule How Lumina satisfies it
3 Three copies of the data Production + on-cloud backup vault + cross-region/cross-cloud copy
2 On two different media/platforms Azure Backup vaults and AWS Backup / S3 — cross-cloud, not one vendor
1 One copy off-site Cross-region copy in the paired geography
1 One copy offline / air-gapped / immutable Immutable, isolated tenant/account with no standing access
0 Zero errors — verified by restore testing Automated monthly restore tests with integrity validation

Backup, immutability and ransomware recovery — sources to immutable vaults to air-gap to clean-room restore

The estate standardises on Azure Backup with Recovery Services / Backup vaults and AWS Backup as the two native planes, orchestrated centrally so protection is assigned by tag and policy, not configured per resource, following AWS Backup DR strategies. Frequency, retention and copy topology are differentiated by data class — the billing ledger and the DRM keys are not protected like a dev VM, and the 400 PB library is protected by immutable object versioning rather than nightly copies.

Data class Tool Frequency / RPO Hot retention Long-term (WORM) Copies
Billing / subscription SoR (PCI) Native DB PITR + vault Continuous PITR (≤5 min) 35 days 7–10 yr immutable Local + cross-region + air-gap
Entitlement / CIAM data Cosmos continuous backup / DynamoDB PITR Continuous 30 days Per retention Local + cross-region
VOD library / mezzanine (400 PB) Blob immutability + S3 Object Lock + versioning On ingest (versioned) n/a Per content schedule (yrs) Local + cross-region, WORM
DRM content keys / secrets Key Vault / KMS backup + soft-delete + purge-protect On change Soft-delete window HSM escrow Vault + HSM
VMs / AKS / EKS (encode/origin) Azure Backup · AWS Backup Daily (critical: 4–6 h) 30 days 1 yr Local + cross-region
Config / IaC state Git + versioned state backend On every apply Full history Repo-retained Git remote + state replica

The last two rows are the ones teams under-protect, and in streaming they are lethal to skip. DRM keys are the subtlest single point of failure in the whole estate: a restored catalogue is worthless if the keys that decrypt it sat in a vault the attacker purged, so Key Vault and KMS run soft-delete with purge protection, key material is escrowed to a managed HSM, and the Key Vault backup and rotation model is part of the recovery plan, not an afterthought. Configuration and Terraform state are as load-bearing as data — a landing zone you cannot rebuild is one you cannot recover a live event into — so IaC repos, pipeline definitions and the encrypted, versioned remote state backends are themselves backed up and replicated. What each source needs and where it bites:

Source Backup method Native gotcha
Azure SQL / Aurora (billing) PITR + long-term retention / snapshots PITR alone is same-region; add cross-region copy
Cosmos / DynamoDB (entitlement) Continuous backup / PITR PITR window bounded; export for long retention
Blob / S3 (VOD, origin, mezzanine) Versioning + Object Lock + backup Versioning without Object Lock is still deletable
DRM keys (Key Vault / KMS) Soft-delete + purge-protect + HSM escrow Purge protection OFF = an admin permanently deletes → catalogue bricked
VMs (encode/origin) Application-consistent snapshot Crash-consistent only unless VSS/pre-scripts run
Terraform state Versioned, replicated backend A lost state file orphans the entire estate

The difference between a backup and a recoverable backup, in a ransomware scenario, is whether the recovery points can be deleted or encrypted by the same identity that owns production. Four controls make them survivable, layered so no single compromised credential can defeat the set. Immutability / WORM makes a recovery point write-once for its retention: on Azure, Recovery Services and Backup vaults support an immutability setting that, once Locked, is irreversible; on AWS, Backup Vault Lock in compliance mode and S3 Object Lock in compliance mode make points undeletable until expiry by anyone, including the root account.

# AWS Backup Vault Lock in COMPLIANCE mode — after the cooling-off, no one can shorten or delete
resource "aws_backup_vault_lock_configuration" "airgap" {
  backup_vault_name   = aws_backup_vault.airgap.name
  changeable_for_days = 3    # cooling-off; after this the lock is immutable (compliance)
  min_retention_days  = 2555 # 7 years
  max_retention_days  = 3650
}

# S3 Object Lock (COMPLIANCE) for the mezzanine/VOD air-gap copy — GOVERNANCE is bypassable, never for content
resource "aws_s3_bucket_object_lock_configuration" "mezzanine_airgap" {
  bucket = aws_s3_bucket.mezzanine_airgap.id
  rule { default_retention { mode = "COMPLIANCE"  days = 2555 } }
}

Soft-delete keeps deleted points in a recoverable recycle state for a window so a deletion is reversible; multi-user authorisation / MFA-delete (Azure Resource Guard; S3 MFA-delete) ensures no lone identity can both disable protection and purge points; and air-gap is the last and most important — the immutable copy sits in a separate cloud, tenant and account, with no standing credentials and no network path from production, so the identity that owns the primary estate simply cannot reach it.

Control Cloud primitive What it stops Failure if misconfigured
Immutable / WORM (compliance) Locked vault immutability · Vault Lock · S3 Object Lock Encryption or deletion of recovery points Governance mode / unlocked = a privileged role deletes them
Soft-delete Enhanced soft-delete · versioning An immediate hard-delete of points Disabled or short window = purge succeeds
Multi-user auth / MFA-delete Azure Resource Guard · S3 MFA-delete A lone admin disabling protection Single identity owns both protect and delete
Air-gap / isolation Separate tenant/account, no standing access The prod identity reaching the copy Same account/tenant = one breach reaches all copies
Cross-region / cross-cloud copy AWS Backup copy · GRS · cross-cloud pipe Regional or single-vendor loss Single region/vendor = correlated failure

A backup is a cost until it is restored, so restore is specified as an SLA per data class and — the step most often skipped — tested on a cadence into an isolated, throwaway landing zone that runs row-count, checksum and application-level integrity checks and records the measured RTO. That automated proof is the 0 of 3-2-1-1-0.

Data class Restore method Restore RTO target Restore test cadence
Billing / subscription SoR DB PITR to timestamp ≤2 h Monthly, into isolated LZ
Entitlement / CIAM Continuous-backup restore ≤2 h Monthly
VOD / mezzanine objects Version / Object-Lock restore ≤4 h (per scope) Quarterly
DRM keys / secrets Key Vault / KMS restore + HSM escrow ≤1 h Quarterly
VMs / IaaS (encode/origin) Vault restore / rebuild from IaC ≤4 h Quarterly
Config / IaC state IaC re-apply + state restore ≤1 h Quarterly

Ransomware recovery is not a bigger restore; it is a forensic operation with a restore inside it, and it fails if run as a hurried “restore latest.” The governing insight is dwell time: an adversary typically lives inside the estate for weeks before triggering encryption, so the newest recovery points are the most likely to already contain the implant. The task is not to find the last backup but the last clean backup — a point that predates the intrusion — and to bring it into an environment the attacker does not already own.

# Stage Action Owner Exit criterion
1 Detect & declare Confirm ransomware (not outage); declare incident; open crisis bridge SOC Incident classified; privacy + content clocks started
2 Isolate & contain Sever affected networks; rotate suspect identities; freeze blast radius SOC + Cloud Security Attacker path cut; no lateral movement
3 Preserve evidence Snapshot compromised state for forensics before any change SOC Immutable forensic copies captured
4 Find the clean point Walk backups backwards from encryption; scan candidates for IOCs SOC + Cloud Ops Point validated as pre-intrusion, not just pre-encryption
5 Stand up clean room Rebuild isolated LZ from Terraform — patched, no path to prod Cloud Platform Eng Clean LZ up; known TTPs closed
6 Restore & scan Restore clean point (incl. DRM keys from HSM escrow) from the air-gapped copy; re-scan Cloud Ops Data + keys restored; EDR scan clean
7 Validate integrity Row-count, checksum, catalogue + entitlement reconciliation, licence-issue test Application Enablement Data reconciles; protected titles play; no reintroduced compromise
8 Reconnect per tier Restore services T0→T3 behind fresh credentials; resume gradually Cloud Ops + Network Clean-state attestation before viewer traffic
9 Forensics & harden Root-cause, close the entry vector, TPN/studio notification if content exfiltrated SOC + Content Security Vector closed; detections + guardrails updated

Three points distinguish this from a routine restore. The restore point must be scanned, not trusted (step 4): pulling the most recent backup usually restores the malware with the data. The target must be clean-built, not the original estate (step 5): restoring into the compromised landing zone re-infects on contact, which is precisely why Terraform state and pipeline definitions are first-class recovery assets. And the DRM keys are restored from the HSM escrow, not the live vault (step 6): because the on-cloud vaults may have been within the attacker’s reach, the escrowed, air-gapped key material is the one trusted to be uncorrupted — without it, a perfectly restored 400 PB library is still a wall of undecryptable ciphertext. Recovery resumes tier by tier behind wholly rotated credentials, and no viewer traffic returns until a clean-state attestation confirms the environment being reopened is not the one the attacker still holds a key to.

Application onboarding for 100+ services

Lumina Media runs 100+ apps and services — a live-playback API, VOD catalog service, entitlement engine, DRM-license proxy, SSAI stitcher, CIAM token service, recommendation ranker, ad-decision connector, dozens of back-office and partner services. If every team hand-builds its own subnet, policy, identity and pipeline, you do not get 100 landing zones — you get 100 snowflakes, each a bespoke audit finding and a bespoke outage. The only way this scales is a paved road: a self-service, manifest-driven onboarding where a team declares intent and the platform vends the matching landing zone with the guardrails already welded on. The team’s freedom is deliberately bounded — they choose an archetype, not a network.

The road has five stages, and each one is a gate with an owner, an artifact and an SLA. Nothing advances by email; every stage transition is a merged pull request or a pipeline run.

Stage Input Machine gate Human/owner Artifact produced SLA
1 · Intake service.yaml in the onboarding repo Schema + owner/cost-centre validation App team lead Registered service record same day
2 · Classify Tier + data-class + archetype fields Tier↔RTO/RPO consistency, data-class allow-list Architecture + Security Approved classification 1 day
3 · Land Classification + archetype IPAM allocation, no CIDR overlap Platform (automated) Vended spoke + repo scaffold < 1 hr (pipeline)
4 · Guardrail Vended spoke Policy attach, WIF, OTel, tags Platform (automated) Compliant baseline in the same run
5 · Go-live Deployed workload DR test, QoE smoke, on-call wired Service owner + SRE Production sign-off 1–5 days

The intake manifest is the entire contract. There is no portal form that emails a human; the team opens a PR against platform/onboarding/services/ with a file the pipeline can parse, validate and act on:

# services/live-playback-api.yaml — the ONLY onboarding input
apiVersion: lumina.tv/v1
kind: ServiceOnboarding
metadata:
  name: live-playback-api
  owner: team-live-delivery          # must resolve to a real Entra group
  costCentre: CC-4821
  slack: "#live-delivery-oncall"
spec:
  tier: tier-1                        # tier-0 | tier-1 | tier-2 | tier-3
  dataClass: [pii-viewer, premium]    # pci | pii-viewer | premium | internal | public
  archetype: streaming-api            # see the archetype→pattern table
  clouds: [azure, aws]                # active/active Tier-1
  regions:
    azure: [eastus2, westeurope, southeastasia]
    aws:   [us-east-1, eu-west-1, ap-southeast-1]
  dependencies: [drm-license-proxy, entitlement-engine, ciam-token-svc]
  dataResidency: gdpr                 # drives EU-only routing of PII
  estPeakRps: 900000                  # informs scaling + load-test target

Classification turns those declared fields into controls. The tier sets the RTO/RPO band and therefore the redundancy the pattern must buy; the data-class pulls the policy set and the network segment. Getting this wrong is the most expensive mistake in onboarding — a PCI service landed in the analytics segment, or a premium MovieLabs pipeline without hardware-root-of-trust controls, is a finding you pay for at audit.

Tier Workloads (Lumina) RTO / RPO Redundancy the pattern buys Change control
Tier-0 Identity, DNS, network-control, privileged access, core security ≤15 min / ≈0 Active/active multi-region, no single control plane CAB + freeze-aware
Tier-1 Playback-API, entitlement, DRM-license, origin, live-event-control, SSAI-core, CIAM-auth ≤15 min / ≤1 min Active/active both clouds, auto-failover, ≥2 AZ Change-gated, freeze-blocked
Tier-2 Business, analytics, support, ad-reporting ≤4 hr / ≤1 hr Warm standby, single-region primary Standard change
Tier-3 Dev, sandbox, experiments ≤24 hr / best-effort Single AZ, redeploy-from-code Self-service
Data-class Regime Policy set attached Segment Encryption / key control
pci PCI-DSS Cardholder-scope, no-internet-egress, flow logs customer-API (isolated) HSM-backed keys, tokenised PAN
pii-viewer GDPR + CCPA EU-routing, consent tags, DSAR-ready storage customer-API CMK, field-level for identifiers
premium MovieLabs ECP / TPN / CDSA Hardware-root, no-copy watermark, restricted egress media-processing CMK + KMS grants, HDCP enforced
internal SOC 2 Baseline deny + tag policy corporate / analytics CMK, platform-managed
public Baseline + WAF static-web / edge TLS only

The heart of the paved road is the archetype → landing-pattern map. Five archetypes cover essentially every service in the estate; each one binds to exactly one Azure pattern and one AWS pattern so that “how do we host this?” is answered before the team asks. This is the menu — there is no à la carte.

Archetype Lumina example Azure pattern AWS pattern Segment Scaling Default tier
streaming-api Playback / entitlement / token API APIM → AKS (HPA/KEDA) behind Front Door, PE to data API GW/ALB → EKS (HPA/Karpenter), CloudFront customer-API Request-driven, per-region autoscale Tier-1
stateful-service Recommendation store, session/concurrency AKS + Cosmos DB (multi-region write), Redis EKS + DynamoDB global tables, ElastiCache media-processing Partitioned, provisioned + burst Tier-1/2
batch-encode VOD transcode, package, DRM-encrypt AKS batch / Batch pool + Blob, event-triggered MediaConvert / Batch on ECS + S3, EventBridge media-processing Queue-depth driven, spot-heavy Tier-2
event-consumer QoE ingest, catalog sync, ad-reporting Functions/AKS + Event Hubs, Service Bus Lambda/ECS + Kinesis, SQS/SNS analytics Concurrency = shard/partition count Tier-2
static-web Marketing site, help centre, config JSON Static Web Apps / Blob + Front Door + WAF S3 + CloudFront + WAF static-web / edge CDN-cached, near-zero origin Tier-2/3

Landing is where the pattern becomes real infrastructure. The vending pipeline allocates a /22 spoke from the region’s supernet (Azure 10.20.0.0/12, AWS 10.40.0.0/12) with a fixed segment offset, scaffolds the app repo from the archetype template, and writes back the addresses — the team never picks a CIDR, so overlaps with on-prem 10.0.0.0/12 are structurally impossible. A trimmed Terraform vend for the Azure streaming-api pattern:

module "spoke" {
  source  = "app.terraform.io/lumina/landing-zone-spoke/azurerm"
  version = "3.6.0"                              # pinned, from the private registry

  name          = "live-playback-api"
  tier          = "tier-1"
  archetype     = "streaming-api"
  segment       = "customer-api"                 # → subnet offsets + NSG intent
  region        = "eastus2"
  address_space = module.ipam.next_free_slash22  # 10.20.x.0/22, no overlap by construction

  hub_vwan_id       = data.azurerm_virtual_wan.hub.id
  private_dns_zones = ["privatelink.vaultcore.azure.net", "privatelink.documents.azure.com"]
  policy_set        = "tier1-pii-premium"        # deny-by-default, attached NOT optional
  workload_identity = true                       # federated creds, zero static secrets
}

The AWS side is symmetrical. The same manifest drives a Control Tower Account Factory vend for the OU the archetype maps to (Workloads/Live/Prod, Workloads/VOD/NonProd, etc.), with a Service Catalog product provisioning the VPC, endpoints, IAM roles and SCP-enforced guardrails — so a streaming-api service gets an EKS/ALB spoke behind CloudFront on AWS exactly as it gets an AKS/APIM spoke on Azure, from one declaration. Active/active Tier-1 services vend in both clouds and both are wired into the go-live test.

Guardrails then attach from the pattern, not from a ticket. They are applied by the vending pipeline in the same run and are not removable by the owning team — a spoke that can reach unfiltered internet egress, or that runs on static keys, is proof the pattern was bypassed and fails the go-live gate.

Guardrail Applied by Azure AWS Failure mode it prevents
Deny-by-default policy Policy set / SCP Azure Policy tier1-pii-premium SCP + Config rules Public blob, wrong region, untagged
Identity (no static keys) Workload-identity federation Entra WIF → Key Vault IAM Roles Anywhere / IRSA Leaked long-lived credentials
Network isolation Segment + private DNS Private Endpoint + NSG VPC endpoint + SG Data-plane over the internet
Observability Auto-instrumentation OTel + Azure Monitor OTel + CloudWatch Blind spot at go-live
Cost + ownership Tag policy Required tags enforced Tag policy + budget Orphaned, unattributable spend

The go-live gate is the one stage a human cannot rubber-stamp — a Tier-1 service does not go live until it has passed a real regional failover test proving RTO ≤15 min / RPO ≤1 min, a QoE smoke test that plays a stream end-to-end through the multi-CDN, and a named on-call rota wired into the NOC.

The whole path, and the six places it silently fails, in one view:

Paved-road onboarding: a new streaming service flows through intake, tier and data-class classification, an archetype-mapped Azure or AWS landing pattern, auto-attached guardrails, and a tested go-live gate

ERP, HR, ITSM and business platforms

Behind the consumer streaming plane sits the business that funds it: SAP S/4HANA back office, Workday/SuccessFactors for HR, ServiceNow for ITSM, and the billing/payments stack under PCI scope. These are not streaming workloads and must never share the customer-API segment — they live in the corporate and dedicated SAP segments, reached over ExpressRoute/Direct Connect from corp.luminamedia.net, with their own identity plane (workforce Entra ID + Okta, never CIAM). But they are deeply integrated with streaming: content royalties, per-title content cost, rights windows and residuals all flow between SAP and the delivery platform.

Platform Function Hosting Tier Identity Integration surface
SAP S/4HANA Finance, royalties, content cost Azure IaaS (RISE optional), M-series + HANA Tier-2 Entra + SAML, SSO OData / IDoc / Event Mesh
SAP BW/4HANA Analytics, revenue reporting Azure IaaS, HANA Tier-2 Entra Batch extract → data lake
SAP Ariba Procurement (production spend) SaaS Tier-3 Okta SAML API + cXML
Workday / SuccessFactors HR, workforce, org SaaS Tier-2 Okta/Entra SCIM + SAML SCIM provisioning, SAML SSO
ServiceNow ITSM, CMDB, change, incident SaaS (ITOM) Tier-1 (ops) Okta SAML REST + Event Mgmt + CMDB
Billing / payments Subscriptions, PSP Azure, PCI segment Tier-1 Workforce + service Tokenised, PE only

SAP carries the most demanding IaaS footprint in the estate, and it is the one place where Ansible rather than Kubernetes owns the day-2 configuration. The HANA database tier runs on memory-optimised, HANA-certified VMs across availability zones, with HANA System Replication for HA and cross-region async replication East US 2 → West Europe for DR; the application tier scales horizontally behind an ASCS/ERS cluster.

SAP layer Azure shape HA DR Backup
HANA DB M-series (M128s/M208s_v2), Write Accelerator HSR sync, zone-redundant HSR async → West Europe Azure Backup for HANA (Backint), 30-day
ASCS/ERS E-series, Pacemaker cluster Zone-redundant, fencing agent Rebuild from IaC + Ansible Config in Ansible, state replicated
App servers (PAS/AAS) E-series scale set ≥2 per zone Redeploy + attach Stateless, snapshot base image
Shared files (sapmnt) Azure NetApp Files / AFS Zone-redundant volume Cross-region snapshot ANF snapshot policy

Integration is where SAP touches the streaming plane. The cadence and PII sensitivity dictate the mechanism — real-time entitlement-adjacent flows go over SAP Event Mesh or Event Hubs; heavy financial extracts go batch to the data lake; nothing exposes a public endpoint, all of it rides Private Link/PrivateLink.

Flow Source → Target Mechanism Cadence PII / sensitivity
Content royalties Streaming usage → SAP FI Event Hubs → BTP Integration Suite Hourly micro-batch Aggregated, low
Per-title content cost SAP → recommendation/finance OData pull via APIM, PE Daily Internal
Rights & windows Rights system → catalog/playout Service Bus topic, idempotent Near-real-time Internal, high-impact
Subscriber revenue Billing → SAP FI/CO IDoc over PrivateLink Daily close PCI-adjacent (tokenised)
Residuals / talent SAP HR/FI ↔ Workday SCIM + secure file (SFTP/PE) Weekly PII, high

ServiceNow is the connective tissue for operations, and it is wired in two directions. Inbound, the observability stack (next section) auto-opens incidents and change records; outbound, ServiceNow’s CMDB is populated by discovery so that every landed spoke and every vended service appears as a configuration item mapped to its owner and tier. This is what makes the event-day war room possible — a rebuffer alert resolves to a CI, an owner and a runbook, not a mystery.

ServiceNow surface Direction Feed Purpose
Event Management Inbound Dynatrace / Azure Monitor / SIEM webhooks Alert → dedup → incident
CMDB Inbound Service Graph connectors + onboarding manifest CI per service, owner, tier, dependencies
Change Management Bi-directional Pipeline change-gate ↔ CAB Prod deploys + freeze windows
Incident / Major Incident Bi-directional NOC bridge, on-call paging Event-day command record

A representative outbound wiring — the CI/CD change gate calling ServiceNow before a Tier-1 prod apply, so that a change record (and a freeze check) precedes any production mutation:

# pipeline step: create + gate on a ServiceNow normal change before prod apply
- task: ServiceNow-CreateChange@2
  inputs:
    connection: 'snow-prod'
    shortDescription: 'Deploy $(Build.Repository.Name) $(Build.BuildNumber) to prod'
    category: 'Streaming Platform'
    cmdbCi: '$(service.ciSysId)'          # resolved from CMDB by service name
    assignmentGroup: 'Live Delivery Ops'
    changeType: 'normal'
  # gate: pipeline BLOCKS if change not approved OR a live-event freeze is active

Terraform and Ansible multi-stage CI/CD

Everything above — spokes, guardrails, SAP IaaS — is delivered by one delivery system, not by hand. The design is deliberately conventional so it is boring and reliable: repo-per-module for versioned building blocks, repo-per-app for the compositions, a single YAML template project so there is one way to build, Artifacts feeds as the only trusted dependency source, and a strict dev → uat → staging → prod promotion. The patterns here are the ones documented in Reusable Terraform Modules at Scale: Repo-per-Module, Versioning & Composition and Centralized Azure Pipeline YAML Templates + Azure Artifacts Feeds; the module interface discipline is in Designing Composable Terraform Modules.

Repo type Contents Versioning Consumed by Access
Module repos (repo-per-module) One resource pattern (aks, apim, cosmos, cdn, spoke) Semver git tag → private registry App repos, pin exact version Platform writes, all read
App repos (repo-per-app) main.tf composing modules + app code Branch + build number Its own pipeline Owning team
Pipeline templates Shared build/plan/gate/apply stages Semver, extends reference Every app repo Platform writes
Policy repo OPA/Conftest + Sentinel policies Semver Gate stage Security writes
Ansible repo Roles + playbooks for IaaS day-2 Semver, tagged Apply stage (post-Terraform) Platform + SAP Basis

The YAML template project means an app repo carries almost no pipeline logic — it extends the central template and passes parameters. This is what keeps 100+ pipelines consistent; a change to the plan/gate/apply flow ships once. A repo-per-app pipeline in full:

# azure-pipelines.yml in the app repo — extends the ONE central template
resources:
  repositories:
    - repository: templates
      type: git
      name: platform/pipeline-templates
      ref: refs/tags/v4.2.0                 # pin the template version

extends:
  template: terraform/multistage.yml@templates
  parameters:
    service: live-playback-api
    tfWorkingDir: infra/
    environments:                            # dev/uat auto, staging/prod gated
      - { name: dev,     subscription: lm-lz-live-dev,     autoPromote: true }
      - { name: uat,     subscription: lm-lz-live-uat,     autoPromote: true }
      - { name: staging, subscription: lm-lz-live-stg,     approval: sre }
      - { name: prod,    subscription: lm-lz-live-prod,    approval: cab, freezeAware: true }
    ansiblePlaybook: playbooks/encoder-node.yml   # runs only where IaaS is provisioned

Inside the template, each environment runs the same shape: init (remote state per env), fmt/validate/tflint, a speculative plan posted to the PR, the policy gate on the plan JSON, a human approval for staging/prod, then apply. The promotion rules make each environment mean something specific.

Env Subscription / account Promotion Data Purpose Approver
dev lm-lz-*-dev Auto on green Synthetic Fast feedback, module wiring None
uat lm-lz-*-uat Auto on green Masked sample Integration, contract tests None
staging lm-lz-*-stg Manual Prod-mirror scale Load/soak at event-scale slice SRE
prod lm-lz-*-prod Manual + change Live Serve viewers CAB, freeze-aware

Dependencies never resolve from the public internet. Azure Artifacts (and CodeArtifact on the AWS side) front every feed — the Terraform private module registry, npm, NuGet, Maven and the base-image feed in ACR/ECR. An upstream-only dependency is both a supply-chain risk and an availability risk you cannot afford mid-event.

Feed Azure AWS Holds Upstream policy
Terraform modules Azure Artifacts / TFC registry S3 module registry / TFC Versioned modules Publish-only, no proxy
npm / NuGet / Maven Azure Artifacts CodeArtifact App dependencies Proxy + retention + scan
Container base images ACR ECR Distroless bases Curated, signed, scanned
Runner images Shared image gallery AMI / CodeBuild image Agent images Platform-built

Runners are self-hosted in the management subscription — VMSS agents for Azure DevOps (or Actions runners on AKS), CodeBuild-in-VPC on AWS — so that apply reaches private endpoints and egresses through the firewall, never over a public agent. Ansible picks up where Terraform stops: Terraform builds the VM, Ansible configures the OS. This seam matters most for the IaaS-heavy media tier — encoder/transcode nodes, playout automation, the on-prem MOC contribution agents in Los Angeles and London, and SAP.

Ansible play Target Trigger What it does
encoder-node.yml Transcode/encode VMs Post-Terraform apply SRT/Zixi tuning, GPU driver, agent, CIS hardening
playout-node.yml Linear playout servers Provision + drift check Playout config, SCTE-35 signalling, NTP/PTP
moc-contribution.yml On-prem MOC (LA/London) Scheduled + on-change Contribution encoders, monitoring agents
sap-app-tier.yml SAP PAS/AAS Provision + patch window Kernel params, SAP host agent, patch
baseline-harden.yml All IaaS Nightly CIS benchmark, EDR (CrowdStrike), patch state

A representative play — the encoder node, hardened and tuned from the same pipeline that provisioned it:

# playbooks/encoder-node.yml — Ansible owns the OS; Terraform built the VM
- hosts: encoders
  become: true
  roles:
    - role: cis-hardening            # CIS Level 1, no interactive root
    - role: crowdstrike-falcon       # EDR sensor + sensor tags = tier/segment
    - role: srt-contribution         # SRT latency, overhead-bw, passphrase from Key Vault
      vars:
        srt_latency_ms: 120
        srt_overhead_bw: 25
    - role: otel-agent               # node metrics + logs → Collector
  tasks:
    - name: Assert encoder reachable on contribution port
      ansible.builtin.wait_for: { port: 9000, timeout: 30 }

Repos, gates and promotion, in one path — the multi-stage flow and its six controls:

Terraform and Ansible CI/CD: module and app repos and YAML templates feed validate-and-plan, policy and human gates, then apply via Terraform and Ansible, promoting dev to uat to staging to prod

The branching and multi-environment mechanics generalise directly to GitHub, Bitbucket and AWS-native tooling — see The Same Enterprise CI/CD Platform on GitHub, Bitbucket & AWS and the Enterprise Branching Strategy + Multi-Stage CI/CD for the blue-green slot detail.

DevSecOps software supply chain

A pipeline that only builds is negligent at this scale; the pipeline is where security and quality are enforced, closed. Every build clears a fixed gate set before it can ever serve a viewer, and the non-negotiable rule is that a gate either fails the build or it is not a gate — an informational annotation nobody actions is a vulnerability with extra steps. The shift-left security and testing patterns here are covered in depth in Shift-Left Security, Testing, Observability — and Mobile + Database Delivery and Shift-Left Testing and Quality Gates in CI/CD.

Security gate Tool (example) Stage Blocking threshold
Secret scan gitleaks / TruffleHog Pre-merge (diff + history) Any verified secret → fail
SAST SonarQube + Semgrep On PR New High/Critical or quality gate fail
SCA Snyk / OWASP Dependency-Check On PR + nightly Reachable High/Critical CVE, copyleft licence
IaC scan checkov / tfsec / KICS On plan Public exposure, missing encryption/tags
Container scan Trivy / Wiz Post-build Fixable High/Critical in image
DAST OWASP ZAP Deployed to uat New High finding on auth/API surface
Pipeline integrity SBOM + cosign + SLSA Build + admission Unsigned or attestation missing → reject

Security gates prove the code is safe; functional gates prove it works — and for a streaming platform, “works” means “holds at 45M concurrent.” Load and soak are not optional smoke tests; a Tier-1 playback API that passes every unit test and folds at peak has failed viewers, not CI.

Functional gate Tool Stage Threshold
Unit Jest / xUnit / JUnit On PR Coverage floor, all pass
API / contract Pact / Schemathesis uat No breaking contract change
UI / E2E Playwright uat/staging Critical journeys green (login → play)
Load / soak k6 / Locust staging p95 latency + error budget at event-scale slice
Chaos Chaos Studio / FIS staging (scheduled) Survives AZ + dependency failure

The three practices that turn “we think this image is ours” into “the cluster proves it” are SBOM, signing and admission. Every image gets a CycloneDX SBOM and an SLSA provenance attestation; cosign signs it keyless using the pipeline’s OIDC identity (no long-lived key to leak); and the cluster admits only signed, attested images.

Integrity control Tool Produces / enforces Payoff
SBOM Syft → CycloneDX Component inventory per image Answer “are we exposed to CVE-X?” in minutes
Sign cosign (keyless OIDC) Image + attestation signature Provenance anchor, no key to steal
Provenance SLSA generator Build metadata attestation Prove how + where it was built
Admission Kyverno / Gatekeeper / Binary Auth Reject unsigned/unscanned The gate that makes all others real

A compact build-and-sign job showing the chain — build, SBOM, sign, verify, all failing closed:

# .github/workflows/build.yml — supply-chain integrity, keyless
permissions: { id-token: write, contents: read, packages: write }
steps:
  - uses: docker/build-push-action@v6
    with: { push: true, tags: ${{ env.IMG }}, provenance: true }   # SLSA provenance
  - name: SBOM
    run: syft ${{ env.IMG }} -o cyclonedx-json > sbom.json
  - name: Sign (keyless, OIDC  no stored key)
    run: cosign sign --yes ${{ env.IMG }}
  - name: Attest SBOM
    run: cosign attest --yes --predicate sbom.json --type cyclonedx ${{ env.IMG }}
  - name: Verify BEFORE it can deploy (fails closed)
    run: |
      cosign verify \
        --certificate-identity-regexp '^https://github.com/lumina/.+$' \
        --certificate-oidc-issuer https://token.actions.githubusercontent.com \
        ${{ env.IMG }}

Two surfaces need bespoke handling. Database schema-as-code removes the last place humans run ad-hoc SQL against prod: migrations are versioned, gated, and use expand/contract so a rollback never strands the app. Mobile and TV app distribution is a release channel CI must own end-to-end, because a bad player build reaches millions of devices and cannot be hot-fixed like a server.

DB schema-as-code Detail
Tooling Flyway / Liquibase, migrations in the app repo
Gate Migration dry-run + lint on PR; forbidden DROP without approval
Strategy Expand → migrate → contract (backward-compatible)
Rollback Down-migration tested in uat; never destructive in one step
Client target Store / channel Automation Test track
iOS / tvOS App Store Connect Fastlane + TestFlight TestFlight beta ring
Android / Android TV Play Console Fastlane + Firebase App Distribution Internal → closed → open
Roku Roku channel store packaged build + API Beta channel
Tizen / webOS Samsung / LG portals vendor CLI in pipeline Vendor test devices

The full path from commit to admission, and the six gates that must fail closed:

DevSecOps supply chain: commit with secret scanning, static SAST/SCA/IaC gates, build with cosign signing and SBOM, dynamic image scan and load test, then an admission gate that verifies signatures before prod deploy

Secret handling across every one of these stages follows the pipeline-secrets discipline in CI/CD Secrets and Credential Management — federated identity, short-lived tokens, nothing stored in a variable group.

Observability, QoE and event-day operations

Two truths make streaming observability different from any other platform. First, infra health and viewer experience are different signals — every pod can be green while video-start-time climbs past 2 s and rebuffer past 0.4%, and only a client-side QoE beacon measures the thing viewers actually feel. Second, the volume is extreme: a live final emits tens of millions of QoE events per second. The design ingests both planes, correlates them on one trace context, and feeds one pane plus the SIEM and ServiceNow. The collector patterns are those in Building Production OpenTelemetry Collector Pipelines; the SLO mechanics in SLOs and Error Budgets in Practice.

Signal source Type Pipeline Store Consumer
App SDK Traces, metrics, logs OTel SDK → Collector (tail-sample) Tempo / Mimir / Loki, Monitor Dashboards, APM
Player QoE RUM beacons (VST, rebuffer) Beacon → Event Hubs / Kinesis ADX (hot) → S3/Blob (cold) QoE dashboards, A/B
CDN + edge Access logs, synthetics Log pull + synthetic probes ADX / Athena Multi-CDN steering, hit-ratio
Infra Metrics, k8s events OTel / Prometheus Mimir / Monitor SRE dashboards, autoscale
APM Deep transaction traces Dynatrace OneAgent Dynatrace Root-cause, service flow

The QoE SLIs are the contract with the audience. They are watched directly, with burn-rate alerting, and they lead every incident — an alarm on infra is a lagging indicator next to a concurrency cliff.

SLI Target Source Alert trigger
Video-start-time (VST) p95 < 2 s Player beacon p95 > 2 s sustained 5 min
Rebuffer ratio < 0.4% Player beacon > 0.4% by region/CDN
Live latency (LL-CMAF) < 8 s Player + origin > 8 s glass-to-glass
Playback-failure < 0.5% Player beacon Multi-window burn-rate
Availability (Tier-1) 99.99% Synthetic + API Fast + slow burn

A trimmed OTel Collector config for the media pipeline — tail sampling keeps the slow and errored traces and drops the boring majority, and one trace context propagates edge → API → DRM → origin so a rebuffer complaint resolves to a hop:

# otel-collector: keep what matters, survive the spike
receivers:
  otlp: { protocols: { grpc: {}, http: {} } }
processors:
  tail_sampling:
    decision_wait: 10s
    policies:
      - { name: errors, type: status_code, status_code: { status_codes: [ERROR] } }
      - { name: slow, type: latency, latency: { threshold_ms: 800 } }
      - { name: baseline, type: probabilistic, probabilistic: { sampling_percentage: 1 } }
  batch: { send_batch_size: 8192 }
exporters:
  otlphttp/tempo: { endpoint: https://tempo.lumina.internal }
  prometheusremotewrite/mimir: { endpoint: https://mimir.lumina.internal/api/v1/push }
service:
  pipelines:
    traces:  { receivers: [otlp], processors: [tail_sampling, batch], exporters: [otlphttp/tempo] }
    metrics: { receivers: [otlp], processors: [batch], exporters: [prometheusremotewrite/mimir] }

Dashboards are role-specific; the concurrency dashboard is the heartbeat the war room watches — a cliff in viewers-now with flat infra is a delivery or DRM failure the audience already feels.

Dashboard Audience Key panels Refresh
Concurrency War room / exec Viewers now by title/region/CDN 10 s
QoE Delivery / SRE VST, rebuffer, failure by CDN/device 30 s
Multi-CDN steering Delivery Hit-ratio, RTT, cost, steer split 1 min
Origin / encode Media ops Origin health, package errors, SCTE-35 30 s
DRM / entitlement Security / delivery License req/s, denials, concurrency fraud 30 s

CDN and edge monitoring close the loop between observation and action. The multi-CDN steering decision — which of CDN-A/B/C a player is sent to — is not static; it is driven by the same telemetry, blending real-user QoE (measured rebuffer and RTT per CDN per region) with synthetic probes and per-GB cost. When RUM shows one CDN’s rebuffer climbing in eu-west, the steering service shifts that region’s split before the war room has to intervene, and the change is visible on the steering dashboard within a minute. This is the difference between a monitored outage and a mitigated one: the observability plane does not just alert, it feeds the control plane that reroutes viewers.

The alert that leads the incident is a multi-window burn-rate on the playback-failure SLO — fast burn pages immediately, slow burn opens a ticket:

# playback-failure SLO = 99.5%; page on fast burn (1h & 5m both hot)
(
  sum(rate(playback_requests_total{result="fail"}[5m]))
    / sum(rate(playback_requests_total[5m])) > (14.4 * 0.005)
)
and
(
  sum(rate(playback_requests_total{result="fail"}[1h]))
    / sum(rate(playback_requests_total[1h])) > (14.4 * 0.005)
)

For a premium live final, none of this is watched ad-hoc — Lumina runs a pre-staffed NOC / war room with a defined operating model. The mistake teams make is assembling the bridge at kickoff; by then it is too late. Roles are named, the timeline is rehearsed, and a change freeze is in force.

War-room role Responsibility Escalation
Incident Commander Owns the call, single decision-maker VP Engineering
Comms lead Status page, exec + partner updates Comms / PR
Delivery / CDN ops Multi-CDN steer, origin shield, cache CDN TAMs on standby
DRM / entitlement ops License service, concurrency, fraud Security on-call
Platform / API ops Playback API, autoscale, CIAM Cloud on-call
Scribe Timeline, decisions, actions → ServiceNow
Phase Window Actions Owner
Prepare T-72h → T-24h Capacity pre-scale, cache warm, CDN commit, freeze on Platform + Delivery
Rehearse T-24h → T-2h Failover drill, runbook walk-through, dashboards up IC + all ops
Standby T-2h → T-0 War room live, ingest pre-warmed, contribution dual-path verified All
Live T-0 → end Watch concurrency + QoE, execute runbooks, hold freeze IC drives
Post +0 → +72h Scale down, blameless review, action items SRE + IC

The rehearsed live runbooks turn a viewer-visible symptom into a first action in seconds, not a debate:

Symptom Leading signal First action Escalate if
Rebuffer spike QoE rebuffer > 0.4% one CDN Steer traffic off the CDN (client SDK + DNS) Multi-CDN all degraded
CDN brownout Hit-ratio drop, RTT climb Shift to shield origin + alternate CDN Origin CPU/egress saturating
DRM license surge/fail License denials climb Scale license service, check key/cert expiry Entitlement store latency
Origin failover Origin health red Promote standby origin region, verify manifests Both regions impacted
Concurrency ceiling Autoscale at max, latency up Pre-approved burst capacity, shed non-Tier-1 CIAM token service throttling

The whole loop — signals in, correlated in the pipeline, out to dashboards, SIEM/SOC and ServiceNow, and up into the event-day NOC:

Observability and QoE: OpenTelemetry app traces, player RUM beacons and CDN signals flow through an OTel collector, QoE ingest and APM into hot and cold stores, feeding dashboards, SIEM and ServiceNow, and the event-day war room

The on-call and paging discipline behind the NOC — routing, escalation and actionable runbooks — is the practice described in Building an On-Call Practice: PagerDuty Escalation, Alert Routing, and Actionable Runbooks, extended here with a pre-staffed command model for premium live.

Cost model and TCO

A streaming platform is not a cost structure you can reason about by counting servers. Lumina Media’s bill is dominated by bytes leaving the edge, not by compute running in a region. At 120M subscribers, a peak of 45M concurrent streams during a live final, and a blended average bitrate of roughly 3.2 Mbps, the platform delivers on the order of 12,000 PB (12 EB) per month — and every one of those bytes has a per-GB price at the CDN. That single fact reorders the entire TCO: egress/CDN delivery, encoding, and storage together are ~79% of run-rate, while the app platform that everyone spends design time on is a distant fourth. The cost-engineering job is therefore to make each delivered gigabyte cheaper — steering, per-title encoding, cache-hit-ratio — and to keep the 400 PB archive off expensive tiers, not to right-size a node pool.

The cost model has ten domains. The table names each, the unit that drives it, and the lever that moves it — read the “lever” column as the FinOps backlog:

Cost domain Primary driver Unit economics Dominant lever
Multi-CDN delivery Delivered GB × per-GB rate ~12,000 PB/mo × blended $/GB Steering to cheapest-that-meets-QoE; committed volume
Cloud origin egress Origin-fill GB (1 − cache-hit-ratio) 2% of delivery via origin shield Cache-hit-ratio; origin shield; private interconnect
Per-tier compute Concurrency × service fan-out AKS/EKS + API + DB, 6 regions A/A Savings Plans / reservations; autoscale to concurrency
Encoding / transcode New content-hours + live channels Live encode fleet + VOD per-title Per-title encoding; spot for VOD; encode-once
Storage (400 PB) Library size × tier price Hot/Cool/Archive lifecycle Aggressive tiering; single-copy archive
CIAM Monthly-active identities B2C/Cognito MAU + token service Refresh-token TTL; cache token validation
Analytics / QoE ingest Heartbeat events/sec Event Hubs/Kinesis + hot/cold stores Sampling cold path; TTL on hot store
Networking ExpressRoute/DX + inter-region GB Circuits + cross-region replication Region affinity; compress replication
Security tooling Log-ingest GB + endpoints Sentinel/GuardDuty ingest + HSM ops Ingestion tiering; archive cold logs
Licensing / SaaS Seats + volume tiers Wiz/Falcon/Dynatrace/QoE/DRM/ADS Consolidate tools; negotiate at renewal

The delivery model, worked. Delivery is where the money is, so it gets a real per-GB calculation rather than a round number. Lumina runs three commercial CDNs with a client-side selection SDK plus DNS steering; the steering policy routes each request to the cheapest CDN that still meets the QoE floor (VST, rebuffer, throughput), spilling to the pricier performance CDN only when a POP is degraded or during a launch spike. The blended effective rate lands near $0.00228/GB — far below retail because volume is committed. Origin fill is held to ~2% of delivered bytes by origin shield and per-asset cache keys, so cloud egress is a rounding error next to CDN spend:

Delivery path Share of 12,000 PB Rate ($/GB) Monthly GB Monthly USD
CDN-A (volume commit, cacheable segments) 60% $0.0018 7.20 B $12.96M
CDN-B (performance / second source) 30% $0.0025 3.60 B $9.00M
CDN-C (premium geos / overflow) 10% $0.0045 1.20 B $5.40M
Blended CDN delivery 100% $0.00228 12.00 B $27.36M
Cloud origin egress (2% fill @ private rate) $0.015 0.24 B $3.60M
Total delivery $30.96M

The full run-rate. Rolling delivery together with the other nine domains gives the steady-state monthly bill. USD is the negotiating currency; INR is shown at ₹86/USD for the finance plan (Lumina reports in both). The right-hand column is the share of total — the point being that content (deliver + encode + store) is four-fifths of the bill:

Domain USD / month INR / month Share
Multi-CDN delivery $27.36M ₹235.3 Cr 61.6%
Cloud origin egress $3.60M ₹31.0 Cr 8.1%
Per-tier compute (platform, 6 regions A/A) $4.50M ₹38.7 Cr 10.1%
Encoding / transcode (live + VOD per-title) $2.20M ₹18.9 Cr 5.0%
Storage (400 PB tiered + working + replication) $1.80M ₹15.5 Cr 4.1%
Analytics / QoE event ingest + stores $1.20M ₹10.3 Cr 2.7%
Licensing / SaaS (Wiz·Falcon·Dynatrace·QoE·DRM·ADS) $1.00M ₹8.6 Cr 2.3%
Networking (ER/DX·VWAN/TGW·PE·inter-region) $0.90M ₹7.7 Cr 2.0%
Platform / management (backup·DR reserve·non-prod) $0.70M ₹6.0 Cr 1.6%
CIAM (B2C/Cognito MAU + token service) $0.60M ₹5.2 Cr 1.4%
Security tooling (Sentinel/GuardDuty ingest·HSM) $0.50M ₹4.3 Cr 1.1%
Total run-rate $44.36M ₹381.5 Cr 100%
Annualised $532.3M ₹4,578 Cr
Per subscriber (infra only) $0.37/sub/mo ₹31.8/sub/mo

At $0.37/subscriber/month of pure infrastructure, delivery is a single-digit percentage of ARPU — which is the number that makes the whole D2C model work, and the number a single-CDN retail contract would quietly destroy.

Storage tiering — why 400 PB does not cost a fortune. If the entire archive sat on hot blob/S3-Standard it would be $7.2M/month. Lifecycle rules push the long tail down two tiers, and the effective storage bill is a quarter of that. The lifecycle policy is a few lines of JSON applied at the container level and left to run:

Tier Share of 400 PB PB Rate ($/GB/mo) Monthly USD
Hot (origin-active catalog, live windows) 5% 20 $0.018 $0.360M
Cool (mid-tail, recent seasons) 15% 60 $0.010 $0.600M
Archive (deep library, masters, compliance) 80% 320 $0.00099 $0.317M
Working / mezzanine + multi-region hot copy $0.523M
Total storage 100% 400 $1.80M
// Azure Storage lifecycle — same shape as an S3 lifecycle rule on the AWS origin
{ "rules": [{
  "name": "vod-library-tiering", "enabled": true,
  "type": "Lifecycle",
  "definition": {
    "filters": { "blobTypes": ["blockBlob"], "prefixMatch": ["vod/masters/", "vod/mezzanine/"] },
    "actions": { "baseBlob": {
      "tierToCool":    { "daysAfterLastAccessTimeGreaterThan": 30 },
      "tierToArchive": { "daysAfterLastAccessTimeGreaterThan": 120 },
      "enableAutoTierToHotFromCool": true
    } } } }] }

Cost-control levers, quantified. The un-optimised version of this platform — retail CDN pricing, all-hot storage, on-demand compute, naive caching — runs near $96.8M/month. The optimised run-rate is $44.4M. The gap is the FinOps programme, and every lever is measurable:

Lever Mechanism Baseline Optimised Monthly saving
Committed / volume CDN pricing 12-EB commit vs retail $0.006/GB $72.0M $27.4M $44.6M
Cache-hit-ratio + origin shield Origin fill 10% → 2% (per-asset keys) $18.0M $3.6M $14.4M
Storage lifecycle tiering 400 PB hot → hot/cool/archive $7.2M $1.8M $5.4M
Per-title / per-scene encoding −20% delivered bitrate → −20% volume +$5.5M $5.5M
Savings Plans / Reserved compute 1–3 yr commit on Tier-0/1 baseline $6.9M $4.5M $2.4M
Multi-CDN steering (cheapest-meets-QoE) Shift ~700 PB from CDN-C/B → CDN-A +$1.6M $1.6M
Programme total $96.8M $44.4M ≈ $52.4M (54%)

Three-year TCO. The commitment posture changes across the programme: Year 1 is build-heavy and mostly on-demand; Years 2–3 lock in Savings Plans and the full CDN commit. Delivery grows with subscriber and viewing-hour growth (~12% YoY assumed), partly offset by deeper per-title encoding and steering maturity:

Fiscal year Run-rate profile Compute posture Annual USD Annual INR
Year 1 (build + ramp) 8 mo partial + on-demand On-demand + short SP $486M ₹4,180 Cr
Year 2 (steady + commit) Full run-rate, committed CDN 1-yr Savings Plans $532M ₹4,578 Cr
Year 3 (scale + optimise) +12% volume, deeper encoding 3-yr SP + reserved DB $561M ₹4,825 Cr
3-year TCO $1,579M ₹13,583 Cr
# Lock baseline Tier-0/1 compute (steady, always-on) onto commitments
az billing benefits savings-plan-order purchase \
  --sku "Compute_Savings_Plan" --term P3Y --billing-plan Monthly \
  --commitment-amount 55000 --commitment-currency USD
aws savingsplans create-savings-plan \
  --savings-plan-offering-id <compute-3yr> --commitment "55000" --upfront-payment-amount "0"

Bill of materials

The bill of materials is the design made purchasable — every plane mapped to the exact service, SKU and quantity a platform team can order and stand up. It spans four supply lines: Azure, AWS, the two on-prem broadcast facilities, and the third-party CDN/DRM/QoE/security tooling no cloud provides at the quality premium content demands. Azure first — the workforce-identity and one CIAM/edge-API home:

Azure service SKU / tier Qty / scale Purpose Tier
Management Groups + Subscriptions EA lm root + ~24 subs Governance scaffold (lm-plat-*, lm-lz-*) T0
Entra ID P2 ~9,000 workforce Workforce SSO, CA, PIM T0
Azure AD B2C Go-tiered ~60M MAU (region split) CIAM plane (social + email, JWT) T1
ExpressRoute 10 Gbps × region 3 circuits On-prem ↔ Azure private path T0
Virtual WAN + Azure Firewall Premium 3 hubs Hub-spoke, IDPS, egress control T0
Front Door + WAF Premium 1 global Edge WAF/bot for API + DRM license T1
API Management Premium, multi-region 3 regions Playback / entitlement / token APIs T1
AKS Standard, GPU + CPU pools 3 regions A/A Playback, entitlement, SSAI, encode T1
Azure Functions Elastic Premium per region Event glue, manifest personalisation T2
Cosmos DB Multi-region write 3 regions Entitlement / session / concurrency T1
Blob + ADLS Gen2 Hot/Cool/Archive ~200 PB (Azure share) VOD masters, mezzanine, analytics lake T1–T2
Event Hubs Dedicated 3 regions QoE telemetry ingest T2
Key Vault + Managed HSM Managed HSM FIPS 140-2 L3 DRM keys, token signing T0
Microsoft Sentinel + Defender estate-wide SIEM + CSPM/CWP T0
Recovery Services Vaults GRS per region Backup / DR T1

The AWS supply line carries the media-processing heavy lifting (AWS Elemental), the second CIAM plane, and CloudFront as one of the three CDNs:

AWS service SKU / tier Qty / scale Purpose Tier
Organizations + Control Tower Root→OU tree Multi-account guardrails T0
IAM Identity Center workforce fed. AWS access via Entra/Okta T0
Amazon Cognito ~60M MAU CIAM plane (second cloud) T1
Route 53 + CloudFront + Shield Adv global DNS steering, CDN-C, DDoS T1
Transit Gateway + Direct Connect 10 Gbps 3 regions Backbone + on-prem private path T0
EKS + Fargate GPU + Graviton 3 regions A/A Streaming platform, reco inference T1
Elemental MediaLive HD/UHD channels ~200 live Live encode (ABR ladder) T1
Elemental MediaConvert on-demand VOD farm Transcode-once, per-title T2
Elemental MediaPackage LL-CMAF 3 regions JIT packaging, origin, DVR T1
Elemental MediaConnect SRT/Zixi/RIST dual feeds Contribution transport T1
Elemental MediaTailor live + VOD SSAI ad insertion T1
SPEKE key provider Multi-DRM key exchange T1
S3 (Std/IA/Glacier/Deep Archive) tiered ~200 PB (AWS share) Origin + archive T1–T2
DynamoDB (Global Tables) on-demand 3 regions Entitlement / session (2nd plane) T1
Kinesis Data Streams + Firehose 3 regions QoE telemetry (2nd plane) T2
KMS + CloudHSM FIPS L3 Key management T0

On-prem and third-party close the BoM. The two Media Operations Centres carry contribution, playout and near-line archive; the SaaS/tooling line carries everything the clouds do not — multi-CDN, multi-DRM license servers, QoE analytics, and the content-security stack that TPN/MovieLabs demands:

Supply line Item Scale / spec Purpose
On-prem (LA + London MOC) Contribution encoders SRT/RIST/Zixi, dual-path Live signal → cloud
On-prem Playout automation + master control SMPTE 2110 / SDI Linear channels, blackout
On-prem Near-line archive LTO + object, PB-scale Masters, DR copy
On-prem AD DS corp.luminamedia.net + SAP HA pair + ECC/S4 Workforce root, back office
Third-party CDN-A / CDN-B / CDN-C 3 commercial CDNs Multi-CDN delivery
Third-party CDN steering SDK client + DNS Cheapest-meets-QoE routing
Third-party Multi-DRM license service Widevine + FairPlay + PlayReady CENC license issuance
Third-party Forensic watermarking A/B server-side + session MovieLabs / TPN leak trace
Third-party QoE analytics (Conviva-class) RUM at 45M concurrent VST/rebuffer/steering data
Third-party Wiz + CrowdStrike Falcon CSPM + EDR/CWP Posture + workload protection
Third-party Dynatrace + ServiceNow APM + ITSM Observability + change/incident
Third-party Ad-decision server / SSP VAST/VMAP Ad selection for SSAI

Operating model and RACI

The platform is run by a thin set of platform-owning teams that vend paved-road landing zones, and workload-owning streaming/live/data/ad teams that consume them — the same split that lets a large estate move without a central bottleneck. The Architecture Review Board (ARB) owns standards and exceptions but does not gate day-to-day deploys; policy-as-code does that continuously. Nine functions own the estate:

Function Owns Vends / consumes Primary planes
Platform Engineering Landing zones, IaC, golden paths Vends Governance, connectivity, CI/CD
Security & Compliance Guardrails, SIEM, IR, TPN/PCI/GDPR Vends Identity floor, CSPM/CWP, audit
Network & CDN Engineering Backbone, edge, multi-CDN steering Vends Connectivity, delivery
Streaming Platform Encode/package/origin/DRM/playback Consumes Media-processing, customer-API
Live Operations / Broadcast MOC, contribution, playout, events Consumes Live path, event control
Data & Personalization Analytics, QoE, reco, A/B, DSAR Consumes Analytics, consent
Ad-Tech SSAI, ADS, VAST, ad reporting Consumes Ad path (separated)
Cloud Operations / SRE Run, on-call, capacity, FinOps Both All (run-state)
Architecture Review Board ADRs, standards, exceptions Governs Cross-cutting

The RACI makes the ownership real for the activities that actually cause 2 a.m. pages and audit findings. R = does the work, A = single accountable owner, C = consulted, I = informed:

Activity Plat Sec Net/CDN Stream Live Data Ad SRE ARB
Landing-zone / IaC guardrail change A/R C C I I I I C C
Workforce identity (Entra/Okta/PIM) C A/R I I I I I C I
CIAM (B2C/Cognito) config + scale C A C R I C I C I
Multi-CDN steering policy I C A/R C C C I C I
Encode / packaging pipeline change I I C A/R C I I C I
DRM / key management + rotation I C I A/R I I I C C
Live event runbook + execution I C C C A/R I C C I
QoE / analytics pipeline I C C C I A/R C C I
SSAI / ad configuration I C I C C C A/R C I
Sev1 incident response C C C C C C C A/R I
Tier-1 DR failover test C C C C C I I A/R C
FinOps / cost optimisation C I C C C C C A/R I
Compliance audit (PCI/TPN/GDPR) C A/R C C C C C C C
ADR approval / exception grant C C C C C C C C A/R

Two operating regimes bind the model together. The severity/escalation ladder decides who wakes up and how fast; the change-freeze regime protects live events — the single largest reputational risk, because a failed final is on every screen at once:

Regime Trigger Response Owner
Sev1 (Tier-1 viewer-facing outage) Playback-failure > 0.5% or region down Bridge < 5 min, active-active shift, exec comms SRE
Sev2 (degraded QoE, single POP/CDN) Rebuffer > 0.4% or VST p95 > 2s in a geo Steer away from POP/CDN, page owning team Net/CDN
Sev3 (non-viewer, business) Ad-reporting / back-office fault Next-business-day, ticket Owning team
Event freeze 48h before → 6h after a Tier-1 live event No prod change except approved break-glass Live Ops + ARB
Peak freeze Launch tent-pole / seasonal peak Change-advisory board approval only SRE + ARB

Migration and onboarding waves

Delivery is sequenced into six waves over 24 months, each with a hard entry gate (what must be true to start) and a hard exit gate (the acceptance event that unlocks the next wave). Nothing lands on an un-baselined landing zone, nothing goes live without DRM, and no live event runs until it has been proven at concurrency. The waves and their gates:

Wave Window Scope Entry gate Exit gate
W1 Foundation M0–M4 MG/OU tree, IPAM, VWAN/TGW/ER/DX, core security, CI/CD, policy Funding + region selection LZ conformance ≥ 98%, connectivity reachable, security baseline green
W2 Identity + CIAM M4–M9 Entra+Okta workforce, AD DS, B2C/Cognito, PE segmentation W1 exit Workforce SSO cutover; CIAM load-proven to 120M / 45M-concurrent auth
W3 Streaming core (VOD) M9–M14 Encode-once, JIT package, origin, playback/entitlement API, tiered storage W2 exit First title live; VST p95 < 2s + rebuffer < 0.4% on pilot; catalog sync
W4 DRM + CDN + Live M14–M19 CENC multi-DRM, SPEKE, signed edge auth, multi-CDN, contribution, LL-CMAF, SCTE-35 W3 exit DRM cutover; multi-CDN steering live (≥98% offload); live event proven
W5 Data + Ad-Tech M19–M22 QoE at scale, reco/personalisation, A/B, SSAI/VAST/VMAP, consent/DSAR W4 exit SSAI in prod (live+VOD); QoE dashboards; personalisation live; DSAR working
W6 Run + Optimise M22–M24 Active-active DR proof, FinOps, audits, legacy decommission W5 exit Tier-1 failover < 15m proven; PCI/TPN/GDPR passed; cost targets met; legacy off

Each wave carries specific workloads at a defined resilience tier, which is what makes the RTO/RPO commitments testable rather than aspirational:

Wave Key workloads onboarded Tier RTO / RPO
W1 Identity floor, network control, security tooling T0 ≤ 15m / ≈ 0
W2 Entitlement, CIAM auth, token service T0–T1 ≤ 15m / ≤ 1m
W3 Playback API, origin, VOD packaging T1 ≤ 15m / ≤ 1m
W4 DRM license, live-event control, SSAI-core, origin shield T1 ≤ 15m / ≤ 1m
W5 QoE analytics, reco, ad-reporting T2 ≤ 4h / ≤ 1h
W6 Optimisation, sandbox, decommission T3 ≤ 24h / best-effort

Architecture decision records

Every pivotal choice in this design is captured as an ADR so a future engineer can see not just what was decided but why, and what was rejected. The index lists the accepted decisions; the pivotal three carry a scored options table underneath. All are Accepted unless noted:

ADR Decision Status Drives
ADR-001 Workforce identity = Entra ID + Okta federation, on-prem AD DS as source Accepted SSO, CA, PIM; kept separate from CIAM
ADR-002 CIAM = Azure AD B2C and Amazon Cognito (per-cloud), not a single IdP Accepted 120M-scale token service, no single point of failure
ADR-003 Multi-CDN (three CDNs + steering), not single CDN Accepted Delivery resilience + cost steering + geo coverage
ADR-004 CENC multi-DRM (Widevine + FairPlay + PlayReady, cenc + cbcs) Accepted Device coverage under one packaging + key flow
ADR-005 Active-active Tier-1 across regions, not active-passive Accepted Viewers see no region outage; RPO ≤ 1m
ADR-006 SSAI (server-side ad insertion), not client-side (CSAI) Accepted Ad-block resilience, seamless on TV/STB
ADR-007 Event-day change-freeze regime around live finals Accepted Protect the highest-blast-radius moments
ADR-008 Archive tiering (hot/cool/archive lifecycle) for 400 PB Accepted ~$5.4M/mo saving vs all-hot
ADR-009 Encode-once + JIT packaging, not pre-packaged per-format Accepted Storage/agility over per-request compute
ADR-010 LL-CMAF for live low-latency, not LL-HLS-only or WebRTC Accepted < 8s glass-to-glass at broadcast scale

ADR-002 — CIAM platform (dual, per-cloud). Context: 120M identities, 45M-concurrent auth at a live final, and a hard requirement never to co-mingle consumer and workforce identity. Decision: run B2C and Cognito each as the CIAM authority in its own cloud, with a shared token contract, rather than forcing one IdP to front both clouds. Consequence: no single-vendor blast radius, per-region token issuance, at the cost of dual operational surface and a token-format reconciliation layer.

Option 120M scale Multi-cloud resilience Lock-in Verdict
Single B2C (or single Cognito) OK Single-cloud dependency High Rejected
Third-party CIAM SaaS only OK Vendor-dependent High Rejected
B2C + Cognito, per-cloud OK No single point Low Accepted

ADR-003 — Multi-CDN vs single. Context: one CDN is one outage and one price. Decision: three CDNs with RUM/synthetic-driven steering to the cheapest that meets the QoE floor, origin shield in front of cloud origin. Consequence: +$44.6M/mo saved vs retail single-CDN, delivery survives a whole-CDN failure, at the cost of steering-SDK complexity and per-CDN cache-policy management.

Option Resilience Cost control Complexity Verdict
Single premium CDN One-outage risk Retail rates Low Rejected
Two CDNs, failover only Better Some Medium Considered
Three CDNs + active steering Best Best ($/GB) High Accepted

ADR-005 — Active-active Tier-1. Context: a viewer must not see an outage when a region fails during a final. Decision: Tier-1 (entitlement, playback API, DRM license, origin, live-event control, CIAM auth) runs active-active across regions with global data replication (Cosmos multi-write / DynamoDB Global Tables), RPO ≤ 1m. Consequence: double the steady-state footprint and cross-region write reconciliation, bought in exchange for RTO ≤ 15m with no cold-start and no viewer-visible failover.

Option RTO RPO Run cost Verdict
Active-passive (warm standby) Minutes, visible ≤ 5m Lower Rejected for T1
Pilot-light Tens of min Higher Lowest Rejected for T1
Active-active ≤ 15m, invisible ≤ 1m Higher Accepted (T1)

Risks, assumptions, issues and dependencies

The RAID register is the honest ledger of what could derail delivery and what is being done about it. Risks are pre-mitigated with a named owner; assumptions are the load-bearing beliefs the plan rests on; issues are live problems; dependencies are the external gates. The top risks first, scored L(ikelihood)×I(mpact):

# Risk L I Mitigation Owner
R1 Live-final concurrency exceeds tested ceiling (>45M) M H Pre-scale + game-day load test to 1.5× peak; multi-CDN overflow to CDN-C SRE
R2 A whole CDN degrades during a Tier-1 event M H Three-CDN steering, real-time RUM, instant weight shift Net/CDN
R3 DRM key/license service outage blocks playback L H Multi-region SPEKE + HSM, license caching, break-glass Streaming
R4 CIAM credential-stuffing / token abuse at scale H M Bot defence, rate-limit, anomaly detection, concurrency caps Security
R5 Egress spend overruns as viewing hours grow H M Per-title encoding, CHR targets, committed pricing, budget alerts SRE/FinOps
R6 Content leak fails TPN/MovieLabs audit L H Forensic watermarking, hardware DRM (HDCP), TPN controls Security
R7 Region failover misses RTO under real load M H Quarterly active-active DR game-days with viewer-traffic replay SRE

The assumptions, issues and dependencies that condition the plan — each is tracked because a wrong assumption is a schedule slip waiting to happen:

Type Item Detail Status / owner
Assumption Blended bitrate ~3.2 Mbps, ~12,000 PB/mo delivered Drives the entire CDN cost model Validate in W3 pilot / SRE
Assumption CDN volume commit yields ~$0.00228/GB blended Contract-dependent Procurement
Assumption 98% origin offload holds via shield + cache keys Governs origin egress line Net/CDN
Issue Azure Media Services retired — no PaaS encoder on Azure Encoding standardised on AWS Elemental + on-prem Open / Streaming
Issue Token-format reconciliation between B2C and Cognito Needs shared claims contract Open / Security
Dependency ExpressRoute / Direct Connect circuit delivery Gates W1 exit Carrier / Net
Dependency Widevine + PlayReady + FairPlay license agreements Gates W4 DRM cutover Legal / Streaming
Dependency TPN site + cloud assessment Gates premium-content go-live Security / TPN
Dependency Multi-CDN + steering-SDK contracts Gates W4 delivery Procurement

Delivery roadmap and acceptance

The roadmap reads left to right as six waves over 24 months, each unlocked only by the prior wave’s exit gate. The diagram below is the one-page view every stakeholder shares: foundation and connectivity, then the two identity planes, the streaming core, the go-live wave that adds DRM, multi-CDN and live, and finally the data/ad and run/optimise waves that turn the platform into a steady, audited, cost-tuned operation.

The wave path with its milestone gates, left to right:

Media landing-zone delivery roadmap: six waves across 24 months — foundation and connectivity, identity and CIAM, streaming core, DRM plus multi-CDN plus live, then data/ad-tech and run/optimise — with milestone badges at each exit gate.

Milestone Month Wave Acceptance event
M1 LZ signed off M4 W1 Conformance ≥ 98%, connectivity + security baseline green
M2 CIAM proven M9 W2 Token service load-passed 120M / 45M-concurrent; SSO cutover
M3 First title M14 W3 VOD pilot meets VST/rebuffer; catalog sync live
M4 DRM + CDN M19 W4 CENC cutover; three-CDN steering; ≥98% offload
M5 Live proven M19 W4 LL-CMAF < 8s; SCTE-35 + SSAI; freeze rehearsed
M6 Run-state M24 W6 DR < 15m; PCI/TPN/GDPR passed; FinOps targets met

Acceptance is defined against the proposal’s objectives, so “done” is measurable, not a matter of opinion. Each objective maps to a concrete test, the wave that proves it, and the evidence produced:

# Objective Acceptance criterion (measurable) Wave Evidence
O1 Viewers see no region outage Tier-1 active-active failover RTO ≤ 15m, RPO ≤ 1m, no viewer-visible drop W6 DR game-day report
O2 Meet QoE targets VST p95 < 2s · rebuffer < 0.4% · live latency < 8s · playback-fail < 0.5% W3–W4 QoE dashboard export
O3 Scale to 120M / 45M-concurrent Load test sustains 1.5× peak; CIAM issues tokens at peak W2–W4 Load-test evidence
O4 Protect premium content CENC multi-DRM + HDCP + forensic watermark; TPN/MovieLabs passed W4 TPN assessment
O5 Separate identity + comply Workforce ≠ consumer plane; PCI-DSS + GDPR + CCPA controls evidenced W2, W6 Audit reports
O6 Multi-cloud, no single dependency Survive loss of one cloud region and one whole CDN with no outage W4, W6 Chaos/DR test
O7 Monetise SVOD/AVOD/TVOD/FAST SSAI live for linear + VOD; VAST/VMAP + ad-reporting reconciled W5 Ad-ops report
O8 Cost efficiency Blended ≤ $0.00228/GB; infra ≤ $0.37/sub/mo; Savings Plans applied W6 FinOps statement
O9 Low-latency live with redundancy Dual SRT/Zixi contribution; LL-CMAF < 8s at concurrency W4 Live event report
O10 Consent-compliant analytics QoE at 45M-concurrent; DSAR + consent honoured end-to-end W5 DSAR test log

When all ten acceptance rows are green — and only then — the programme moves from delivery to steady-state run, and the operating model, RACI and FinOps levers above become the discipline that keeps 120M subscribers watching without ever seeing the machinery behind the glass.

Media & StreamingLanding ZoneMulti-CloudAzureAWSDRMMulti-CDNZero Trust
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading