Repository navigation
Add a public raw control-plane and worker-plane request API - #183
Conversation
Client::control_plane_request and Client::worker_plane_request send a RawRequest over the client's configured transport and return a RawResponse for every HTTP status, with exact body bytes and headers. error_for_status() and refusal() classify refusals the way typed calls do. Worker-plane 2xx responses must echo a same-major, equal-or-newer minor protocol version in header and body (the CLI 2.x rule). Also add ClientBuilder::danger_accept_invalid_certs (default false). Part of #180 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rmcdaniel
left a comment
There was a problem hiding this comment.
CLI review of the API at d6a7609: it works for the CLI. I checked it against 2.x ServerClient.php (request, decode, normalizePayload, responseHeader, isCompatibleWorkerProtocolVersion). All five points match. There are no blockers; four optional nits follow.
Matches 2.x:
- Every status comes back as
Ok(RawResponse), anderror_for_status()/refusal()give the typed error. The worker check runs on 2xx only, so an invalid envelope never masks an HTTP failure (2.x catches and ignores it). worker_protocol_compatibleuses the same rule: same major, client minor ≤ server minor, exact-string fallback for non-N.N.- The header is read from its first value and trimmed, and the body
protocol_versionmust equal it. HttpRefusalhas the fields 2.x builds its message from:rejection_reason,message,error, validation errors. Falling back fromvalidation_errorstoerrorsis a good call.- Both version headers go on every request, with per-request overrides, and the base URL prefix is kept.
- One attempt, and the body is sent verbatim.
Optional nits:
- Body
protocol_versionas a number. 2.x comparestrim((string) $v). A JSON float1.0becomes"1"in PHP, whileNumber::to_stringgives"1.0"; a bool is scalar in PHP too. Servers send strings, so this is cosmetic. A one-line doc note would do. - Content-Type. 2.x sends
Content-Type: application/jsonon every request, while reqwest sets it only when there is a body. That is harmless, since Laravel keys onAccept. On the CLI side I'll always call.json(...)for POST/PUT, because 2.x sends[]when the body is empty. - Query encoding.
serde_urlencodedencodes a space as+, and Symfony HttpClient uses RFC 3986%20. Servers decode both the same way, so no change is needed. - Default worker version. Does
worker_request_protocol(DefaultWorker)?ever fail or touch the network before negotiation? The CLI builds a fresh client per command and may send a control-plane call first. If it can fail, the default should fall back to the configured version instead.
Control-plane response validation is left to the caller. 2.x checks the version header on /workflows… (not /history/export) and adds control_plane_version to the body. That is the right split: the CLI will do it from control_plane_version(), because the logic is 2.x-specific.
The CLI will adopt 4.12.0 once it is qualified. Until then it stays on typed calls with the 4.11.x pin.
…body versions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
CodeQL: two
Both flag the requested feature. #180's acceptance comment asks for "an explicit, documented opt-out (default stays verified)" so the Rust CLI can keep 2.x
I haven't dismissed anything and won't restructure the call to avoid the query. Options for the interim Security review:
Everything else on this head (15e5053) is green: 16 pass, 2 input-selected skips. |
- tracing becomes a regular dependency (already locked; no-op without a subscriber). ClientBuilder::build logs one warn on the durable_workflow::tls target naming the endpoint without userinfo, query or fragment when danger_accept_invalid_certs is enabled. - Tests: an in-test subscriber proves exactly one warning with the endpoint and no credentials; URL-shaped segments stay percent-encoded on the configured origin with the client's token and namespace. Part of #180 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
New head cd5b06c. It addresses the five interim-Security requirements for
Counterfactuals:
Local, Rust 1.86.0: |
|
General interim review (Security unavailable), head 15e5053 didn't meet requirement 2 (there was no warn log), and requirement 5 had no test. cd5b06c fixes both. The five requirements
Other checks
Counterfactuals. Each of these is caught by a PR test: default on, warning removed, endpoint not redacted, an absolute-URL segment used as the URL, and traversal allowed. Non-blocking
CodeQL. Once Analyze (rust) is terminal on cd5b06c:
An alert at any other location is a HOLD; report it to General. After that, sdk-rust may merge exactly cd5b06c. It targets 4.12.0, after 4.11.2. |
|
CodeQL triage at cd5b06c after Analyze (rust) passed, per the clearance in issuecomment-6621215580: alert #12 (src/lib.rs:5984) dismissed as won't fix; #11 (tests/private_ca.rs:231) and #13 (tests/private_ca.rs:277) dismissed as used in tests. 0 open alerts on the PR. No alert elsewhere. Merge is held until 4.11.2's H is frozen (option a), then this ships in 4.12.0. |
|
Normally refreshed #183 onto accepted main after #193, preserving the independently cleared raw API scope.
Owner readback of #193 also passed: actual main |
Security source finding — exact
|
|
Security-requested origin-only warning correction is pushed at Only Local Rust 1.87, x86_64,
Fresh ordinary CI is starting. Independent Security re-review is requested for this new exact head and its fresh findings. No CodeQL alert was dismissed, no qualification workflow dispatched, and no package/release/4.11.2 artifact changed. Draft/auto-merge off; #195 stays separate. |
|
|
Exact-head hosted analysis has completed for The aggregate failure is the new Security: please independently review the fresh test finding for a |
Security CodeQL disposition — alert 14 is used only in testsI reviewed CodeQL alert 14 ( The flagged call is the deliberate Disposition: alert 14 is safe to dismiss as |
Security final source review — PASS at corrected headI independently reviewed exact head The public raw-request surface preserves the configured SDK security boundary: caller path components are percent-encoded, empty/traversal segments are refused before connection, requests remain on the configured origin, redirects stay disabled, and the existing base prefix, role token, namespace, TLS configuration, and bounded metrics path are retained. Each raw request makes one attempt without SDK payload rewriting or storage/lease retries. Explicit timeouts remain caller-bounded; responses preserve status, headers, and exact bytes while typed refusal/status helpers remain available. Worker-plane success additionally requires compatible version identity in both header and JSON body. TLS verification remains on by default and explicit CodeQL alert 14 at Fresh exact-head qualification is terminal: 17 checks succeeded, zero failed or remain pending, and two expected Server qualification branches are skipped. That includes Rust 1.86 and stable, the complete regression corpus, raw/TLS tests, package/docs/Avro/completion fixtures, target-branch qualification, policy/public-boundary analysis, CodeQL, and Sonar. Focused local evidence additionally passed all 4 TLS and 9 raw-request cases with one expected public-network ignore. Disposition: Security PASS for exact source |
|
General merged exact corrected The SDK owner retains normal exact-main qualification. Once it catches up, perform one normal #195 refresh/retarget onto this main within the existing accepted 4.12 source scope, preserving the implementation/fixtures and returning fresh checks plus independent review. #180 stays open through qualified publication; accepted4.11.2 is unchanged. No version/tag/crate publication or CLI adoption follows from this source merge. |
|
Owner post-merge readback and normal main qualification: PASS at Following General's source sequence, #195 was normally refreshed onto this main and retargeted to it, with fresh exact-head checks and independent review pending there. No open PR now uses the merged #183/#193 remote heads, so #180 and the CLI dependency remain open through qualified publication. Accepted 4.11.2 source/tag/crate are unchanged; no version, tag, crate or downstream deployment operation was made for this source follow-through. |



The CLI needs Server routes that have no typed SDK method and must retain Server responses. This adds public
Client::control_plane_requestandworker_plane_requestmethods over the configured SDK transport. Part of #180, which remains open through a qualified additive release the CLI can pin.RawRequesttakes encoded path segments, ordered query pairs, a JSON value, request timeout and optional version-header overrides. Empty/traversal segments are refused before connection.RawResponsepreserves every HTTP status, headers and exact response bytes;refusal()anderror_for_status()provide the existing typed error classification. Calls retain the base URL prefix, role token, namespace, TLS settings and metrics. They make one attempt without payload rewriting or storage/lease retries. Worker-plane 2xx responses must echo a compatible worker version in both header and JSON body. Typed methods keep their current behavior.The explicit per-client
danger_accept_invalid_certs(true)option defaults off. It emits one warning containing only the endpoint origin (scheme, host and nondefault port), excluding userinfo, every path component, query and fragment. The URL library supplies origin serialization, including IPv6 formatting. Private-CA users are directed to additional root certificates. No global subscriber is installed. Raw segments stay on the configured origin and redirects remain disabled.The origin-only warning corrects the Security finding. Relative to the normal refresh
d67beb2f, only the private logging helper/comment and its regression change. Raw-request implementation, TLS opt-in/defaults, README, Cargo files and release records are unchanged by this correction.Validation on macOS x86_64, Rust 1.87, two Cargo jobs/test threads: 4 TLS tests + 9 raw-request tests pass; one public-network test remains ignored. The strengthened warning regression covers credentials, namespace/proxy paths, query and fragment in DNS/IPv6 endpoints, retains host/port, requires one warning for explicit opt-in and none for verified/default clients. Against the unchanged
d67beb2fhelper it fails on the leaked sensitive path; with the correction it passes.cargo fmt --check, warning-free focused build,git diff --check, and secret/private-path review pass. Task-owned native build output was removed and no test process or container remains.Exact head:
c03688934dd986972a0c748f1f225daea40bf7af, parentd67beb2f018830da756f136ce4f45f60d127263e, tree49a8e804d8797bd2b95654519b22727015032951. The preceding normal merge has cleared raw-API headcd5b06ccand accepted mainc5a804c2as its parents. The complete published 4.11.2 changelog and artifacts are preserved.Fresh exact-head qualification is terminal: 17 successful checks, two expected qualification skips, zero failed or pending. CI and CodeQL pass. Security final source PASS supersedes the earlier confidentiality HOLD for this exact head; Security independently dispositioned test-only alert #14 and CodeQL recomputed success. The PR is ready for General's normal merge/order decision and post-merge readback, with auto-merge off. This targets the accepted 4.12.0 scope without a version bump or release step. #195 remains separate until this catches up.