Skip to main content
A guard is a policy service that decides whether an agent may act. guardHook runs a guard that lives in your process. httpGuard calls a guard over HTTPS, so a vendor ships an endpoint instead of JavaScript. Both plug into beforeAuthorize, and timeout, default deny and audit fields come from the hook runner (see hooks).

Protocol 2026-10

httpGuard sends one POST per check. The vendor answers with a signed verdict.
The signature is v1= plus the hex HMAC-SHA256, keyed with the shared secret, of the string <timestamp>.<raw body>. x-theauth-key-id is sent only when you set keyId, for secret rotation on the vendor side. The response is HTTP 2xx, carries two signature headers, and has one of the JSON bodies below.
The response signature is v1= plus the hex HMAC-SHA256 of <requestId>.<response timestamp>.<raw response body>, keyed with the shared secret (or responseSecret if you set one). requestId is the one from the request envelope, so a response to a different request does not verify. httpGuard checks it in constant time and applies the same 300 second window (toleranceSec). A missing, malformed, stale or wrong signature denies through the hook runner before the body is parsed. Unknown fields in the body are rejected.
allow and escalate may carry reason. deny requires a non-empty reason (returned to the calling agent, so keep secrets out) and may carry code. All three may carry guard, ruleId and metadata (at most 16 scalar keys), which are written to the audit row. code is stored as ruleId when ruleId is absent. escalate sends the call to the approval flow. An allow never overrides theAuth’s own permission checks.

Vendor side

Verify, decide, sign. Three lines:
signGuardResponse(secret, { requestId, body, timestamp? }) returns the two response headers. Sign the exact string you send, byte for byte. If you do not use TypeScript, implement the same HMAC in any language. verifyGuardRequest checks the signature in constant time, rejects timestamps more than 300 seconds away from now (set toleranceSec to change it), and parses the envelope. Pass seen: (requestId) => boolean to reject exact replays inside the window.

Exposing HOL Guard (or any guard)

  1. Put the guard behind an HTTPS endpoint and give the customer a secret.
  2. Verify signature and freshness, then map input to your engine. agent, delegation, action and resource are always present, arguments only if the customer did not redact it.
  3. Sign the verdict with signGuardResponse and return it inside the latency budget (default 2000 ms, the customer sets hookSafety).
  4. Treat dryRun: true as a question, not an event: do not count or alert on it.
  5. Run the conformance check and publish the result. List the guard in community integrations.
For the conformance canaries, answer allow for resource conformance:canary:allow and deny for conformance:canary:deny.

Security properties

  • The shared secret never leaves the process and is never logged. Errors name the endpoint origin only, not the path, query, headers or body.
  • Signature plus timestamp bind the body to a moment. Stale or future requests fail at the vendor.
  • Any problem denies through the hook runner: network error, timeout, non-2xx, redirect, non-JSON, schema mismatch, oversized body (over 64 KiB). Failing open requires onError: "allow" in hookSafety and is recorded on the audit row.
  • Redirects are never followed, so a compromised endpoint cannot bounce the signed body elsewhere.
  • url must be https. http is accepted only for localhost, 127.0.0.1 and [::1] when NODE_ENV is not production. URLs with embedded credentials are refused.
  • The agent token and session data are never sent.
  • Responses are signed over the request id, a fresh timestamp and the raw body. A TLS-terminating proxy or a compromised hop that does not hold the secret cannot turn a deny into an allow, replay an old allow, or answer one request with another request’s verdict. httpGuard requires this by default (requireSignedResponse: true).
  • requireSignedResponse: false skips the check and is unsafe. Use it only for local development against a vendor stub, never in production.
  • What signing does not do: it does not hide the request or the verdict (TLS is still required for confidentiality and to authenticate the endpoint), it does not help if the secret leaks, and it does not stop a vendor that signs a wrong answer. Within the replay window the same signed response can be replayed only to the same request id, which httpGuard generates fresh per check.
  • Use responseSecret to keep the response key separate from the request key, so a vendor that only verifies requests cannot be used to forge responses.

What leaves your process

The agent (id, owner, tenant, type, name), action, resource, arguments, the delegation chain with each grant’s permissions, source, ip when known and dryRun. arguments can hold user data, so use redact to drop or mask fields before sending. The default sends everything.

Limits

  • One request per check, no batching, no retries. A retry would double the latency budget.
  • Response body limit 64 KiB. Default timeout 2000 ms, clamped to 60000 ms by hookSafety.
  • No mTLS option yet.
  • Conformance proves the protocol, not the quality of the vendor’s decisions.
Last modified on October 18, 2026