Files
atelier/domains/edge/cdn.md
T
Jon Chery 20992883ff docs(P01): complete edge domain phase — v0.4
---ci---
project: atelier
phase: 1
milestone: v0.4
status: complete
phase_role: execution
phase_tag: v0.3.1
requirements:
  covered: [ATELIER-92, ATELIER-93, ATELIER-94, ATELIER-95, ATELIER-96]
  partial: []
---/ci---
2026-08-05 15:51:03 +00:00

12 KiB

CDN — Derived Rules

Derives from domains/edge/first-principles.md. Applies P1 (Proximity is the Design Driver) and P6 (Cache Invalidation is Explicit) primarily, with P5 (idempotent cache fill), P8 (geographic distribution), and P9 (identity at the edge). For the edge-cache-vs-origin decision, see the decision matrix below. Cross-links domains/performance/frontend for generic caching, domains/security/input-validation for cache poisoning, and domains/observability/metrics for cache-hit ratio.

What a CDN Is (P1 Proximity is the Design Driver)

  • A content delivery network is a fleet of PoPs (points of presence) placed near users. The PoP serves cached content; the origin is the authoritative source. The CDN's whole purpose is P1: compute (the cache) is placed near the user so the round trip to the origin does not bound latency. Latency is a correctness constraint at the edge (C1), not a performance preference.
  • The CDN is the canonical edge-cache architecture (Akamai, Cloudflare, Fastly): PoPs near users, origin shielding, cache-key normalization, purge APIs. Atelier derives the placement/invalidation principles, not the vendor config.
  • The boundary with domains/performance/frontend is per D-061: performance owns generic caching and optimization (cache what is expensive, stable, read often — performance/P5 Caching with Intent); edge owns the geographic, partition-aware placement and invalidation angle. A CDN is an edge concern because its defining trait is geographic distribution (P8) and partition-aware invalidation (P6), not measurement.

Cache Key Design (P6 Cache Invalidation is Explicit)

  • The cache key is the contract between the URL and the cached representation. A key that varies on the wrong dimensions serves the wrong content; a key that varies on too many dimensions collapses the hit ratio. Key design is the invalidation surface: a key that includes a content hash or version segment makes invalidation explicit; a key that ignores Vary headers serves stale variants.
  • Normalize the key: lower-case the host, strip default ports, sort query parameters, ignore tracking parameters. A non-normalized key is a cache-poisoning vector (see domains/security/input-validation) and a hit-ratio destroyer (see domains/observability/metrics).
  • A cache with no explicit key strategy is a TTL-less cache under partition (P6 violation): staleness is silent and unbounded.
# Cache key derivation: vary on what changes content, ignore what
# does not. The key is the tuple (host, normalized-path, sorted-
# query, Vary-headers); the cache entry is the representation + TTL.
Cache-Key: example.com /api/v1/products?sort=price&region=us Vary:Accept-Encoding
Cache-Control: public, max-age=60, s-maxage=600, stale-while-revalidate=300
Vary: Accept-Encoding
  • max-age bounds the browser cache; s-maxage bounds the CDN PoP; stale-while-revalidate allows serving stale while refetching. Each is an explicit invalidation strategy (P6).

TTL vs Explicit Invalidation (P6, C3 Simplicity)

  • TTL-based invalidation (max-age, s-maxage): the cache entry expires after a duration. Simple, no origin contact required to invalidate, but bounded staleness is the contract — the entry may be stale up to TTL. Fits content where eventual consistency is acceptable (asset fingerprints, lists, derived images).
  • Explicit invalidation (purge, surrogate keys): the operator signals the cache to drop entries. Tighter staleness bounds, but requires the origin or operator to know which entries to purge. Fits content where staleness is a correctness defect (price updates, availability, breaking news).
  • A TTL-less cache with no explicit invalidation is a P6 violation: stale-forever under partition. Every cache must have one or the other (or both), and the choice is documented per content type.

Cache-Hit / Miss / Origin-Fetch (P5, P6)

  • Hit: the PoP serves from cache. Latency is PoP-local (P1).
  • Miss: the PoP has no entry; it fetches from the origin (or an origin-shield PoP). The fetch must be idempotent (P5) — a retried miss must not corrupt the cache or double-write side effects.
  • Revalidate: the PoP holds a stale entry and asks the origin (If-None-Match, If-Modified-Since); a 304 refreshes the TTL without re-fetching the body. Revalidation is the bandwidth-economical middle ground (C8).
# Conditional revalidation — the PoP asks the origin "is this still
# current?" The 304 response refreshes the TTL without a body.
GET /api/v1/products HTTP/1.1
Host: example.com
If-None-Match: "etag-7a3f"

HTTP/1.1 304 Not Modified
ETag: "etag-7a3f"
Cache-Control: s-maxage=600
  • A cache-hit ratio that is not measured is a gate on noise — see domains/observability/metrics for the SLI/SLO discipline that makes the hit ratio a meaningful signal. A CDN with no hit-ratio metric is operating blind (P10 analog).

Origin Shielding (P1, P8, C8 Economy)

  • Origin shielding routes all origin fetches through a single shield PoP (or shield region). The shield absorbs the thundering-herd: 10 000 PoPs missing the same URL fetch the origin once, not 10 000 times. This is C8 Economy (origin bandwidth is bounded) and P1 (the shield is itself a proximity layer for the origin).
  • Shielding is a geographic decision (P8): the shield sits in a region close to the origin, not close to the user. The shield is the inner ring of the CDN; the user-facing PoPs are the outer ring.
  • A CDN without origin shielding under a stampede will overload the origin; a shield that is itself partitioned from the origin must degrade gracefully (P7) — serve stale per stale-while-revalidate rather than 500.

Purge Strategies (P6, C3 Simplicity)

Strategy Granularity Latency to Invalidate Cost Best for
URL purge One URL Seconds Low (one entry) Surgical fixes, single-page corrections
Soft purge One URL (mark stale, serve while refetch) Seconds Low High-traffic URLs where a hard purge causes a stampede
Surrogate-key purge A tag set (e.g., product:123, category:shoes) Seconds Medium (key indexing) Related-content invalidation (a product update purges all its category pages)
Wildcard purge A path prefix or pattern Seconds to minutes High (scan) Site-wide template changes
All-cache purge Everything Seconds Very high (origin stampede) Disaster recovery only; never the steady-state invalidation path
  • Surrogate-key purge (Fastly, Akamai) is the highest-value strategy: tag cache entries with content keys, then purge by tag. This is explicit invalidation at scale (P6) without the origin stampede of an all-cache purge.
  • An all-cache purge as the steady-state invalidation path is a P6 violation dressed as a feature — it pushes the origin load back to 100% miss, defeating the CDN's purpose (P1).
  • A cache poisoned by a crafted request (a URL with a malicious header that gets cached and served to others) is a correctness defect (C1) and a security breach (P9 — the edge node is exploited). Prevent poisoning by:
    • Normalizing the cache key (strip untrusted query parameters, ignore unknown headers, lower-case the host).
    • Validating Vary against an allow-list; never Vary: * on a shared cache (poisonable via header injection).
    • Treating uncacheable responses (Set-Cookie, Cache-Control: private) as never-stored.
  • See domains/security/input-validation for the general input-validation discipline the cache key must follow. The cache key is a validation surface; a non-validated key is an attack surface.

Multi-CDN Routing (P8 Geographic Distribution)

  • A multi-CDN strategy routes each request to the best PoP across providers (Akamai + Cloudflare + Fastly). Routing is location-aware (P8): latency, cost, and availability vary by region and provider. The DNS layer (or a client-side router) selects the CDN per request.
  • Multi-CDN is a P8 decision, not a vendor-management decision: geographic distribution is the first-class constraint. A single-CDN deployment routes everything to one provider's PoPs; a multi-CDN deployment routes by region, latency, and cost.
  • Invalidation across multiple CDNs is harder (P6): each provider has its own purge API and surrogate-key scheme. A multi-CDN purge must fan out to all providers; a purge that reaches only one CDN leaves the others stale. Track purge completion per provider — see domains/observability/metrics for the per-CDN hit-ratio and purge-latency signals.
# A CDN config example: cache-control headers + a purge rule.
# Origin response: declare the cache contract (P6).
HTTP/1.1 200 OK
Cache-Control: public, max-age=60, s-maxage=600, stale-while-revalidate=300
Surrogate-Key: product:123 category:shoes
ETag: "etag-7a3f"
Vary: Accept-Encoding

# Purge rule (Fastly-style surrogate-key): when product 123
# updates, purge every cache entry tagged product:123 OR
# category:shoes. Explicit, bounded, no origin stampede (P6).
POST /service/svc1/purge
Surrogate-Key: product:123 category:shoes
# Returns: {"status": "ok", "id": "purge-abc"} — poll the purge
# status to confirm completion across all PoPs (P8, P10).

Edge-Cache vs Origin — Decision Matrix (D-069)

Strategy When Latency Origin Load Correctness Risk
Serve from PoP (cache hit) The PoP holds a fresh entry (within TTL or revalidated) Lowest (PoP-local, P1) None Low — bounded by TTL staleness (P6)
Serve stale while revalidate The PoP holds a stale entry and stale-while-revalidate is set Low (stale served immediately, refetch in background) Background refetch (1 per entry) Medium — stale served up to the revalidate window; acceptable for eventually-consistent content
Fetch fresh from origin (miss) The PoP has no entry, or the content is non-cacheable High (origin round trip) Full fetch per miss Low — fresh by construction; the miss is the correctness floor
Origin-shield fetch Multiple PoPs miss the same URL; the shield collapses the herd Medium (PoP → shield → origin) Bounded to one origin fetch per shield (C8) Low — shield is the inner ring; staleness bounded by shield TTL
Purge and serve fresh Explicit invalidation received (surrogate-key or URL purge) Medium (purge propagates, then fresh fetch) Full fetch post-purge Lowest — explicit invalidation is the tightest staleness bound (P6)
Serve from origin directly (bypass cache) Content is non-cacheable (personalized, real-time) Highest (every request hits origin) Full fetch per request Lowest for correctness, highest for origin load — use sparingly
  • The default is serve from PoP when fresh, fetch fresh from origin on miss with origin-shield to bound origin load, and purge and serve fresh when explicit invalidation is required. Bypass-the-cache is for non-cacheable content only — bypassing for cacheable content is a P1 violation (you have defeated the CDN).
  • The correctness risk column is bounded by the invalidation strategy (P6): every row except "bypass" carries staleness risk that is bounded by TTL or explicit purge. A row with no invalidation strategy is a P6 violation.

What Violates CDN Discipline

Violation Principle
TTL-less edge cache under partition (stale-forever, no explicit invalidation) P6 Cache Invalidation is Explicit
Cache key that varies on untrusted query parameters (poisonable) P6, P9 (domains/security/input-validation)
All-cache purge as the steady-state invalidation path (origin stampede) P6, C8 Economy
Non-normalized cache key (case-sensitive host, unsorted query) P6, domains/security/input-validation
Bypass-the-cache for cacheable content P1 Proximity is the Design Driver (defeats the CDN)
Multi-CDN with no per-CDN purge completion tracking P8, P10 (stale cache invisible to the operator)
Origin fetch that is not idempotent under retry P5 Edge Operations are Idempotent
Cache-hit ratio not measured P10, domains/observability/metrics
Single-CDN deployed where geographic distribution requires multi-CDN P8 Geographic Distribution
Shield PoP that 500s instead of serving stale under partition P7 Partial Degradation is Engineered