Skip to content

core.http

← Module catalog

soul-sidesoulstack.networknetwork

Read-probe of an HTTP endpoint: health check, API readiness, reading a version.

core.http.probe makes a single GET or HEAD to url and returns facts about the response (status / body / elapsed_ms / header keys) into register. This is a verb form, not a declarative state: changed=false always and non-configurable — the probe changes nothing on the host. It is secure-by-default: https-only, an SSRF-guard on the resolved IP, TLS chain verification; it does not write to the filesystem and does not run subprocesses, so root is not needed.

Requirements

  • Rootnot required
  • Sidesoul-side
  • Collectionsoulstack.network
  • Categorynetwork
Capabilitiesnetwork_outbound

States

core.http.probe — A single GET/HEAD request to url; the response (status / body / elapsed_ms / header keys) is returned into register.

Changes when

Never: a read-only probe by construction does not mutate state.

Stays unchanged when

Always — the probe only reads, the step stays a no-op.

Parameters

ParamTypeRequired / defaultDescription
urlstringrequiredTarget URL (https:// only; SSRF-guard on the resolved IP).
methodstring
GET | HEAD
optional · GETHTTP method GET|HEAD (default GET); mutating methods are forbidden.
headersmap<string>optionalHTTP headers (values are secret, never appear in output).
status_codeslist<int>optionalExpected response codes (list of int); 2xx by default.
allow_privatebooloptionalLift the SSRF-guard for an internal health-check (default false).
allow_httpbooloptionalAllow http:// (lift https-only); file:// stays forbidden. Does not open SSRF (default false).
insecure_skip_verifybooloptionalDisable TLS verification (self-signed / internal CA); MITM risk (default false).
timeoutstring
format: duration
optionalProbe timeout (Soul Stack duration, e.g. "30s").

Example — Wait until the service answers HTTP 200 (with retry)

- name: Wait until the service answers HTTP 200
module: core.http.probe
register: health
retry: { count: 5, delay: 3s }
params:
url: https://service.internal:8443/healthz
method: GET
status_codes: [200]
allow_private: true

Output

FieldTypeDescription
statusint
bodystring (semi-trusted, cap 64 KiB)
truncatedbool
elapsed_msint
changedfalse
headers_keys[string]only if headers are set (sorted keys without values)
warnings[string]only if an opt-out flag is enabled (one line per relaxed guard, with host)

Notes

  • changed=false always, by construction and non-configurable — a read-probe does not change host state. The fact (up/down, a version from body) can be interpreted via changed_when: at the scenario level (precedent — core.exec.run).
  • A verb form, not a declarative state: it returns facts about the endpoint into register without bringing anything to a state. Mutating HTTP (POST/PUT/PATCH/DELETE) is deliberately deferred post-MVP to a separate ADR extension (probably core.http.request) — the changed contract for mutations will be decided then too.
  • Does not run subprocesses and writes nothing to the filesystem: a pure in-memory HTTP client. The manifest declares only network_outbound (no exec_subprocess and no fs_write_root).
  • The body cap is 64 KiB (OOM protection): beyond the limit the body is dropped and truncated: true is set in the output (the boundary is cut on a full UTF-8 rune). Binary / corrupt body bytes are coerced to valid UTF-8 (replaced with U+FFFD).
  • A status outside status_codes → the step is failed, but with output attached (status/body for diagnostics). A transport error (DNS/TLS/timeout/a blocked downgrade redirect) → failed with no output.

Security & defaults

  • https-only by default — http:// and file:// are rejected (util.ValidateFetchURL). To relax to http(s) — allow_http; file:// stays forbidden even with allow_http.
  • SSRF-guard: a probe to metadata (169.254.169.254) / loopback / RFC1918 / link-local is blocked on the actually resolved IP — this closes direct SSRF to cloud-metadata IAM and DNS rebind. To relax it for a legitimate internal health check — allow_private. allow_http does not open SSRF: the dial-guard lives separately.
  • TLS — the system trust store by default; to disable verification for self-signed / internal CA — insecure_skip_verify (a MITM risk, enable only for a trusted internal endpoint). Redirect downgrade protection: a redirect to non-https is blocked; with allow_http an https→http hop is allowed, but a redirect to a non-http(s) scheme is always blocked.
  • The three opt-out flags are orthogonal (the HTTP client is built per-call): relaxing one does not weaken the others. When any is enabled, the probe puts warnings into the output — one line per relaxed guard, with host only (without the full URL and headers).
  • The body is semi-trusted: only vault-ref substrings are masked (vault:… → ***MASKED***, including a ref inside JSON); an arbitrary plaintext secret in the body is not masked — do not put into a probe endpoint anything that must not be exposed. headers are sensitive-by-construction: values are not logged, only the list of keys is returned in the output.

See also