> ## Documentation Index
> Fetch the complete documentation index at: https://hoopdev-feat-new-runbook-parameters.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Guardrails

> Block dangerous queries before they execute with pattern-based rules

export const GuardrailsAnimation = () => {
  const QUERIES = [{
    sql: "UPDATE users SET status = 'inactive'",
    blocked: true,
    rule: "Missing WHERE clause",
    type: "UPDATE"
  }, {
    sql: "SELECT * FROM orders LIMIT 50",
    blocked: false,
    rule: null,
    type: "SELECT"
  }, {
    sql: "DROP TABLE customers",
    blocked: true,
    rule: "Destructive DDL blocked",
    type: "DROP"
  }, {
    sql: "SELECT id, email FROM users WHERE active = true",
    blocked: false,
    rule: null,
    type: "SELECT"
  }, {
    sql: "DELETE FROM logs",
    blocked: true,
    rule: "Missing WHERE clause",
    type: "DELETE"
  }, {
    sql: "SELECT * FROM events",
    blocked: true,
    rule: "Missing LIMIT on large table",
    type: "SELECT"
  }, {
    sql: "INSERT INTO audit_log VALUES (...)",
    blocked: false,
    rule: null,
    type: "INSERT"
  }, {
    sql: "SELECT * FROM salaries",
    blocked: true,
    rule: "Restricted table access",
    type: "SELECT"
  }, {
    sql: "ALTER TABLE users DROP COLUMN ssn",
    blocked: true,
    rule: "Destructive DDL blocked",
    type: "ALTER"
  }, {
    sql: "SELECT name, role FROM team WHERE dept = 'eng'",
    blocked: false,
    rule: null,
    type: "SELECT"
  }];
  const FONT_URL = "https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&family=JetBrains+Mono:wght@400;500&family=Sora:wght@600;700;800&display=swap";
  const animationStyles = `
    @keyframes slideIn {
      from { opacity: 0; transform: translateX(-40px); }
      to { opacity: 1; transform: translateX(0); }
    }
    @keyframes slideThrough {
      0% { opacity: 0; transform: translateX(-20px); }
      15% { opacity: 1; transform: translateX(0); }
      85% { opacity: 1; transform: translateX(0); }
      100% { opacity: 0; transform: translateX(20px); }
    }
    @keyframes pulseShield {
      0%, 100% { transform: scale(1); filter: drop-shadow(0 0 0px rgba(var(--bronze-rgb),0)); }
      50% { transform: scale(1.08); filter: drop-shadow(0 0 12px rgba(var(--bronze-rgb),0.4)); }
    }
    @keyframes pulseShieldBlock {
      0% { transform: scale(1); filter: drop-shadow(0 0 0px rgba(180,60,60,0)); }
      30% { transform: scale(1.15); filter: drop-shadow(0 0 16px rgba(180,60,60,0.5)); }
      100% { transform: scale(1); filter: drop-shadow(0 0 0px rgba(180,60,60,0)); }
    }
    @keyframes resultSlide {
      from { opacity: 0; transform: translateY(8px); }
      to { opacity: 1; transform: translateY(0); }
    }
    @keyframes flowDot {
      0% { offset-distance: 0%; opacity: 0; }
      10% { opacity: 1; }
      90% { opacity: 1; }
      100% { offset-distance: 100%; opacity: 0; }
    }
    @keyframes fadeInUp {
      from { opacity: 0; transform: translateY(12px); }
      to { opacity: 1; transform: translateY(0); }
    }
    @keyframes scanLine {
      0% { top: 0%; opacity: 0; }
      10% { opacity: 1; }
      90% { opacity: 1; }
      100% { top: 100%; opacity: 0; }
    }
  `;
  const ShieldIcon = ({state, size = 48}) => {
    const color = state === "block" ? "#B43C3C" : state === "pass" ? "#4A7C59" : "var(--bronze)";
    const anim = state === "block" ? "pulseShieldBlock 0.5s ease-out" : state === "pass" ? "pulseShield 0.6s ease-out" : "none";
    return <svg width={size} height={size} viewBox="0 0 48 48" style={{
      animation: anim
    }}>
        <path d="M24 4L6 12v12c0 11.1 7.7 21.5 18 24 10.3-2.5 18-12.9 18-24V12L24 4z" fill="none" stroke={color} strokeWidth="2.5" strokeLinejoin="round" />
        <path d="M24 8L10 14.5v9.5c0 9.2 6.4 17.8 14 20 7.6-2.2 14-10.8 14-20v-9.5L24 8z" fill={color} fillOpacity="0.08" />
        {state === "block" && <g stroke="#B43C3C" strokeWidth="3" strokeLinecap="round">
            <line x1="18" y1="18" x2="30" y2="30" />
            <line x1="30" y1="18" x2="18" y2="30" />
          </g>}
        {state === "pass" && <polyline points="16,24 22,30 32,18" fill="none" stroke="#4A7C59" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />}
        {state === "idle" && <g>
            <rect x="19" y="15" width="10" height="3" rx="1.5" fill="var(--bronze)" fillOpacity="0.5" />
            <rect x="17" y="21" width="14" height="3" rx="1.5" fill="var(--bronze)" fillOpacity="0.35" />
            <rect x="19" y="27" width="10" height="3" rx="1.5" fill="var(--bronze)" fillOpacity="0.2" />
          </g>}
      </svg>;
  };
  const QueryTag = ({type}) => {
    const colors = {
      SELECT: {
        bg: "rgba(var(--bronze-rgb),0.08)",
        text: "var(--bronze)"
      },
      UPDATE: {
        bg: "rgba(180,120,60,0.1)",
        text: "var(--bronze)"
      },
      DELETE: {
        bg: "rgba(180,60,60,0.08)",
        text: "#B43C3C"
      },
      DROP: {
        bg: "rgba(180,60,60,0.08)",
        text: "#B43C3C"
      },
      INSERT: {
        bg: "rgba(74,124,89,0.08)",
        text: "#4A7C59"
      },
      ALTER: {
        bg: "rgba(180,60,60,0.08)",
        text: "#B43C3C"
      }
    };
    const c = colors[type] || colors.SELECT;
    return <span style={{
      fontFamily: "'JetBrains Mono', monospace",
      fontSize: 10,
      fontWeight: 500,
      color: c.text,
      background: c.bg,
      padding: "2px 8px",
      borderRadius: 4,
      letterSpacing: "0.04em",
      textTransform: "uppercase",
      whiteSpace: "nowrap"
    }}>{type}</span>;
  };
  const [activeIndex, setActiveIndex] = useState(-1);
  const [phase, setPhase] = useState("idle");
  const [results, setResults] = useState([]);
  const [stats, setStats] = useState({
    blocked: 0,
    allowed: 0
  });
  const timerRef = useRef(null);
  const indexRef = useRef(0);
  const processNext = useCallback(() => {
    const idx = indexRef.current % QUERIES.length;
    const q = QUERIES[idx];
    indexRef.current += 1;
    setActiveIndex(idx);
    setPhase("scanning");
    timerRef.current = setTimeout(() => {
      setPhase("result");
      setResults(prev => {
        const next = [{
          ...q,
          ts: Date.now()
        }, ...prev];
        return next.slice(0, 6);
      });
      setStats(prev => ({
        blocked: prev.blocked + (q.blocked ? 1 : 0),
        allowed: prev.allowed + (q.blocked ? 0 : 1)
      }));
      timerRef.current = setTimeout(() => {
        setPhase("idle");
        timerRef.current = setTimeout(() => {
          processNext();
        }, 600);
      }, 1400);
    }, 900);
  }, []);
  useEffect(() => {
    timerRef.current = setTimeout(() => processNext(), 800);
    return () => clearTimeout(timerRef.current);
  }, []);
  const currentQuery = activeIndex >= 0 ? QUERIES[activeIndex] : null;
  const shieldState = phase === "result" && currentQuery ? currentQuery.blocked ? "block" : "pass" : phase === "scanning" ? "idle" : "idle";
  return <div style={{
    fontFamily: "'DM Sans', system-ui, sans-serif",
    borderRadius: 12,
    padding: "20px 24px 16px",
    position: "relative",
    overflow: "hidden",
    width: "100%",
    color: "var(--sand-100)"
  }}>
      <style>{animationStyles}</style>

      {}
      <div style={{
    position: "absolute",
    top: "-30%",
    right: "-10%",
    width: "60%",
    height: "160%",
    background: "radial-gradient(ellipse at center, rgba(var(--warm-gold-rgb),0.12) 0%, transparent 70%)",
    pointerEvents: "none"
  }} />

      {}
      <div style={{
    display: "grid",
    gridTemplateColumns: "1fr auto 1fr",
    gap: 24,
    alignItems: "start",
    position: "relative",
    height: 320
  }}>
        {}
        <div style={{
    background: "rgba(var(--sand-100-rgb),0.04)",
    border: "1px solid rgba(var(--sand-100-rgb),0.08)",
    borderRadius: 10,
    padding: 20,
    height: 300,
    overflow: "hidden"
  }}>
          <div style={{
    fontSize: 10,
    fontWeight: 600,
    color: "rgba(var(--sand-100-rgb),0.25)",
    textTransform: "uppercase",
    letterSpacing: "0.08em",
    marginBottom: 14
  }}>Incoming Query</div>

          {currentQuery && phase !== "idle" ? <div key={indexRef.current} style={{
    animation: "slideIn 0.35s ease-out"
  }}>
              <div style={{
    display: "flex",
    alignItems: "center",
    gap: 8,
    marginBottom: 10
  }}>
                <QueryTag type={currentQuery.type} />
                <span style={{
    fontSize: 11,
    color: "rgba(var(--sand-100-rgb),0.3)"
  }}>
                  via CLI
                </span>
              </div>
              <div style={{
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: 13,
    lineHeight: 1.7,
    color: "var(--sand-300)",
    background: "rgba(var(--sand-100-rgb),0.03)",
    padding: "14px 16px",
    borderRadius: 8,
    border: "1px solid rgba(var(--sand-100-rgb),0.06)",
    wordBreak: "break-all",
    position: "relative",
    overflow: "hidden"
  }}>
                {currentQuery.sql}
                {phase === "scanning" && <div style={{
    position: "absolute",
    left: 0,
    width: "100%",
    height: 2,
    background: "linear-gradient(90deg, transparent, var(--warm-gold), transparent)",
    animation: "scanLine 0.8s ease-in-out"
  }} />}
              </div>

              {phase === "scanning" && <div style={{
    marginTop: 14,
    fontSize: 12,
    color: "var(--warm-gold)",
    display: "flex",
    alignItems: "center",
    gap: 6,
    animation: "fadeInUp 0.3s ease-out"
  }}>
                  <span style={{
    width: 6,
    height: 6,
    borderRadius: "50%",
    background: "var(--warm-gold)",
    display: "inline-block",
    animation: "pulseShield 1s ease-in-out infinite"
  }} />
                  Evaluating rules...
                </div>}

              {phase === "result" && <div style={{
    marginTop: 14,
    animation: "fadeInUp 0.3s ease-out",
    display: "flex",
    alignItems: "center",
    gap: 8
  }}>
                  <span style={{
    width: 8,
    height: 8,
    borderRadius: "50%",
    background: currentQuery.blocked ? "#B43C3C" : "#4A7C59",
    display: "inline-block"
  }} />
                  <span style={{
    fontSize: 12,
    fontWeight: 500,
    color: currentQuery.blocked ? "#D4726A" : "#7CB88A"
  }}>
                    {currentQuery.blocked ? "Blocked" : "Allowed"}
                  </span>
                  {currentQuery.rule && <span style={{
    fontSize: 11,
    color: "rgba(var(--sand-100-rgb),0.3)"
  }}>
                      {currentQuery.rule}
                    </span>}
                </div>}
            </div> : <div style={{
    color: "rgba(var(--sand-100-rgb),0.15)",
    fontSize: 13,
    fontStyle: "italic",
    paddingTop: 20
  }}>Waiting for next query...</div>}
        </div>

        {}
        <div style={{
    display: "flex",
    flexDirection: "column",
    alignItems: "center",
    justifyContent: "center",
    paddingTop: 40,
    gap: 12,
    minWidth: 72
  }}>
          <ShieldIcon state={shieldState} size={56} />
          <div style={{
    fontSize: 10,
    fontWeight: 600,
    color: "rgba(var(--sand-100-rgb),0.2)",
    textTransform: "uppercase",
    letterSpacing: "0.08em",
    textAlign: "center"
  }}>
            {phase === "scanning" ? "Checking..." : phase === "result" ? currentQuery?.blocked ? "Denied" : "Clear" : "Ready"}
          </div>
        </div>

        {}
        <div style={{
    background: "rgba(var(--sand-100-rgb),0.04)",
    border: "1px solid rgba(var(--sand-100-rgb),0.08)",
    borderRadius: 10,
    padding: 20,
    height: 300,
    overflow: "hidden"
  }}>
          <div style={{
    fontSize: 10,
    fontWeight: 600,
    color: "rgba(var(--sand-100-rgb),0.25)",
    textTransform: "uppercase",
    letterSpacing: "0.08em",
    marginBottom: 14
  }}>Session Log</div>

          <div style={{
    display: "flex",
    flexDirection: "column",
    gap: 6
  }}>
            {results.length === 0 && <div style={{
    color: "rgba(var(--sand-100-rgb),0.15)",
    fontSize: 12,
    fontStyle: "italic"
  }}>
                No sessions yet
              </div>}
            {results.map((r, i) => <div key={r.ts} style={{
    display: "flex",
    alignItems: "center",
    gap: 8,
    padding: "8px 10px",
    background: i === 0 ? "rgba(var(--sand-100-rgb),0.03)" : "transparent",
    borderRadius: 6,
    border: i === 0 ? "1px solid rgba(var(--sand-100-rgb),0.06)" : "1px solid transparent",
    animation: i === 0 ? "resultSlide 0.3s ease-out" : "none",
    opacity: 1 - i * 0.12
  }}>
                <span style={{
    width: 6,
    height: 6,
    borderRadius: "50%",
    flexShrink: 0,
    background: r.blocked ? "#B43C3C" : "#4A7C59"
  }} />
                <span style={{
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: 11,
    color: "rgba(var(--sand-100-rgb),0.5)",
    overflow: "hidden",
    textOverflow: "ellipsis",
    whiteSpace: "nowrap",
    flex: 1
  }}>
                  {r.sql.length > 32 ? r.sql.slice(0, 32) + "..." : r.sql}
                </span>
                <span style={{
    fontSize: 10,
    fontWeight: 500,
    flexShrink: 0,
    color: r.blocked ? "#D4726A" : "#7CB88A"
  }}>{r.blocked ? "BLOCKED" : "OK"}</span>
              </div>)}
          </div>
        </div>
      </div>

      {}
      <div style={{
    display: "flex",
    alignItems: "center",
    gap: 20,
    marginTop: 16,
    paddingTop: 12,
    borderTop: "1px solid rgba(var(--sand-100-rgb),0.06)",
    position: "relative"
  }}>
        <span style={{
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: 11,
    color: "rgba(var(--sand-100-rgb),0.25)"
  }}>
          {stats.blocked} blocked
        </span>
        <span style={{
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: 11,
    color: "rgba(var(--sand-100-rgb),0.25)"
  }}>
          {stats.allowed} allowed
        </span>
        <span style={{
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: 11,
    color: "var(--warm-gold)"
  }}>
          &lt;5ms latency
        </span>
      </div>
    </div>;
};

<div style={{background: 'linear-gradient(135deg, #111111 0%, #1A1A1A 35%, #2A2A2A 70%, #3A3A3A 100%)', borderRadius: 14, overflow: 'hidden', padding: 24, height: 380}}>
  <GuardrailsAnimation />
</div>

## What You'll Accomplish

Guardrails let you block dangerous queries before they execute. You can:

* Prevent accidental `UPDATE` or `DELETE` without a `WHERE` clause
* Block `DROP TABLE` and other destructive DDL commands
* Enforce read-only access for specific user groups
* Require `LIMIT` clauses on large tables
* Block queries that access sensitive columns

***

## The Problem Guardrails Solve

Without guardrails, one typo can destroy production data:

```sql theme={null}
-- Intended: Update one user's email
UPDATE users SET email = 'new@email.com' WHERE id = 123;

-- Accidental: Update ALL users (forgot WHERE clause)
UPDATE users SET email = 'new@email.com';
-- 500,000 rows affected 💥
```

Guardrails catch these mistakes before they execute, showing an error instead of running the dangerous query.

***

## How Guardrails Work

<Steps>
  <Step title="Query Submitted">
    User runs a query through Hoop (CLI, Web App, or API)
  </Step>

  <Step title="Rules Evaluated">
    Each guardrail rule is checked against the query using pattern matching
  </Step>

  <Step title="Decision Made">
    If a rule matches: block, warn, or require approval based on configuration
  </Step>

  <Step title="Result Returned">
    User sees either the query result or an error explaining which rule was violated
  </Step>
</Steps>

### Rule Types

| Type             | Description                         | Use Case                        |
| ---------------- | ----------------------------------- | ------------------------------- |
| **Input Rules**  | Evaluate the query before execution | Block dangerous commands        |
| **Output Rules** | Evaluate results after execution    | Redact sensitive data in output |

***

## Quick Start

## Prerequisites

To get the most out of this guide, you will need to:

* Either [create an account in our managed instance](https://use.hoop.dev) or [deploy your own hoop.dev instance](/setup/deployment/overview)
* You must be your account administrator to perform the following actions

- At least one database connection configured
- Admin access to create guardrails

### Step 1: Create a Guardrail

<Steps>
  <Step title="Navigate to Guardrails">
    Go to **Manage > Guardrails** in the Web App
  </Step>

  <Step title="Create New Guardrail">
    Click **Create New Guardrail** in the top-right corner
  </Step>

  <Step title="Set Basic Information">
    * **Name:** `block-unsafe-updates`
    * **Description:** `Blocks UPDATE/DELETE without WHERE clause`
  </Step>
</Steps>

### Step 2: Add an Input Rule

In the **Input Rules** section:

1. Click **Add Rule**
2. Select **Pattern Match (Regex)**
3. Enter the pattern:

```regex theme={null}
(?i)(UPDATE|DELETE)\s+\w+\s+(SET|FROM)(?!.*WHERE)
```

4. Set **Action** to **Block**
5. Enter the error message: `Blocked: UPDATE/DELETE requires a WHERE clause`

<Note>
  The `(?i)` makes the pattern case-insensitive, so it catches `update`, `UPDATE`, and `Update`.
</Note>

### Step 3: Assign to Connections

1. In the **Connections** section, select which connections this guardrail applies to
2. Choose your production database connections
3. Click **Save**

### Step 4: Test the Guardrail

Try running an unsafe query:

```bash theme={null}
hoop exec prod-db -i "UPDATE users SET status = 'inactive'"
```

Expected output:

```
Error: Guardrail violation
Rule: block-unsafe-updates
Message: Blocked: UPDATE/DELETE requires a WHERE clause
```

Now try with a WHERE clause:

```bash theme={null}
hoop exec prod-db -i "UPDATE users SET status = 'inactive' WHERE id = 123"
```

This query executes normally because it includes a WHERE clause.

***

## Common Guardrail Recipes

### Recipe 1: Block Destructive DDL

Prevent accidental schema changes in production.

**Pattern:**

```regex theme={null}
(?i)^\s*(DROP|TRUNCATE|ALTER)\s+(TABLE|DATABASE|INDEX|SCHEMA)
```

**Action:** Block

**Message:** `DDL commands are blocked. Use a migration tool or request elevated access.`

**What it catches:**

* `DROP TABLE users`
* `TRUNCATE TABLE orders`
* `ALTER TABLE customers DROP COLUMN email`

### Recipe 2: Read-Only Access

Block all write operations for analyst or read-only user groups.

**Patterns (create separate rules for each):**

| Pattern                            | Blocks            |
| ---------------------------------- | ----------------- |
| `(?i)^\s*INSERT\s+INTO`            | INSERT statements |
| `(?i)^\s*UPDATE\s+`                | UPDATE statements |
| `(?i)^\s*DELETE\s+FROM`            | DELETE statements |
| `(?i)^\s*(CREATE\|DROP\|ALTER)\s+` | DDL commands      |

**Action:** Block

**Message:** `This connection is read-only. Write operations are not permitted.`

<Tip>
  Apply this guardrail only to connections used by read-only groups, not to admin connections.
</Tip>

### Recipe 3: Block SELECT \* on Large Tables

Prevent queries that could return millions of rows.

**Pattern:**

```regex theme={null}
(?i)SELECT\s+\*\s+FROM\s+(orders|logs|events|transactions)(?!.*LIMIT)
```

**Action:** Block

**Message:** `SELECT * without LIMIT is blocked on large tables. Add a LIMIT clause or select specific columns.`

**What it catches:**

* `SELECT * FROM orders` (blocked)
* `SELECT * FROM orders LIMIT 100` (allowed)
* `SELECT id, status FROM orders` (allowed)

### Recipe 4: Prevent Credential Access

Block queries that might expose passwords or secrets.

**Pattern:**

```regex theme={null}
(?i)SELECT\s+.*\bFROM\s+\w*users\w*.*\b(password|secret|token|api_key)\b
```

**Action:** Block

**Message:** `Queries selecting credential columns are blocked. Use the appropriate service to manage credentials.`

### Recipe 5: Require LIMIT on All Queries

Warn users when they forget to add LIMIT.

**Pattern:**

```regex theme={null}
(?i)^\s*SELECT\s+(?!.*\bLIMIT\b).*FROM
```

**Action:** Warn

**Message:** `Consider adding a LIMIT clause to prevent large result sets.`

<Note>
  Use **Warn** instead of **Block** when you want to educate users without stopping their work.
</Note>

### Recipe 6: Block Specific Table Access

Restrict access to sensitive tables like `salaries` or `api_keys`.

**Pattern:**

```regex theme={null}
(?i)\b(FROM|JOIN|INTO|UPDATE)\s+(salaries|api_keys|credentials|secrets)\b
```

**Action:** Block

**Message:** `Access to this table is restricted. Contact your administrator for access.`

***

## Testing Guardrails Safely

<Warning>
  Always test guardrails before applying them to production connections.
</Warning>

### Testing Process

<Steps>
  <Step title="Create a Test Connection">
    Create a separate connection to the same database (e.g., `prod-db-test`)
  </Step>

  <Step title="Apply the Guardrail">
    Assign the guardrail only to the test connection
  </Step>

  <Step title="Run Test Queries">
    Test both queries that should be blocked and queries that should pass
  </Step>

  <Step title="Verify Results">
    Confirm the guardrail blocks what it should and allows what it should
  </Step>

  <Step title="Apply to Production">
    Once verified, add production connections to the guardrail
  </Step>
</Steps>

### Test Cases to Run

For each guardrail, test:

1. **Should block:** A query that matches the pattern exactly
2. **Should allow:** A similar query that doesn't match
3. **Edge cases:** Queries with different casing, extra whitespace, or variations

***

## Viewing Blocked Queries

When a guardrail blocks a query, it's logged in Sessions.

<Steps>
  <Step title="Go to Sessions">
    Navigate to **Sessions** in the sidebar
  </Step>

  <Step title="Filter by Status">
    Use the filter to show only **Blocked** sessions
  </Step>

  <Step title="View Details">
    Click any session to see:

    * The query that was blocked
    * Which guardrail rule triggered
    * The error message shown to the user
    * Timestamp and user information
  </Step>
</Steps>

***

## Emergency Bypass

If a legitimate query is blocked and needs to run immediately:

### Option 1: Temporarily Disable the Guardrail

1. Go to **Manage > Guardrails**
2. Find the blocking guardrail
3. Toggle it to **Disabled**
4. Run your query
5. Re-enable the guardrail immediately after

<Warning>
  Document any emergency bypass in your incident log. Re-enable the guardrail as soon as possible.
</Warning>

### Option 2: Refine the Pattern

If the guardrail is catching legitimate queries, update the pattern:

1. Go to **Manage > Guardrails > \[guardrail name]**
2. Edit the rule that's causing issues
3. Refine the regex to be more specific
4. Save and test

### Option 3: Add an Exception

For specific users or groups that need to bypass certain rules:

1. Create a new connection without the guardrail
2. Restrict access to that connection to specific groups
3. Use this "elevated" connection for exceptional cases

***

## Troubleshooting

### Guardrail Not Blocking Expected Queries

**Check:**

1. **Guardrail is enabled** - Verify the toggle is on in Manage > Guardrails
2. **Connection is assigned** - Check that the connection is in the guardrail's connection list
3. **Pattern syntax is valid** - Test your regex at [regex101.com](https://regex101.com)
4. **Case sensitivity** - Add `(?i)` at the start for case-insensitive matching

**Common pattern mistakes:**

| Problem                  | Bad Pattern  | Fixed Pattern                          |
| ------------------------ | ------------ | -------------------------------------- |
| Missing case-insensitive | `DROP TABLE` | `(?i)DROP TABLE`                       |
| Missing word boundaries  | `DELETE`     | `\bDELETE\b`                           |
| Anchoring too strict     | `^DROP`      | `^\s*DROP` (allows leading whitespace) |

### False Positives (Legitimate Queries Blocked)

**Problem:** Pattern is too broad

**Example:**

* Pattern: `DROP`
* Blocks: `SELECT * FROM drop_shipped_orders` (has "DROP" in table name)

**Fix:** Use word boundaries and be specific:

```regex theme={null}
(?i)^\s*DROP\s+(TABLE|DATABASE|INDEX)
```

### Performance Impact

Guardrails add minimal latency (typically 1-5ms per query). If you notice slowdowns:

1. **Reduce rule count** - Combine similar rules where possible
2. **Simplify patterns** - Complex regex with backtracking is slower
3. **Use Input Rules** - Input rules are faster than Output rules

***

## Guardrails vs Other Features

| Feature               | Purpose                            | When to Use                            |
| --------------------- | ---------------------------------- | -------------------------------------- |
| **Guardrails**        | Block queries based on patterns    | Prevent dangerous operations           |
| **Live Data Masking** | Redact sensitive data in output    | Protect PII in query results           |
| **Access Requests**   | Require approval for access        | Time-limited or command-level approval |
| **Access Control**    | Control who can access connections | Restrict connection visibility         |

These features work together. For example:

* Guardrails block `DROP TABLE` commands
* Live Data Masking redacts SSN in `SELECT` results
* Access Requests require approval before connecting
* Access Control limits who sees the connection

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Guardrails Configuration" icon="gear" href="/setup/configuration/guardrails-configuration">
    Detailed configuration options and rule syntax
  </Card>

  <Card title="Live Data Masking" icon="mask" href="/learn/features/live-data-masking">
    Automatically redact sensitive data in query results
  </Card>

  <Card title="Access Requests" icon="clock" href="/learn/features/access-requests/jit">
    Require approval for access to sensitive connections
  </Card>

  <Card title="Session Recording" icon="video" href="/learn/features/session-recording">
    Audit all query executions including blocked queries
  </Card>
</CardGroup>
