Skip to main content
A custom scanner is an HTTPS endpoint you host. For each covered event, Runlayer sends it a signed request describing the event, and your endpoint answers allow or deny. Runlayer keeps enforcement. A deny goes through the same block or alert flow, audit log, and incidents as Runlayer’s built-in scanners, so you can plug in an existing DLP or classification service without building the plumbing around it.

What gets scanned

Each scanner covers one or both directions: Custom scanners run alongside the built-in scanners. A call is blocked if any scanner blocks it.

Add a scanner

  1. Go to Settings → Security Scanners → Custom scanners and click Add scanner.
  2. Enter:
    • Name: how the scanner appears in settings, the audit log, and incidents.
    • Endpoint URL: the https URL Runlayer posts to, exactly as entered.
    • Scan: tool input, tool output, or both.
    • Action: what Runlayer does when your scanner denies a call. Block rejects it, Alert records the deny and lets the call through, and Disabled turns the scanner off. You can also change it from the scanner’s row, like any other scanner.
    • Timeout (ms): 1 to 10000. The default is 5000.
    • When it fails or times out: fail open (the call goes through) or fail closed (the failure counts as a deny, so the call is blocked when the action is Block and recorded when it’s Alert).
    • Custom headers (optional): static headers sent on every request, for example an API key for your gateway. They are stored encrypted and never shown again.
  3. Copy the signing secret. Runlayer shows it once. Your endpoint uses it to verify requests.
  4. Click the settings button on the scanner’s row, then Test connection. Runlayer sends a test request and shows the verdict, latency, and status code.
A workspace can have up to 10 custom scanners.

Build the endpoint

The full request and response format is in the custom scanner OpenAPI spec. In short: Request. Runlayer sends one POST per event with a JSON body:
Tool output requests carry the tool_use message followed by a user message with a tool_result block. Ignore fields and block types you don’t recognize; new optional ones may be added. Response. Return HTTP 200 with a verdict:
deny_reason (up to 500 characters) and reference_id (up to 50 characters of A-Z a-z 0-9 . _ : / -) are optional and recorded in the audit log. For a pass, return {"action": "allow"}. Failures. Anything other than a 200 with a valid verdict counts as a scanner failure: other status codes (including redirects, which Runlayer doesn’t follow), an invalid or oversized body (over 64 KiB), a timeout, or a connection error. The scanner’s failure mode then decides whether the call goes through. If the connection can’t be opened, Runlayer retries once after 100 ms within the same timeout. Nothing else is retried. Connection tests have a request_id starting with test_ and metadata.scan_path set to config_test. Return allow for them.

Verify requests

Requests are signed with the Standard Webhooks scheme, so any Standard Webhooks library can verify them. Each request carries webhook-id, webhook-timestamp, and webhook-signature headers. To verify by hand:
  1. Base64-decode the part of the secret after whsec_. That is the HMAC key.
  2. Compute base64(HMAC-SHA256(key, "{webhook-id}.{webhook-timestamp}.{raw body}")) over the exact bytes received.
  3. Accept the request if any v1,<signature> entry in webhook-signature matches, using a constant-time comparison.
  4. Reject requests whose webhook-timestamp is more than 5 minutes from your clock.

Rotate the secret

Click Rotate secret on the scanner’s page. For 5 minutes after rotation, Runlayer signs every request with both the new and the old secret, so you can switch your verifier over without rejecting traffic.

Network requirements

  • The endpoint must use https. Credentials in the URL aren’t accepted; use a header instead.
  • Private-network addresses are allowed, so the scanner can run next to a self-hosted Runlayer. Loopback, link-local, and cloud metadata addresses are refused.
  • Don’t compress responses. Runlayer sends Accept-Encoding: identity.
Your scanner sits on the request path of every covered call, so keep it fast.

Overrides

Custom scanners can be overridden in the same places as the built-in scanners:
  • On a connector’s Security tab, for the whole connector or for a single tool on it.
  • Under Local Tools and Client Overrides, for a client, a local tool family such as Terminal, or a single local tool such as bash.
Only the action can be overridden. The failure mode and timeout always come from the scanner’s own settings. When several overrides apply to a call, the most specific one wins, the same as for built-in scanners.

Monitor a scanner

The scanner’s settings page, opened from its row under Custom scanners, charts its activity over the last 7, 30, or 90 days, filterable by direction:
  • Calls, denies, timeouts, and failure rate.
  • Failures over time, split into timeouts and errors.
  • Median and 95th-percentile latency.
Charts update within a few minutes. Each failure is also written to the audit log with its cause and latency, and each deny appears like any other scanner finding.