/*
RFC-1284: unauthenticated live probe for access protection — fetches a
channel URL anonymously and reports the observed HTTP status. Consumed by
sternsystem.validate ACCESS-01 (expected-401 check); leitstand.verify may reuse
it for the same check.
- Does not authenticate — the probe must observe the anonymous answer;
a 401 is the pass signal.
- Does not retry or follow redirects — one GET, redirect: "manual".
- Does not distinguish channel health — only "does the edge demand auth".
- RFC-1284: initial — shared probe for ACCESS-01 desired-state reconciliation.
RFC-1284
*/
export interface AccessProbeResult {
/** "status" when the edge answered; "transport-error" on network failure/timeout. */
outcome: "status" | "transport-error";
/** HTTP status observed (present iff outcome === "status"). */
status?: number;
/** Error detail (present iff outcome === "transport-error"). */
error?: string;
}
export const ACCESS_PROBE_DEFAULT_TIMEOUT_MS = 5000;
/**
* Fetch `url` anonymously and report the edge answer. Transport failures and
* timeouts return `transport-error` — never throw — so callers can downgrade
* inconclusive probes to warnings instead of violations.
*/
export async function probeAccessProtection(
url: string,
opts: { timeoutMs?: number; fetchImpl?: typeof fetch } = {},
): Promise {
const { timeoutMs = ACCESS_PROBE_DEFAULT_TIMEOUT_MS, fetchImpl = fetch } = opts;
try {
const res = await fetchImpl(url, {
method: "GET",
redirect: "manual",
signal: AbortSignal.timeout(timeoutMs),
});
// Drain the body so the socket can be reused; ignore drain errors.
try {
await res.arrayBuffer();
} catch {
// body unreadable — status is still the answer
}
return { outcome: "status", status: res.status };
} catch (err) {
return {
outcome: "transport-error",
error: err instanceof Error ? err.message : String(err),
};
}
}