Overview
Clients dial a Sidecar listener; each listener forwards to exactly one upstream. The Sidecar makes one outbound HTTPS connection to the Control Plane, which reads and writes its own PostgreSQL database.
- The Sidecar connects outbound. The Control Plane never opens a connection to a Sidecar, so a Sidecar host needs no inbound firewall rule — including behind NAT, in a private subnet, or on a laptop.
- User traffic stays on the Sidecar. Statements, result sets and audit records are never sent to the Control Plane. The Control Plane sends configuration out and receives a periodic check-in.
- Only the database holds state. The Sidecar keeps per-connection state and the Control Plane keeps none, so durable state lives in PostgreSQL alone.
A Control Plane outage is not a data-path outage. A Sidecar holds its configuration in memory and keeps serving traffic under the configuration it last received. A failed heartbeat changes nothing.
Components
Sidecar
A single binary that sits between a client and one or more protected resources. It decodes the wire protocol, applies the rules it holds, and decides what reaches the resource and what comes back. Guardrails, data masking and audit all run here.Control Plane
Serves the HTTP API and the web app, and answers a Sidecar that asks for its configuration. It carries no user traffic: there is no gRPC listener, there are no protocol proxies, and it holds no sessions.PostgreSQL
Stores organizations, users, Sidecar registrations, configuration documents and rule sets. It is the Control Plane’s only stateful dependency.Protected resources
The databases, APIs and hosts behind a Sidecar. They are unchanged by the deployment and keep their own ports, credentials and TLS settings.Sidecar Connectivity
A Sidecar retrieves its configuration from the Control Plane over HTTP(S). The exchange is an ordinary request and response: nothing is tunnelled, and no bidirectional connection is held open.The Sidecar sends a handshake with its token, loads the configuration it receives, and repeats the exchange as a heartbeat every 60 seconds.
Set the Control Plane’s address in the Sidecar’s configuration file. The token is not written here; it is passed as
--token or HOOP_SIDECAR_TOKEN.
config.yaml
Because this is plain HTTPS to one hostname on one port, an egress proxy or a TLS-terminating gateway carries it without special handling. Neither side needs the other’s physical address.
Database Connectivity
The Control Plane connects to PostgreSQL at startup and keeps all of its state there.GET /api/healthz means the database was reachable and the schema is current. For the same reason, the readiness probe needs a longer initial delay than usual — ten seconds rather than five.
Listeners
A Sidecar binds one listener per upstream, and all listeners run in a single process. Protecting a second resource means adding a second entry tolisteners, not starting a second Sidecar.
Each listener binds either a TCP port or a unix socket. There are no reserved or default data ports: choose any address the host has free. On the client side, the only change is the host and port in a connection string.
config.yaml
Listeners are configured independently of one another:
- Transports are independent. Moving
billingto a socket leavesappdbon its TCP port. Guardrails, masking and audit behave the same either way, because the gate reads a connection without asking what kind it is. - Protocols are independent. Any mix of
postgres,mysql,mssql,mongodb,http,ssh,grpc,spannerandbigqueryruns in one process, each with its own rules and its own audit trail. - Rules are per listener. Top-level guardrails and masking are defaults that every listener inherits, and any listener can override them. A strict production database and a permissive staging one can run in the same process.
Scaling and Availability
The Sidecar tier and the Control Plane tier scale for different reasons, and neither holds shared state. Durable state exists only in PostgreSQL, which is where availability has to be configured.Both tiers run multiple instances without coordination. All durable state sits in one database.
Sidecar
Run as many Sidecar instances as your traffic needs and put them behind anything that spreads connections across them — a load balancer, a virtual IP, or DNS. Each instance is an independent proxy: it holds no shared state, reads the same configuration, and connects to the Control Plane on its own. Sessions are per-connection, so an instance that goes away drops the connections it was carrying and no others. Clients reconnect through whatever fronts the tier. Because a Sidecar sits in the data path, replace instances one at a time: start the new one, let it accept connections, then stop the old one. Stopping the whole tier at once closes every open connection with nothing left listening. Capacity scales with concurrent connections and with what each listener does — detection and masking read the response body, and an AI analyzer adds a network call per statement shape. Size an instance against your own connection count rather than a fixed figure.Control Plane
The Control Plane scales horizontally and is stateless HTTP over a shared database. Instances need no coordination, no leader election and no sticky sessions: point each one at the samePOSTGRES_DB_URI and place them behind an ordinary HTTP load balancer.
A single instance is enough for most deployments, since the Control Plane carries no user traffic. Add instances when your API traffic calls for it, or to run a second instance in another availability zone or failure domain.
Because the process holds no sessions, instances can be replaced one at a time with nothing to drain. Keep one serving while another restarts and the API stays available throughout.
Summary
A lost Sidecar instance drops only its own connections, and clients reconnect through whatever fronts the tier. A lost Control Plane instance leaves every Sidecar serving the configuration it already holds. A lost database stops a Control Plane from completing a boot and leaves running Sidecars untouched.
Losing the Control Plane therefore degrades management — no configuration changes, no new Sidecar registrations, no web app — but not access. Losing a Sidecar degrades access to the resources behind it, which is why that tier is usually the one running more than one instance.
Health checks
Control Plane —GET /api/healthz on 8009, under API_URL’s path if it has one. Give it a ten-second initial delay: the listener opens only after migrations, the organization bootstrap and the identity provider, so a response means the bootstrap finished.
Sidecar — GET /healthz on the admin listener. It runs only when the configuration names an address for it; there is no default port.
Firewall rules
The complete set of connections a deployment makes:
A listener on a unix socket opens no port, so the first rule does not apply to it. The admin API belongs on loopback, which makes the last rule a local concern rather than a firewall one.
No rule is needed for Control Plane → Sidecar. The Control Plane never opens a connection to a Sidecar, in any topology or at any scale.
Next
Connect a Sidecar
Issue a token, point a Sidecar at the Control Plane, and confirm the handshake.
Install the Control Plane
Docker Compose, Kubernetes and AWS.
Environment Variables
POSTGRES_DB_URI, API_URL, TLS and the identity provider.Sidecar Architecture
What happens inside one connection, from accept to upstream and back.