core.http
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
- Side
soul-side - Collection
soulstack.network - Category
network
network_outboundStates
core.http.probe — A single GET/HEAD request to url; the response (status / body / elapsed_ms / header keys) is returned into register.
Never: a read-only probe by construction does not mutate state.
Always — the probe only reads, the step stays a no-op.
Parameters
| Param | Type | Required / default | Description |
|---|---|---|---|
url | string | required | Target URL (https:// only; SSRF-guard on the resolved IP). |
method | stringGET | HEAD | optional · GET | HTTP method GET|HEAD (default GET); mutating methods are forbidden. |
headers | map<string> | optional | HTTP headers (values are secret, never appear in output). |
status_codes | list<int> | optional | Expected response codes (list of int); 2xx by default. |
allow_private | bool | optional | Lift the SSRF-guard for an internal health-check (default false). |
allow_http | bool | optional | Allow http:// (lift https-only); file:// stays forbidden. Does not open SSRF (default false). |
insecure_skip_verify | bool | optional | Disable TLS verification (self-signed / internal CA); MITM risk (default false). |
timeout | stringformat: duration | optional | Probe 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: trueOutput
| Field | Type | Description |
|---|---|---|
status | int | |
body | string (semi-trusted, cap 64 KiB) | |
truncated | bool | |
elapsed_ms | int | |
changed | false | |
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.