Documentation Index
Fetch the complete documentation index at: https://docs.blindcast.dev/llms.txt
Use this file to discover all available pages before exploring further.
Encrypted HLS segments should be served from a CDN. The CDN cannot decrypt the content (zero-knowledge) — it caches and delivers opaque bytes, just like any other static asset.
Architecture
The player fetches segments via fetch() from a different origin than your app. The CDN must return CORS headers.
CloudFront
Create a response headers policy:
{
"CorsConfig": {
"AccessControlAllowOrigins": {
"Items": ["https://app.example.com"]
},
"AccessControlAllowMethods": {
"Items": ["GET", "HEAD"]
},
"AccessControlAllowHeaders": {
"Items": ["*"]
},
"AccessControlExposeHeaders": {
"Items": ["Content-Length", "Content-Range"]
},
"OriginOverride": true
}
}
Attach this policy to your CloudFront distribution’s behavior for the segment path pattern (/content/*).
Cloudflare
Add a Transform Rule or Worker to set CORS headers:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, HEAD
Access-Control-Expose-Headers: Content-Length, Content-Range
Or use Cloudflare’s CORS settings in the dashboard under your zone’s security settings.
Cache policy
| Asset | Cache behavior | Why |
|---|
.ts segments | Cache forever (max-age=31536000, immutable) | Segments are content-addressed and never change |
.m3u8 manifests | Short TTL or no-cache (max-age=0, must-revalidate) | Manifests may be updated (e.g., live streams, key rotation) |
.key files | Do not cache | Key requests go directly to the key server, not the CDN |
CloudFront cache policy
Create a cache policy for segments:
{
"MinTTL": 86400,
"MaxTTL": 31536000,
"DefaultTTL": 31536000
}
For manifests, use a separate behavior with MinTTL: 0 and forward the Cache-Control header from the origin.
Set cache headers when uploading segments:
# The CLI sets these automatically during upload
# If uploading manually:
aws s3 cp seg-0.ts s3://bucket/content/vid-001/seg-0.ts \
--cache-control "max-age=31536000, immutable" \
--content-type "video/mp2t"
S3 as origin
CloudFront + S3
- Create an S3 bucket (private, no public access)
- Create a CloudFront distribution with the S3 bucket as origin
- Use Origin Access Control (OAC) to let CloudFront read from S3 without making the bucket public
- Set the origin path to match your upload prefix (e.g.,
/content)
Cloudflare + R2
R2 buckets can be connected as custom domains in Cloudflare:
- Create an R2 bucket
- Connect a custom domain (e.g.,
cdn.example.com)
- R2 serves objects directly with Cloudflare’s CDN
Security
The CDN sees only encrypted bytes. Even if CDN cache is compromised, the attacker gets ciphertext without the decryption key. The key server is a separate service that requires authentication.
Do not cache key server responses. Key requests contain authentication tokens and return cryptographic material. They should always go directly to the key server.
Testing
Verify CORS headers are set correctly:
curl -I -H "Origin: https://app.example.com" \
https://cdn.example.com/content/vid-001/seg-0.ts
# Should include:
# Access-Control-Allow-Origin: https://app.example.com
Verify cache behavior:
# First request: cache miss
curl -I https://cdn.example.com/content/vid-001/seg-0.ts
# X-Cache: Miss from cloudfront (or CF-Cache-Status: MISS)
# Second request: cache hit
curl -I https://cdn.example.com/content/vid-001/seg-0.ts
# X-Cache: Hit from cloudfront (or CF-Cache-Status: HIT)