Skip to content

CONCEPT Cited by 1 source

Envelope wrap-and-unwrap metadata routing

Definition

Envelope wrap-and-unwrap metadata routing is a wire-format technique for threading routing metadata (tenant ID, version, account key, agent ID, etc.) through an opaque payload that a downstream engine persists and replays without knowing about the metadata. On the outbound side, the dispatcher wraps the caller's payload in an envelope:

caller →  { __metadata: <routing-hints>, payload: <original> }
          ─────────────────────────────────────────────────
              ↓ persisted by engine (unchanged)
              ↓ survives sleep, crash, redeploy
              ↓ replayed N hours later
              ─────────────────────────────────────────────
engine → dispatcher: wrapped.payload arrives
                     unwrap → routing-hints → load the right code
                     forward the unwrapped payload

The engine treats the envelope as opaque bytes. The dispatcher at both ends handles wrap (on invocation) and unwrap (on replay). The envelope is the only piece that knows about routing; the engine doesn't care, and the caller doesn't know. Canonicalised by Cloudflare's Dynamic Workflows library.

Canonical example

Dynamic Workflows wraps env.WORKFLOWS.create(...) on the tenant's side:

tenant calls:  create({ params: { name: 'Alice' } })
engine sees:   create({ params: {
                 __workerLoaderMetadata: { tenantId: 't-42' },
                 params: { name: 'Alice' }
               }})

The Workflows engine persists event.payload — which now includes the envelope — and schedules the run. Every time the engine later wakes the workflow (whether after a 24-hour sleep, a crash, or a deploy), the metadata rides along with the payload. On run(event, step), the registered entrypoint class unwraps the envelope, hands the metadata to the loadRunner callback (which loads the tenant's code), and forwards the unwrapped event through. (Source: Cloudflare Dynamic Workflows.)

Why it works

The power of the envelope pattern is that it requires no changes to the downstream engine. The engine is still doing exactly what it always did: persist a payload, replay it later, call a class's run(event, step). All the per-tenant routing is implemented in additive wrap/unwrap glue layered on top. This makes the pattern cheap to adopt — you can ship dynamic-dispatch behaviour on top of any durable primitive without a new engine fork.

Cloudflare frames this explicitly: "every interesting line of this library is either a wrapper around .create() on the outbound side or a wrapper around WorkflowEntrypoint on the inbound side." The envelope is the load-bearing connective tissue.

Structural requirements

  1. The engine's payload must be caller-controlled-opaque-ish. The engine has to be willing to persist whatever shape you give it, without inspecting the inner structure. Workflows satisfies this; it treats event.payload as user data.
  2. The envelope must be stable across engine restarts and replays. It rides along with the payload through sleep boundaries, crashes, and redeploys. It must not depend on runtime state that won't exist at replay time.
  3. Wrap and unwrap must be deterministic. Replay-based durable execution relies on deterministic state reconstruction. The envelope shape must be stable in the face of library upgrades (breaking it mid-flight is a replay hazard — see concepts/workflow-determinism-requirement).
  4. Metadata is routing, not authorization. The tenant can read the envelope back via instance.status(). Don't put secrets in there. The isolation boundary is elsewhere (capability-based sandboxing, per-tenant keys in KMS, etc.). (Source: sources/2026-05-01-cloudflare-introducing-dynamic-workflows-durable-execution-that-follows-the-tenant.)

Distinction from adjacent shapes

  • Not a new serialisation format. The envelope is just a JSON-shaped wrapper ({ __metadata, payload }). No new bytes-on-wire format; no new schema registry.
  • Not HTTP header-routing. Headers are stripped by engines that only care about the body. The envelope is inside the body, so it survives the engine's persistence step.
  • Not encryption envelopes. The envelope is unencrypted metadata. It's a routing hint, not a confidentiality mechanism. Contrast with concepts/envelope-encryption on the wiki, which wraps a ciphertext in a KMS-derived DEK.
  • Not Authorization: tokens. The envelope is authority- free metadata. Its purpose is to tell a future invocation who was this for, not is this call allowed.

Generalises to

The pattern works wherever a durable engine persists an opaque payload and replays it later:

  • Queues — envelope the producer ID so the consumer fleet can dispatch into the right per-producer handler.
  • Durable timers — envelope the tenant ID so the timer firing spins up the right tenant's handler.
  • Event-sourced aggregates — envelope the aggregate key so replay routes into the right projection handler.
  • Outbound webhooks — envelope the subscription ID so the retry logic knows which tenant's destination it's talking to.

Cloudflare pre-announces queues / caches / databases / object stores / AI bindings / MCP servers as the next applications of this pattern on their platform.

Seen in

Last updated · 438 distilled / 1,268 read