Skip to content

Architecture

Rendorix is a serverless, edge-first pipeline: clients only talk to HTTPS; originals never ship directly from a public bucket; transforms run only after policy and signature checks. The same logical path applies whether you use a custom domain or the CloudFront hostname your stack outputs.

The exact Terraform module names and whether validation runs in a CloudFront Function, Lambda@Edge, or another hook depend on the published infrastructure—treat the diagram as the reference model, not a binding bill of materials.

At a high level, the delivery stack is a loop between the CDN, lightweight edge checks, and on-demand image work backed by private object storage.

flowchart TB
  subgraph clients [Clients]
    browser[BrowserOrBackend]
  end
  subgraph edge [Edge]
    cdn[CloudFront]
    signatureCheck[SignatureAndPolicy]
  end
  subgraph compute [Serverless compute]
    imageFn[ImageTransformLambda]
  end
  subgraph data [Durable data]
    originals[S3Originals]
    presetCfg[PresetConfiguration]
    secrets[SecretsForSigning]
  end
  browser -->|HTTPS| cdn
  cdn -->|cacheHit| browser
  cdn -->|cacheMiss| signatureCheck
  signatureCheck -->|deny| deny403[ClientError403]
  deny403 --> browser
  signatureCheck -->|valid| imageFn
  imageFn -->|getObject| originals
  imageFn -->|resolvePresets| presetCfg
  imageFn -->|imageBytes| cdn
  browser -.->|build_signed_urls| secrets

Roles in short

ComponentRole
CloudFrontGlobal TLS, caching of successful image responses, and the public URL integrators use.
Signature and policy (edge)Rejects unsigned, expired, or out-of-policy requests before heavy work (see Security).
Image transform (Lambda)On a cache miss, loads the original from S3, applies presets / overrides, and returns bytes (e.g. via Sharp or equivalent).
S3Private source of truth for originals; not a public file browser for your app users.
SecretsHMAC key material and similar values—stored in Secrets Manager, SSM Parameter Store, or your CI, never in the browser.
  1. The client issues GET to your image URL (path + preset and signed query string, or your project’s equivalent).
  2. CloudFront matches a cache key derived from the request (path + relevant query, vary rules, Accept if you negotiate formats—your policy should match the edge config).
  3. If there is a cache hit for a valid 200/304 response, CloudFront returns it—no Lambda runs.
  4. On a miss, the edge path evaluates signature, expiry, and allowlists (preset names, max dimensions, etc.). Fail fast with 4xx when something is off.
  5. The image Lambda (or the component your repo uses) GETs the original from S3 using least privilege IAM, decodes and transforms (CPU-bound work), then returns the bytes through CloudFront so the response can be cached according to your TTL and Cache-Control design (Caching, TTL).
  6. The client receives an optimized image; repeat visitors hit the CDN instead of re-running compute.

Keep cache keys stable: the same logical image (preset + source + options) should map to the same key so you get one transform per unique variant.

  • Origin (S3): Originals are stored as objects (e.g. JPEG, PNG, WebP in). Access is IAM-restricted; only the transform role and operational roles you define can GetObject (and optionally List for admin tasks).
  • Presets: Named bundles (e.g. width, quality, output format) live in configuration the transform code reads—JSON in S3, environment for small static sets, or Parameter Store / DynamoDB if you need runtime updates. See Core concepts: Presets.
  • Overrides: Optional per-request fields may narrow or adjust within bounds you define; document precedence in Usage: Overrides when you lock behavior.
  • Transform runtime: Usually Node on Lambda with Sharp (or a similar library). Memory and timeout need to match your largest reasonable source images; giant uploads may require async or rejection policies.

The image worker is stateless between invocations: all state is S3 + config + secrets—which keeps scaling simple and Terraform-friendly.

  • CloudFront is the right place to amortize work: a miss pays compute + S3; a hit pays egress and request pricing, not a full Lambda run.
  • TTL is split: signed URL lifetime (Signed URLs, Expiration) vs. HTTP cache for successful responses. Align them so you do not serve stale bytes past what your product allows, and so browsers do not fight the CDN.
  • Invalidation is expensive as a day-one habit; prefer versioned or content-addressed keys if you need aggressive updates. For rare global changes, use CloudFront invalidation as an operational tool, not a per-edit workflow.
Kind of stateTypical homeNotes
Preset definitionsRepo JSON, S3 config object, or SSMShip with IaC when possible; version changes like app deploys.
HMAC / API secretsSecrets Manager or SSM (encrypted)Rotated on a schedule; Security.
Terraform stateS3 + DynamoDB lock (common pattern)See Deployment: Terraform.
Per-request URL stateQuery string (signed)Exp, signature, preset—nothing secret in the browser-visible URL except what you intentionally expose.

Nothing in the client should be able to mint valid signed URLs without your server (or build pipeline) and secret.

FailureLikely effectMitigation (directional)
Invalid or expired signature403 at edgeSync canonical signing with edge validation; HMAC.
S3 throttling or NoSuchKey5xx or 404 from pipelineRetry in Lambda with backoff; validate keys at upload time.
Lambda timeout or OOM5xx or partial responseTune memory; cap max pixel count; reject pathological files early.
Cold start on first request after idleHigher TTFBProvisioned concurrency only if revenue-critical; else accept rare cold cost.
Stale CDN after deployOld images for TTLShorter TTL, key versioning, or invalidation (sparingly).
Signing key leakForged URLs until rotateRotate secret; short URL lifetimes; audit who can sign.

For multi-region or zero-downtime deploys, your IaC and CloudFront behavior need their own runbook—start from Deployment: AWS setup.