Integrations/OpenClaw
Live

FastGRC × OpenClaw

Protect your OpenClaw agents with FastGRC compliance policies. Every tool call is evaluated before it executes — blocking violations, flagging drift, and building an audit trail automatically.

Server / Docker setupNo Node.js required

Running OpenClaw on a Linux server or inside a Docker container? Use the standalone bash hook — just curl and bash, no npm needed.

1. Download the hook script

curl -fsSL https://app.fastgrc.ai/fastgrc-hook.sh -o /usr/local/bin/fastgrc-hook && chmod +x /usr/local/bin/fastgrc-hook

This places the hook at /usr/local/bin/fastgrc-hook — on the system PATH and ready to reference in the next step.

Can't reach the URL? Create the file manually ›

Copy the script below, paste it into a new file named fastgrc-hook, then chmod +x fastgrc-hook.

#!/usr/bin/env bash
# fastgrc-hook — FastGRC PreToolUse compliance hook (no Node.js required)
set -uo pipefail

API_KEY="${FASTGRC_API_KEY:-}"
BASE_URL="${FASTGRC_BASE_URL:-https://app.fastgrc.ai}"
POLICY_ID="${FASTGRC_POLICY_ID:-}"

[[ -z "$API_KEY" ]] && exit 0

INPUT="$(cat 2>/dev/null || true)"
[[ -z "$INPUT" ]] && exit 0

_json_str() {
  local expr="$1" input="$2"
  if command -v jq &>/dev/null; then
    printf '%s' "$input" | jq -r "$expr // empty" 2>/dev/null || true
  elif command -v python3 &>/dev/null; then
    printf '%s' "$input" | python3 -c "
import sys, json
try:
    d = json.load(sys.stdin)
    print($expr, end='')
except Exception:
    pass
" 2>/dev/null || true
  fi
}

TOOL_NAME="$(_json_str '.tool_name' "$INPUT")"
AGENT_ID="$(_json_str '.session_id // .agent_id // ""' "$INPUT")"

if command -v jq &>/dev/null; then
  TOOL_INPUT="$(printf '%s' "$INPUT" | jq -c '.tool_input // {}' 2>/dev/null || echo '{}')"
elif command -v python3 &>/dev/null; then
  TOOL_INPUT="$(printf '%s' "$INPUT" | python3 -c "
import sys, json
try:
    d = json.load(sys.stdin)
    print(json.dumps(d.get('tool_input', {})), end='')
except Exception:
    print('{}', end='')
" 2>/dev/null || echo '{}')"
else
  exit 0
fi

TOOL_NAME="${TOOL_NAME:-unknown}"
TOOL_INPUT="${TOOL_INPUT:-{}}"
CONTENT_ESC="$(printf '%s' "tool_name: ${TOOL_NAME}\nargs: ${TOOL_INPUT}" | sed 's/\\/\\\\/g; s/"/\\"/g')"

BODY="{\"subjectContent\":\"$CONTENT_ESC\",\"subjectType\":\"tool_argument\",\"direction\":\"ingress\",\"agentId\":\"$AGENT_ID\""
[[ -n "$POLICY_ID" ]] && BODY+=",\"policyId\":\"$POLICY_ID\""
BODY+="}"

RESPONSE="$(curl -sf --max-time 5 \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d "$BODY" \
  "${BASE_URL}/api/v1/policy-router/evaluate" 2>/dev/null)" || true

[[ -z "$RESPONSE" ]] && exit 0

if command -v jq &>/dev/null; then
  DECISION="$(printf '%s' "$RESPONSE" | jq -r '.decision // "allow"' 2>/dev/null || echo 'allow')"
  REASONING="$(printf '%s' "$RESPONSE" | jq -r '.reasoning // ""' 2>/dev/null || true)"
  MATCHED="$(printf '%s' "$RESPONSE" | jq -r '.policyContext.matchedRule // ""' 2>/dev/null || true)"
elif command -v python3 &>/dev/null; then
  DECISION="$(printf '%s' "$RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('decision','allow'),end='')" 2>/dev/null || echo 'allow')"
  REASONING="$(printf '%s' "$RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('reasoning',''),end='')" 2>/dev/null || true)"
  MATCHED="$(printf '%s' "$RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print((d.get('policyContext') or {}).get('matchedRule',''),end='')" 2>/dev/null || true)"
else
  exit 0
fi

if [[ "${DECISION:-allow}" == "block" || "${DECISION:-allow}" == "require_approval" ]]; then
  [[ -n "${MATCHED:-}" ]] && printf 'FastGRC blocked: [%s] %s\n' "$MATCHED" "${REASONING:-action blocked}" >&2 || printf 'FastGRC blocked: %s\n' "${REASONING:-action blocked}" >&2
  exit 2
fi
exit 0

2. Set your API key

export FASTGRC_API_KEY=fgrc_k1_your_key_here
# Add to ~/.bashrc or your Docker env to persist across sessions

Get your API key free at fastgrc.ai/connect — no credit card required.

3. Configure the hook in .claude/settings.json

OpenClaw looks for this file in ~/.claude/settings.json or the project's .claude/settings.json. Use the full path to the script:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": ".*",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/local/bin/fastgrc-hook"
          }
        ]
      }
    ]
  }
}

Optional env vars: FASTGRC_POLICY_ID (target a specific policy), FASTGRC_BASE_URL (override API host).

Restart OpenClaw after saving. The hook is fail-open — if the API is unreachable or times out, the tool call proceeds normally.

4. Test it

Run this in the same terminal where OpenClaw runs to confirm the hook fires and reaches the API:

echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /tmp"},"session_id":"test-123"}' \
  | /usr/local/bin/fastgrc-hook
echo "Exit: $?"
# Exit 0 = allowed  |  Exit 2 = blocked

If exit is always 0 and no logs appear in your dashboard, check that FASTGRC_API_KEY is exported in the environment where OpenClaw runs (echo $FASTGRC_API_KEY). The hook exits 0 silently when the key is missing.

Hook connects but nothing is blocked? Your policy starts in Observability Mode — violations are logged but not blocked. Enable enforcement in the dashboard when ready.

Node.js project setup

If your project already uses Node.js and you prefer a typed plugin API:

npm install fastgrc-openclaw

Setup (2 lines)

Add to your openclaw.config.ts file (not the terminal):

// File: openclaw.config.ts
import { FastGRCPlugin } from 'fastgrc-openclaw';

export default {
  plugins: [
    FastGRCPlugin({
      apiKey: process.env.FASTGRC_API_KEY,
    }),
  ],
};
# .env
FASTGRC_API_KEY=fgrc_k1_your_key_here

Get your API key free at fastgrc.ai/connect — no credit card, unlimited agents.

Policy options at install time

When you sign up at /connect, you choose one of three default policies — all start in Observability Mode (log only, nothing blocked):

PolicyBest for
General ObserverAny agent — just start logging to understand behavior
Compliance Drift MonitorSOC 2 / ISO 27001 — detect policy drift as agents evolve
SOC 2 AuditorAudit-ready agents — strict read-only with evidence collection

Switch to enforcement from your dashboard when ready.

Full plugin options

FastGRCPlugin({
  apiKey: string;           // Required
  policyId?: string;        // Target a specific policy (omit for org-wide default)
  onBlock?: 'throw'         // Default: throw FastGRCBlockedError
           | 'warn'         // console.warn and allow through
           | 'silent';      // Allow through silently
  timeoutMs?: number;       // Default: 3000ms. Fail-open on timeout.
  baseUrl?: string;         // Default: https://app.fastgrc.ai
})

Error handling

import { FastGRCBlockedError, FastGRCApprovalRequiredError } from 'fastgrc-openclaw';

// In your OpenClaw error handler or agent wrapper:
try {
  await agent.run(task);
} catch (err) {
  if (err instanceof FastGRCBlockedError) {
    console.log('Blocked by policy:', err.matchedRule);
    console.log('Reason:', err.reasoning);
    // Surface err.message to the user — it's human-readable
  }
  if (err instanceof FastGRCApprovalRequiredError) {
    console.log('Approval needed:', err.dashboardUrl);
  }
}

What gets evaluated

Every before_tool_call invocation sends:

{
  "subjectContent": "tool_name: delete_record\nargs: {\"table\":\"users\",\"id\":\"123\"}",
  "subjectType": "tool_argument",
  "direction": "ingress",
  "agentId": "<agent context id>"
}

The FastGRC policy router checks this against your configured rules (blocked actions, sensitive patterns, approval patterns) and returns a decision in ~20–50ms.

Safety guarantees

  • Fail-open: If FastGRC is unreachable or times out, the tool call is allowed through with a console.warn. FastGRC never breaks your agent due to infra issues.
  • No data stored: Tool arguments are evaluated in-memory. Only the decision record (tool name, decision, reasoning) is stored in the audit log — not the full argument payload.