Skip to content

feat(httpaction,curl,outboundhttp): per-hop redirect admission, cancellation non-denial, and partial outcome accounting - #2109

Open
chris-beckman wants to merge 2 commits into
feat/ua01-http-result-ingestionfrom
feat/ua04-redirect-admission
Open

chris-beckman wants to merge 2 commits into
feat/ua01-http-result-ingestionfrom
feat/ua04-redirect-admission

Conversation

@chris-beckman

@chris-beckman chris-beckman commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #2066 → #2080 → #2083 → #2084 → #2086 → #2090 → #2092 → #2093 → #2095 → #2102

Summary (UA04 Redirect Admission & Truthful Partial Outcomes)

  1. Typed Per-Hop Request Facts (outboundhttp, httpaction):

    • Introduces outboundhttp.HopRequest{URL *url.URL, Method string, HostHeader string} and BeforeHopRequest func(context.Context, HopRequest) error on outboundhttp.Request, keeping BeforeHop intact for non-httpaction callers (webresearch, loopwake).
    • Preserves caller HostHeader across same-origin redirects (sameOrigin(current, next)) and drops HostHeader across cross-origin redirects (!sameOrigin(current, next)), grounded in the shipped http_request.yaml contract ("drops caller headers across origins" and host_header: "Override HTTP Host header without changing destination network dial").
    • Maps native curl -H "Host: ..." to http_request host_header so explicit Host overrides flow through httpaction.Perform, per-hop secret screening, and cookie authority checks.
  2. Per-Hop Route Admission & Initial-Hop Loopback Port Extraction (httpaction, curl):

    • Evaluates admitHopRoute per hop via BeforeHopRequest, enforcing egressgate.AwaitHTTPRequest on both the logical target URL and any --connect-to / --resolve dial target selected for that hop.
    • Restricts curl.loopbackPorts to the initial request URL (including Hop 0 --connect-to / --resolve port remappings) so redirect-only dial mappings do not pre-authorize later loopback hops before Hop 0 runs.
  3. Cancellation vs. Denial Separation (confine, egressgate):

    • Ensures confine (askOrCache) and egressgate (awaitEndpoint) do not cache negative verdicts (b.verdict.Store(key, false) / state.denied) or return HostDeniedError when the caller/review context is cancelled (context.Canceled / context.DeadlineExceeded), preserving clean same-session recovery.
  4. Truthful Partial & Unknown Multi-Hop Outcome Accounting (outboundhttp, httpaction, curl, test/wiring):

    • Tracks RequestsStarted and ResponsesReceived across redirect hops in outboundhttp and surfaces them in httpaction.Outcome / TransportFailure and native curl outcome details (http_requests_started, http_responses_received) without fabricating sent:sha256:... body digest claims for uncompleted or pre-wire failed hops.
    • Settles Hop 1+ mid-chain redirect denials, pre-wire failures (dns, dial, tlsFault), and post-dial/post-TLS failures before response headers arrive with truthful EffectCompleted / EffectUnknown certainty and non-zero exit codes, preventing command from auto-replaying already-executed Hop 0 exchanges.
    • Verified end-to-end through the real registered native curl adapter (TestCommandProgramCurlRedirectDenialUnknownHopAndRecoveryRealAdapter in lycaon/test/wiring/command_curl_data_path_e2e_test.go).

…llation non-denial, and partial outcome accounting

Signed-off-by: Chris Beckman <chris@paintedwolf.ai>
@chris-beckman
chris-beckman marked this pull request as draft November 10, 2026 11:49
…op, and recovery proof

Signed-off-by: Chris Beckman <chris@paintedwolf.ai>
@chris-beckman
chris-beckman marked this pull request as ready for review November 10, 2026 12:14

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant