Model or dataset
hoophq/hoop avatar
hoophq/hoop

hoop.dev: A Wire-Level Sidecar That Masks Data and Blocks Destructive SQL Before Agents Reach Your Database

One gateway in front of every protocol. Same policy across MCP, LLMs, databases and containers. Wire-level enforcement at under 5ms.

812 stars66 forksGoMIT

At a glance

What is it?
hoop.dev puts one Go binary between your agents and PostgreSQL, SQL Server or HTTP, rewriting responses in memory and refusing statements at the protocol level. The sidecar works today; the control plane that would manage a fleet of them is mostly unbuilt, and the README says so.
Who is it for?
Adopt hoop.dev if you already run agents against PostgreSQL, SQL Server or HTTP and want statement-level guardrails plus response masking without changing agent code, which the README reduces to changing a port in the connection string. Do not adopt it if you need a managed fleet today: the README lists sidecar token issuance, the review queue and configuration push as not built, so each sidecar still reads its own file.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The Problem: Agents Get Credentials and Then Get Creative

An agent with a database connection is an agent that can run anything the connection allows. The README frames the tradeoff plainly: data and context make agents useful, and runtime controls make them safe. The failure it targets is not a malicious agent but a fast one. A statement like DELETE FROM customers WHERE id = 1 arrives at the database in milliseconds, well before any human review process can react, and a static allowlist cannot read intent from syntax alone.

The sidecar is aimed at platform and security engineers who already have agents talking to internal data stores and want a control point that does not require rewriting the agent. The README is explicit that the agent never knows the sidecar exists: no SDK, no prompt changes, no agent-side config. That constraint shapes everything else in the design. Control has to live where the bytes are, because that is the only place the agent cannot route around.

Masking Rewrites the Response, Not the Request

The masking mechanism is narrow by design. According to the README, masking rewrites sensitive values in the response in memory before they reach the client, and the request itself is never touched. The worked example shows a SELECT name, email FROM customers returning Ada Lovelace and Grace Hopper with their email addresses replaced by [REDACTED:EMAIL_ADDRESS].

That distinction matters when you reason about what masking does and does not protect. The query still runs against the real table. If the agent's own logic depends on the true value, for example joining on an email it just read, the redacted form is what it gets back. Masking here is an output filter, not a query rewriter, and the README does not describe any column-level access control or row filtering. The rule syntax shown is a list of entries with name, entity and strategy, where the example uses entity: EMAIL_ADDRESS with strategy: redact. The README does not enumerate the available entities or strategies beyond that one, so treat the rule vocabulary as something to confirm against the repository before you plan a rollout.

Guardrails Return a Real Protocol Error, Not a Dropped Connection

The guardrail path is the more interesting half. An ordered deny list is checked against every statement, and destructive statements never reach the database. The part worth noting is the feedback channel: the README says the client reads a real pgwire error carrying the message you wrote in the config, shown as FATAL: destructive statements are not permitted. The stated rationale is that the agent reads why it was refused instead of guessing at a dropped connection.

This is a deliberate choice with consequences in both directions. A structured error lets a capable agent adapt, retry with a narrower statement, or surface the refusal to a human. It also tells the agent exactly which rule fired, which is fine for an internal tool and less fine if you would rather not advertise your policy boundaries. The README does not discuss error verbosity as a configurable surface. The rule shape shown is name, type: operation, operations: [drop, delete, truncate], and a message string. Ordering is called out as significant, which implies first-match semantics, though the README does not spell out what happens on overlapping rules.

Session Analyzer Runs Local Rules Before Spending a Model Call

The Session Analyzer is described as an agent that scores every action's intent and syntax for risk before it executes. The README gives one implementation detail that changes its cost profile: local rules run first, so a statement a guardrail already refuses never costs a model call. That ordering is the sensible one, and it means the analyzer only sees traffic that survived the cheaper checks.

What the README does not give is the analyzer's own configuration surface, its latency contribution, or which model it calls. The headline claim of enforcement at under 5ms appears in the repository description, not in the README body, and nothing in the supplied material says whether that figure covers the analyzer path or only the masking and guardrail checks. If sub-5ms matters to your workload, that is a number to measure yourself rather than assume. The protocol table marks the Session Analyzer as available on PostgreSQL, SQL Server and HTTP, with HTTP requiring http.capture_body, which suggests the analyzer needs the request body to score an HTTP action.

Running It: One Binary, One YAML File, One Changed Port

Installation on macOS goes through a Homebrew tap: brew tap hoophq/brew https://github.com/hoophq/brew.git followed by brew install hoop. The README also references a Docker image at hoophq/hoop, though it does not give a run command for it.

Startup is hoop start sidecar --config config.yaml. There is a dry-run mode, hoop start sidecar --config config.yaml --validate, which checks the config and exits. Given the config surface, that flag is the first thing to run.

The config file has four top-level blocks in the example. log_level: info. An admin block listening on 127.0.0.1:19000 with /healthz, /stats, /config and /events. A mask block with enabled: true and a rules list. A policy block with enforce: true. And a listeners list, where each entry has name, protocol (postgres, mssql or http), listen for the client-facing address, upstream for the real resource, and connection. The example listener binds 127.0.0.1:15432 and forwards to 127.0.0.1:5432.

One thing in the README needs attention before you copy it. The example contains both a policy block and a guardails block, each with a rule named no-destructive-sql, and the message field appears under the misspelled guardails key. Whether that is a typo in the README or the actual key name is not something the supplied material resolves. Validate before you trust either spelling.

What the Control Plane Does Not Do Yet

The README is unusually direct about the gap between the sidecar and the control plane. The control plane is the admin surface for connecting sidecars, setting Data Masking, Guardrails, Session Analyzer and Review rules once for all of them, and working the Reviews queue. Its own status table lists Guardrails, Data Masking, Session Analyzer and Review rules as built, with configuration living in the control plane, plus Slack for review delivery and Administrators as built.

Three rows are marked not built: sidecar fleet token issuance, resources and liveness; the review queue's approve, reject and retry actions; and pushing configuration to the fleet. The last one is the operational sting. Each sidecar still reads its own file, so at any real fleet size you are managing N YAML files by hand regardless of what the control plane UI shows you. The README also notes that routes with no backend behind them say so and name the work they wait on, rather than rendering an empty table.

To run the control plane locally: make run-dev-postgres, then make run-dev-control-plane for the backend on port 8019, then cd webapp_v2 && API_URL=http://localhost:8019 npm run dev for the UI on 5173. The webapp renders as the control plane when the backend reports application_mode: "control-plane". That is a development workflow, not a deployment story, and the README does not offer one.

Where It Sits Against PAM, MCP Gateways and Sandboxes

The README positions the sidecar against three categories it says it is not. A PAM decides who connects and is done once a user is connected; hoop.dev controls what every statement does and what comes back. An MCP gateway brokers one interface, while the sidecar sits on the wire underneath and covers protocols an MCP server never speaks. A sandbox isolates the agent, while the sidecar governs the connection between the agent and the resource.

The PAM comparison is the sharpest one, because it names a real architectural difference rather than a feature gap. If your requirement is credential vaulting and session recording for human operators, a PAM does that and this does not. If your requirement is that a DELETE never lands no matter which agent issued it, statement-level inspection is the layer that answers it. The MCP comparison is also concrete: an MCP gateway only sees traffic that goes through MCP, so an agent that opens a raw Postgres connection bypasses it entirely. A wire-level sidecar does not have that hole, provided the agent's connection string points at the sidecar port.

That proviso is the whole ballgame. The README's claim that agents change one thing, the port in their connection string, assumes you control the connection string. An agent that discovers its database endpoint some other way, or one that ships with a hardcoded address, is not covered.

Maintenance Cost, Licence and What to Check First

The project is MIT licensed, which is permissive and places few obligations on how you redistribute or modify it. This is not legal advice; read the LICENSE file in the repository for the actual terms.

Release cadence is high. Three releases landed within roughly two days in September 2026, 1.161.1, 1.162.0 and 1.163.0, and the repository shows continued pushes into the same month. For an operator, a fast minor-version cadence on a component sitting inline with production database traffic means you need a pinning and upgrade-testing habit, and the README does not describe a stability policy, a support window, or a compatibility guarantee across versions. The --validate flag gives you a cheap pre-upgrade check on config compatibility, which is a start but not a substitute for testing against a staging database.

The repository layout is worth knowing before you commit: sidecar/ holds the inspection core, client/ holds the CLI, gateway/ runs a REST API on 8009 and gRPC on 8010, agent/ runs on your infrastructure and dials the gateway, agentrs/ is a Rust binary for the RDP proxy and TLS termination, and tunnel/ is a client-side tunnel daemon. The README describes hoop.dev as having begun as a gateway for human access to infrastructure, with the sidecar and control plane as where the product is going. That history explains why the repository is larger than the sidecar story alone, and it means you should be clear about which of those components you are actually deploying. For a single-team deployment against Postgres, the sidecar plus one YAML file is the whole footprint. For anything resembling a fleet, the missing configuration push is the thing to plan around.

Editorial conclusion

Adopt hoop.dev if you already run agents against PostgreSQL, SQL Server or HTTP and want statement-level guardrails plus response masking without changing agent code, which the README reduces to changing a port in the connection string. Do not adopt it if you need a managed fleet today: the README lists sidecar token issuance, the review queue and configuration push as not built, so each sidecar still reads its own file. Before rolling it out, run hoop start sidecar --config config.yaml --validate against your real config, and reconcile the two rule blocks the README shows, since policy and guardails both appear with a no-destructive-sql rule and the misspelled key is the one carrying the message field.

Official sources

  1. hoophq/hoop on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Community notes

Community notes