Skip to content

Commit 5858e94

Browse files
deploy: e70ee95
1 parent d20887c commit 5858e94

271 files changed

Lines changed: 1661 additions & 1016 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

2.0/llms-full.txt

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10310,6 +10310,14 @@ and serverless platform errors are transport facts. They only become workflow
1031010310
facts after the carrier maps them to the declared result envelope or to
1031110311
`malformed_output`.
1031210312

10313+
The first concrete carrier under this contract is the
10314+
[invocable HTTP carrier](./invocable-carrier.md), published at
10315+
`worker_protocol.invocable_carrier_contract`. It is activity-task only,
10316+
requires HTTPS (with loopback HTTP allowed only for development), and resolves
10317+
auth through the external executor configuration. Its transport-level
10318+
`retry_policy` is distinct from the durable activity retry policy, which
10319+
remains the server/runtime authority once a result is reported.
10320+
1031310321
## Bridge Adapters
1031410322

1031510323
Bridge adapters are bounded ingress or handoff surfaces. They can start,
@@ -11335,6 +11343,250 @@ For support bundles, collect `dw doctor --output=json`, `dw server:info
1133511343
- [CLI and Python Parity](./cli-python-parity.md) compares shared request
1133611344
fixtures across CLI and SDK clients.
1133711345

11346+
<!-- Source: docs/polyglot/invocable-carrier.md -->
11347+
11348+
# Invocable HTTP Carrier
11349+
11350+
The invocable HTTP carrier is the first concrete carrier defined under the
11351+
[external execution surface](./external-execution.md). It is published from
11352+
`GET /api/cluster/info` at
11353+
`worker_protocol.invocable_carrier_contract` with
11354+
`schema: durable-workflow.v2.invocable-carrier.contract`, `version: 1`, and
11355+
`carrier_type: invocable_http`.
11356+
11357+
When a configured handler mapping resolves to an `invocable_http` carrier, the
11358+
server invokes the configured endpoint with the carrier-neutral
11359+
[external task input envelope](./external-execution.md#published-contract-seams)
11360+
and reconciles the response against the
11361+
[external task result envelope](./external-execution.md#published-contract-seams).
11362+
Workflow tasks, signal/update ordering, replay, and history mutation stay
11363+
inside the server. The carrier only moves declared input and result envelopes
11364+
across an HTTPS boundary.
11365+
11366+
## What It Is
11367+
11368+
The invocable HTTP carrier exists for activity-grade work that an operator
11369+
team would rather host as an HTTPS handler than as a long-poll worker:
11370+
11371+
- operator maintenance activities and platform automation hosted in an internal
11372+
HTTP service
11373+
- bounded integration handoffs that already terminate at an HTTPS endpoint
11374+
- serverless or container-based activity handlers that expose a single POST
11375+
route per activity type
11376+
11377+
Activity execution is delegated to the configured endpoint. Durable workflow
11378+
state, history, and the activity-completion contract remain server-owned.
11379+
11380+
## Activity-Only Scope
11381+
11382+
The published manifest fixes the carrier scope to `task_kinds: [activity_task]`
11383+
and names explicit non-goals so the boundary cannot drift through configuration:
11384+
11385+
- `workflow_task_execution` — workflow tasks remain on real workflow runtimes
11386+
- `workflow_replay` — the carrier never replays workflow history
11387+
- `history_mutation` — handlers may not edit event history directly
11388+
- `generic_webhook_ingress` — generic ingress goes through a bridge adapter,
11389+
not through this carrier
11390+
11391+
A handler mapping with a non-activity `kind`, or a carrier mapping with a
11392+
non-`activity_task` capability, fails configuration validation with
11393+
`invalid_invocable_carrier_scope` before it can appear on the activity poll
11394+
response.
11395+
11396+
## HTTPS Target And Method
11397+
11398+
Carrier `target_fields` are validated by the server before a mapping is
11399+
exposed:
11400+
11401+
| Field | Required | Allowed | Notes |
11402+
| --- | --- | --- | --- |
11403+
| `url` | yes | absolute HTTPS URL, or HTTP for loopback dev (`localhost`, `127.0.0.0/8`, `::1`) | URL credentials (`scheme://user:pass@host`) are forbidden. |
11404+
| `method` | no | `POST` | Defaults to `POST`. No other HTTP method is accepted. |
11405+
| `timeout_seconds` | no | integer 1–900 | Transport deadline for one handler attempt. The task deadline is enforced separately by the server/runtime. |
11406+
| `retry_policy` | no | object (see below) | Carrier-owned transport retry budget. |
11407+
11408+
Invalid targets fail closed with `invalid_carrier_target` and never appear in
11409+
discovery output.
11410+
11411+
## Auth Model
11412+
11413+
Non-loopback `invocable_http` mappings must resolve an `auth_ref` from the
11414+
external executor configuration. Unauthenticated invocable HTTP is allowed
11415+
only against loopback HTTP targets so a developer can iterate locally.
11416+
11417+
- `auth_refs` are declared once at the top of the config and referenced from
11418+
defaults or per-mapping. Supported types include `profile`, `env`,
11419+
`token_file`, `mtls`, and `signed_headers`.
11420+
- A mapping that resolves to a non-loopback target without an effective
11421+
`auth_ref` fails configuration validation with `missing_invocable_auth_ref`.
11422+
- Tokens, secrets, signatures, and authorization headers are never echoed in
11423+
cluster diagnostics, in `dw server:info` output, or on the activity poll
11424+
response. The mapping diagnostics report which `auth_ref` resolved and the
11425+
redacted summary, never the credential value.
11426+
11427+
The intent is that an operator can read the redacted runtime diagnostics and
11428+
confirm which auth secret will be used, without the server ever exposing the
11429+
secret itself.
11430+
11431+
## Transport Retry Versus Durable Activity Retry
11432+
11433+
The optional `retry_policy` on an invocable carrier is **transport-only**. It
11434+
governs the carrier's HTTP delivery before the handler reports a result:
11435+
11436+
| `retry_policy` field | Allowed | Default | Meaning |
11437+
| --- | --- | --- | --- |
11438+
| `max_attempts` | integer 1–5 | `1` | Maximum HTTP delivery attempts the carrier will make for one task lease. |
11439+
| `backoff_seconds` | array of integers, each 0–300, up to 5 entries | `[]` | Per-attempt backoff before the next HTTP try. |
11440+
| `retryable_status_codes` | subset of `[408, 425, 429, "5xx"]` | `[408, 429, "5xx"]` | Response codes that count as transport-retryable. |
11441+
11442+
Transport retries never become history events. The server only learns about
11443+
the carrier's final attempt — either the structured success/failure envelope
11444+
the handler returned, or a transport timeout that maps to
11445+
`failure.kind=timeout, classification=deadline_exceeded`, or one of the
11446+
malformed-output paths.
11447+
11448+
Once the handler reports a result, the **durable activity retry policy
11449+
remains the server/runtime authority**. Task-level retry, scheduling, and
11450+
backoff continue to follow the activity retry policy declared on the
11451+
workflow side. The carrier's `retry_policy` does not extend, override, or
11452+
substitute for it.
11453+
11454+
## Request And Response Envelope
11455+
11456+
The server sends the carrier-neutral input envelope and expects the
11457+
carrier-neutral result envelope back:
11458+
11459+
| Direction | Content type | Schema |
11460+
| --- | --- | --- |
11461+
| Request | `application/vnd.durable-workflow.external-task-input+json` | `external_task_input_contract` |
11462+
| Response | `application/vnd.durable-workflow.external-task-result+json` | `external_task_result_contract` |
11463+
11464+
The handler must preserve `task.id`, `task.attempt`, and
11465+
`task.idempotency_key` from the input envelope, and must respond with the
11466+
declared success or failure envelope (or fail-closed by emitting a
11467+
malformed-output mapping).
11468+
11469+
The carrier maps transport facts to result facts deterministically:
11470+
11471+
- transport timeout → `failure.kind=timeout`,
11472+
`classification=deadline_exceeded`
11473+
- non-2xx response without a valid result envelope → `malformed_output`
11474+
- invalid JSON or schema mismatch on the response → `malformed_output`
11475+
- result envelope referencing an unsupported payload reference →
11476+
`unsupported_payload`
11477+
11478+
Handlers must be idempotent. The same `task.id` and
11479+
`task.idempotency_key` may arrive more than once if the carrier retries
11480+
transport delivery or the runtime redelivers an unfinished lease.
11481+
11482+
## Configuration Example
11483+
11484+
The server reads handler mappings from
11485+
`DW_EXTERNAL_EXECUTOR_CONFIG_PATH`, optionally selecting an overlay with
11486+
`DW_EXTERNAL_EXECUTOR_CONFIG_OVERLAY`. See the
11487+
[Server Config Reference](./server-config-reference.md) for the full list of
11488+
environment variables.
11489+
11490+
Below is a minimal `durable-workflow.external-executor.config` document that
11491+
registers one `invocable_http` carrier and one activity mapping:
11492+
11493+
```json
11494+
{
11495+
"schema": "durable-workflow.external-executor.config",
11496+
"version": 1,
11497+
"defaults": {
11498+
"profile": "prod",
11499+
"namespace": "operations",
11500+
"task_queue": "operator-tasks",
11501+
"auth_ref": "handler-token"
11502+
},
11503+
"auth_refs": {
11504+
"handler-token": {
11505+
"type": "env",
11506+
"env": "DURABLE_WORKFLOW_HANDLER_TOKEN"
11507+
}
11508+
},
11509+
"carriers": {
11510+
"ops-invocable": {
11511+
"type": "invocable_http",
11512+
"url": "https://handlers.example.com/durable/activity",
11513+
"method": "POST",
11514+
"timeout_seconds": 60,
11515+
"capabilities": ["activity_task"],
11516+
"retry_policy": {
11517+
"max_attempts": 3,
11518+
"backoff_seconds": [2, 5],
11519+
"retryable_status_codes": [408, 429, "5xx"]
11520+
}
11521+
}
11522+
},
11523+
"mappings": [
11524+
{
11525+
"name": "billing.reconcile-ledger",
11526+
"kind": "activity",
11527+
"task_queue": "operator-tasks",
11528+
"activity_type": "billing.reconcile-ledger",
11529+
"carrier": "ops-invocable",
11530+
"handler": "billing.reconcile-ledger",
11531+
"timeout_seconds": 60
11532+
}
11533+
]
11534+
}
11535+
```
11536+
11537+
A loopback dev variant uses `"url": "http://127.0.0.1:8080/durable/activity"`
11538+
and may omit `auth_ref` because loopback HTTP is the one allowed
11539+
unauthenticated path.
11540+
11541+
## Inspection And Diagnostics
11542+
11543+
Two CLI commands surface the published invocable carrier contract from a
11544+
running server:
11545+
11546+
- `dw server:info` renders the schema name, contract version, carrier type,
11547+
task kinds, allowed request/response content types, and the redacted
11548+
external executor mapping diagnostics. Use it to confirm the server has
11549+
loaded the expected mappings.
11550+
- `dw doctor` runs the cluster-info diagnostic, prints the
11551+
`invocable_carrier_contract` block, and surfaces any
11552+
`invalid_carrier_target`, `missing_invocable_auth_ref`,
11553+
`invalid_invocable_carrier_scope`, `unknown_carrier`, `unknown_auth_ref`,
11554+
or `unknown_handler` errors that blocked a mapping from being advertised.
11555+
11556+
The activity poll response itself reports the resolved mapping, the carrier
11557+
target (with auth redacted), and the effective `retry_policy`. Workers and
11558+
operator tooling read these fields as the source of truth instead of caching
11559+
their own copy of the configuration file.
11560+
11561+
## Coexistence And Rollout
11562+
11563+
The invocable carrier rollout boundary is published in the same manifest:
11564+
11565+
- A poll-based carrier and an `invocable_http` carrier may share a queue only
11566+
when the mappings are activity-type specific. Two mappings cannot claim the
11567+
same `(task_queue, activity_type)` pair.
11568+
- Operators must remove or overlay-disable invocable mappings before deleting
11569+
the credentials they reference. Pulling credentials before the mapping is
11570+
drained will move every new attempt to `failure.kind=auth`, not silently
11571+
succeed.
11572+
- Carrier `retry_policy` is purely transport. The durable activity retry
11573+
policy remains the only authority over how many task attempts the workflow
11574+
observes.
11575+
11576+
## Related Surfaces
11577+
11578+
- [External Execution Surface](./external-execution.md) — the carrier-neutral
11579+
product boundary that this carrier implements.
11580+
- [Server Config Reference](./server-config-reference.md) — environment
11581+
variables for `DW_EXTERNAL_EXECUTOR_CONFIG_PATH` and
11582+
`DW_EXTERNAL_EXECUTOR_CONFIG_OVERLAY`.
11583+
- [External Payload Storage](../features/external-payload-storage.md) — how
11584+
oversized request or result payloads are offloaded to a configured driver
11585+
and represented as a verifiable reference envelope inside the same input
11586+
and result schemas the invocable carrier uses.
11587+
- [Worker Protocol](./worker-protocol.md) — the broader worker-plane contract
11588+
that publishes this carrier alongside poll-based handler shapes.
11589+
1133811590
<!-- Source: docs/polyglot/namespace-auth-workers.md -->
1133911591

1134011592
# Namespace, Auth, And Worker Registration

0 commit comments

Comments
 (0)