> ## Documentation Index
> Fetch the complete documentation index at: https://hoopdev-docs-control-plane-owns-listeners.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Runtime control for agents

export const HeroAnimation = () => {
  const QUERY = 'SELECT * FROM customers LIMIT 4;';
  const ROWS = [{
    id: 1001,
    name: 'J. Smith',
    ssn: '123-45-6789',
    email: 'jsmith@acme.com'
  }, {
    id: 1002,
    name: 'M. Rodriguez',
    ssn: '234-56-7890',
    email: 'mrodriguez@acme.com'
  }, {
    id: 1003,
    name: 'A. Chen',
    ssn: '345-67-8901',
    email: 'achen@acme.com'
  }, {
    id: 1004,
    name: 'S. Patel',
    ssn: '456-78-9012',
    email: 'spatel@acme.com'
  }];
  const [cycle, setCycle] = useState(0);
  const [typedLen, setTypedLen] = useState(0);
  const [visibleRows, setVisibleRows] = useState(0);
  const [phase, setPhase] = useState('typing');
  const timers = useRef([]);
  function T(fn, delay) {
    const id = setTimeout(fn, delay);
    timers.current.push(id);
  }
  useEffect(() => {
    timers.current.forEach(clearTimeout);
    timers.current = [];
    setTypedLen(0);
    setVisibleRows(0);
    setPhase('typing');
    let t = 0;
    for (let i = 1; i <= QUERY.length; i++) {
      t += 38;
      const ci = i;
      T(() => setTypedLen(ci), t);
    }
    t += 350;
    for (let r = 1; r <= ROWS.length; r++) {
      const cr = r;
      T(() => setVisibleRows(cr), t);
      t += 95;
    }
    t += 900;
    T(() => setPhase('flashing'), t);
    t += 480;
    T(() => setPhase('masked'), t);
    t += 3200;
    T(() => setPhase('resetting'), t);
    t += 550;
    T(() => setCycle(c => c + 1), t);
    return () => timers.current.forEach(clearTimeout);
  }, [cycle]);
  const isMasked = phase === 'masked';
  const isFlashing = phase === 'flashing';
  const isResetting = phase === 'resetting';
  const showRows = phase !== 'typing' && phase !== 'resetting';
  return <div className="ha-root" aria-hidden="true">
      <div className={`mock-container ha-mock${isResetting ? ' ha-fadeout' : ''}`}>

        {}
        <div className="mock-toolbar" style={{
    justifyContent: 'space-between'
  }}>
          <div style={{
    display: 'flex',
    gap: 6
  }}>
            <div className="mock-dot" />
            <div className="mock-dot" />
            <div className="mock-dot" />
          </div>
          <span className="ha-wintitle">hoop — postgres:prod</span>
          <span style={{
    width: 48
  }} />
        </div>

        {}
        <div className="ha-querybar">
          <span className="ha-prompt">psql&gt; </span>
          <span className="ha-querytext">{QUERY.slice(0, typedLen)}</span>
          {!isMasked && !isResetting && <span className="ha-cursor" />}
        </div>

        {}
        <div className="ha-tablewrap">
          <table className="ha-table">
            <thead>
              <tr>
                <th className="ha-th">id</th>
                <th className="ha-th">name</th>
                <th className="ha-th">ssn</th>
                <th className="ha-th">email</th>
              </tr>
            </thead>
            <tbody>
              {ROWS.map((row, i) => <tr key={row.id} className="ha-tr" style={{
    opacity: showRows && i < visibleRows ? 1 : 0,
    transition: 'opacity 0.2s ease'
  }}>
                  <td className="ha-td ha-dim">{row.id}</td>
                  <td className="ha-td ha-bright">{row.name}</td>
                  <td className={`ha-td ha-sensitive${isFlashing ? ' ha-flash' : ''}${isMasked ? ' ha-masked' : ''}`}>
                    {isMasked ? `***-**-${row.ssn.slice(-4)}` : row.ssn}
                  </td>
                  <td className={`ha-td ha-sensitive${isFlashing ? ' ha-flash' : ''}${isMasked ? ' ha-masked' : ''}`}>
                    {isMasked ? '****@****.com' : row.email}
                  </td>
                </tr>)}
            </tbody>
          </table>
        </div>

        {}
        <div className={`ha-notice${isMasked ? ' ha-notice-visible' : ''}`}>
          <span className="ha-notice-dot" />
          <span>2 columns masked · SSN · email</span>
        </div>

      </div>

      <style>{`
        .ha-root {
          position: relative;
          width: 100%;
          max-width: 540px;
        }

        .ha-mock {
          border-radius: 12px;
          overflow: hidden;
          transition: opacity 0.4s ease;
        }

        .ha-fadeout {
          opacity: 0;
        }

        .ha-wintitle {
          font-family: var(--mono);
          font-size: 11px;
          color: rgba(var(--sand-100-rgb), 0.2);
        }

        /* Query bar */
        .ha-querybar {
          display: flex;
          align-items: center;
          padding: 9px 16px;
          background: rgba(var(--sand-100-rgb), 0.03);
          border-top: 1px solid rgba(var(--sand-100-rgb), 0.06);
          border-bottom: 1px solid rgba(var(--sand-100-rgb), 0.06);
          min-height: 38px;
        }

        .ha-prompt {
          font-family: var(--mono);
          font-size: 12px;
          color: rgba(var(--warm-gold-rgb), 0.7);
          flex-shrink: 0;
        }

        .ha-querytext {
          font-family: var(--mono);
          font-size: 12px;
          color: rgba(var(--sand-100-rgb), 0.7);
        }

        .ha-cursor {
          display: inline-block;
          width: 6px;
          height: 13px;
          background: rgba(var(--sand-100-rgb), 0.5);
          margin-left: 1px;
          vertical-align: middle;
          animation: ha-blink 1s step-end infinite;
        }

        @keyframes ha-blink {
          0%, 100% { opacity: 1; }
          50%       { opacity: 0; }
        }

        /* Table */
        .ha-tablewrap {
          overflow-x: auto;
          padding-bottom: 4px;
        }

        .ha-table {
          width: 100%;
          border-collapse: collapse;
          font-family: var(--mono);
          font-size: 12px;
        }

        .ha-th {
          text-align: left;
          padding: 7px 16px;
          color: rgba(var(--sand-100-rgb), 0.25);
          font-size: 10px;
          font-weight: 500;
          text-transform: uppercase;
          letter-spacing: 0.06em;
          border-bottom: 1px solid rgba(var(--sand-100-rgb), 0.04);
        }

        .ha-tr:not(:last-child) td {
          border-bottom: 1px solid rgba(var(--sand-100-rgb), 0.04);
        }

        .ha-td {
          padding: 7px 16px;
          transition: color 0.25s ease, background 0.25s ease;
        }

        .ha-dim       { color: rgba(var(--sand-100-rgb), 0.3);  }
        .ha-bright    { color: rgba(var(--sand-100-rgb), 0.6);  }
        .ha-sensitive { color: rgba(var(--sand-100-rgb), 0.5);  }

        .ha-flash {
          color: var(--warm-gold) !important;
          background: rgba(var(--warm-gold-rgb), 0.1);
        }

        .ha-masked {
          color: var(--warm-gold) !important;
          font-weight: 600;
        }

        /* Masking notice */
        .ha-notice {
          display: flex;
          align-items: center;
          gap: 7px;
          padding: 8px 16px;
          border-top: 1px solid rgba(var(--sand-100-rgb), 0.06);
          font-family: var(--sans);
          font-size: 11px;
          color: rgba(var(--warm-gold-rgb), 0.75);
          opacity: 0;
          transform: translateY(4px);
          transition: opacity 0.3s ease, transform 0.3s ease;
        }

        .ha-notice-visible {
          opacity: 1;
          transform: translateY(0);
        }

        .ha-notice-dot {
          width: 6px;
          height: 6px;
          border-radius: 50%;
          background: var(--warm-gold);
          flex-shrink: 0;
        }
      `}</style>
    </div>;
};

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

Agents and engineers reach real infrastructure with real credentials. Someone has to decide what they can do. Hoop decides it at runtime, per command.

The **Sidecar** is where you start. It is a proxy that runs next to your resource, decodes the wire protocol, and decides what reaches the database and what comes back, blocking destructive statements and masking sensitive values in responses. Nothing else to deploy and no database to run: one binary, one config file.

***

## Install the Sidecar

The Sidecar ships in the `hoop` CLI.

<Tabs>
  <Tab title="MacOS">
    Through homebrew:

    ```bash theme={null}
    brew tap hoophq/brew https://github.com/hoophq/brew.git
    brew install hoop
    ```

    To upgrade it:

    ```bash theme={null}
    brew update && brew upgrade hoop
    ```
  </Tab>

  <Tab title="Linux">
    To install or upgrade:

    ```bash theme={null}
    curl -s -L https://releases.hoop.dev/release/install-cli.sh | sh
    ```
  </Tab>

  <Tab title="Windows">
    <Steps>
      <Step title="Download">
        Download the latest release of [the hoop command line](https://github.com/hoophq/hoop/releases) for your architecture (usually **hoop-windows-amd64**) and save it in the **Downloads** folder.
      </Step>

      <Step title="Extract">
        Extract the file using **winrar** or **winzip**.
      </Step>

      <Step title="Place it in the right folder">
        Move the extracted file `hoop.exe` to the folder `C:\Windows\System32\`.
      </Step>

      <Step title="Check the installation">
        Open a terminal session and run `hoop version`. If it prints a version, you are installed.
      </Step>
    </Steps>
  </Tab>

  <Tab title="Windows WSL">
    1. Install the [Windows Terminal](https://aka.ms/terminal)
    2. Install [Ubuntu WSL](https://ubuntu.com/wsl)
    3. Open the Windows Terminal app
    4. Install and open Ubuntu by typing `ubuntu`. The first run prompts you to create a user.

    ```bash theme={null}
    PS C:\Users\san> ubuntu
    san@DESKTOP-BBQAAEG:~$
    ```

    5. Gain root access:

    ```bash theme={null}
    sudo su -
    ```

    6. Install or upgrade the CLI:

    ```bash theme={null}
    curl -s -L https://releases.hoop.dev/release/install-cli.sh | sh
    ```
  </Tab>

  <Tab title="Manual">
    Check the latest build for your operating system and architecture on the [releases page](https://github.com/hoophq/hoop/releases).
  </Tab>
</Tabs>

***

## Run it

One file describes a Sidecar: where it listens, what it sits in front of, and the rules it applies. This one masks emails in every response and refuses destructive statements.

```yaml config.yaml theme={null}
log_level: info

admin:
  listen: 127.0.0.1:19000        # /healthz /stats /config /events

audit:
  file: "-"                      # JSON lines on stdout
  memory_buffer: 256             # last N events, readable at /events
  query_sessions: 500            # backs /api/sessions

pii:                             # optional: omit it and every entity is enabled
  entities: [EMAIL_ADDRESS, US_SSN, CREDIT_CARD]

mask:                            # one Data Masking rule
  rules:
    - {name: emails, entities: [EMAIL_ADDRESS], strategy: redact}

guardrails:                      # one Guardrail
  rules:
    - name: no-destructive-sql
      type: operation
      operations: [drop, delete, truncate]
      message: destructive statements are not permitted

listeners:
  - name: localdb                # the name audit rows and rules key on
    protocol: postgres           # postgres | mssql | http
    listen: 127.0.0.1:15432      # where clients connect
    upstream: 127.0.0.1:5432     # your real resource
```

<Note>
  This file sits exactly at the free tier: one Data Masking rule, one Guardrail. Both features are free at that size, forever.
</Note>

Start it:

```bash theme={null}
hoop start sidecar --config config.yaml
```

That is the whole setup. Point a client at `127.0.0.1:15432` instead of the database, and every statement it sends is inspected against the rules above.

***

## Next step

<Card title="Quickstart" icon="rocket" href="/introduction/quickstart">
  Watch the rules fire, read the audit trail, and reshape the file: other protocols, more listeners, and a rollout that denies nothing until you say so.

  <br />

  **Start here** <Icon icon="arrow-right" />
</Card>

***

## Core Concepts

<CardGroup cols={2}>
  <Card title="Sidecar" icon="bolt" href="/core-concepts/sidecar">
    The engine. A proxy beside your resource that reads the wire protocol and decides what gets through — with no external dependency.

    <br />

    **Learn more** <Icon icon="arrow-right" />
  </Card>

  <Card title="Control Plane" icon="tower-control" href="/core-concepts/control-plane">
    The brain. Centralizes every Sidecar you run and delivers rule sets to all of them from one place. An Enterprise feature.

    <br />

    **Learn more** <Icon icon="arrow-right" />
  </Card>
</CardGroup>

***

## Features

Two ways a request can travel through the Sidecar, and two controls that apply on either path.

<CardGroup cols={2}>
  <Card title="Direct Access" icon="arrow-right-long" href="/features/direct-access">
    The straight-through path. Guardrails and Data Masking apply inline, deterministically, with no model call and no third party in the chain.

    <br />

    **Learn more** <Icon icon="arrow-right" />
  </Card>

  <Card title="Agentic Access" icon="robot" href="/features/agentic-access">
    The AI Analyzer classifies the statement and the risk level picks a tool: block, allow, review, apply a guardrail, or mask the response.

    <br />

    **Learn more** <Icon icon="arrow-right" />
  </Card>

  <Card title="Data Masking" icon="mask" href="/features/data-masking">
    Rewrite sensitive values in the response, in memory, before they reach the client. Requests are never touched.

    <br />

    **Learn more** <Icon icon="arrow-right" />
  </Card>

  <Card title="Guardrails" icon="shield-halved" href="/features/guardrails">
    An ordered deny list evaluated against every statement, refusing what should never run with a message you wrote.

    <br />

    **Learn more** <Icon icon="arrow-right" />
  </Card>
</CardGroup>
