- On GKE: an app, a job or an agent in the cluster connects to the Sidecar. One Terraform module from the hoop repository installs it.
- From Agent Runtime: an ADK agent on Gemini Enterprise Agent Platform reaches the Sidecar over Private Service Connect and has no route to the database. A template for
agents-clicreates the agent and its Sidecar.
hoop is not on Google Cloud Marketplace yet. Install from the command line, as below. The Marketplace form runs the same module.
The values on this page
Words in capitals are values you replace. The second column is what they are in the demo:Why a client cannot skip the Sidecar
- On GKE. A NetworkPolicy in the database’s namespace admits connections from the Sidecar’s pods only. A connection from any other pod times out. Autopilot clusters enforce NetworkPolicies. A Standard cluster needs Dataplane V2 or network policy enforcement.
- From Agent Runtime. The agent’s network attachment reaches the Sidecar’s internal load balancer. The same NetworkPolicy keeps the agent from the database.
On GKE
Requirements
- A GKE cluster on x86 nodes, where you can create namespaces, and a Postgres database that its pods reach. For example, a Postgres Deployment in the same cluster. To try it with neither, see A demo cluster and database.
-
gcloudwith thegke-gcloud-auth-plugincomponent,kubectl, and Terraform 1.5 or later:CLUSTERandLOCATIONare your cluster’s name and region. With no cluster yet, skip this line: the demo below runs it for you. - hoop 1.281.0 or later. Every release is on GitHub.
A demo cluster and database
A demo cluster and database
A new Autopilot cluster in The rest of this page uses its values:
us-east1, the region where agents-cli deploys agents, and a Postgres Deployment with invented customers. The database is appdb with a table customers (id, name, email, plan) and a login agent that can only read it, which is what the Agent Runtime template’s agent expects.appdb.yaml
upstream appdb.default.svc.cluster.local:5432, the labels app: appdb, the login app for writes and agent for reads. Change the passwords before you use it for more than a test.1
Write main.tf
In an empty directory, replace the capitals:
RELEASE with a hoop release, the project, the cluster and its location, and upstream with your database’s host:port.main.tf
- Keep both slashes in
source: Terraform reads//as the end of the repository and the start of the module’s folder in it. guardrail_preset = "read-only"adds one rule that refuses writes. Without it, the Sidecar only records.goog_cm_deployment_namenames the install. The Sidecar’s Deployment isappdb-sidecar-hoopsidecarand the Service clients use isappdb-sidecar-hoopsidecar-lanes, in the namespacehoop-sidecar.- The module’s other variables are in its README.
2
Install the Sidecar
lane ready line holds "enforcing":true and "rules":1.3
Lock the database to the Sidecar
Set the namespace and the labels of your database’s pods, then apply:A selector that matches nothing locks nothing, and
database-policy.yaml
kubectl reports no error. NetworkPolicies add up: another policy that selects the database’s pods and admits other clients keeps them in. List them with kubectl -n default get networkpolicy. Verify checks it.4
Point your clients at the Sidecar
Clients connect to
appdb-sidecar-hoopsidecar-lanes.hoop-sidecar.svc.cluster.local, port 5432, with the same login and database as before. The Sidecar passes the login through.Verify
From a pod in the cluster, replaceUSER, PASSWORD, DATABASE and the table. In the demo: app, change-me-app, appdb. Use a login that can write, so a refused write shows the Sidecar at work, not the database:
kubectl run can print a warning that it set the pod’s CPU: it is harmless.
The audit trail is the Sidecar’s log. Each statement is one JSON line, and a refused one has
"kind":"violation" and the rule. Trimmed to its main fields:
What the rule refuses, and what it misses
read-only refuses the statements that the Sidecar’s classifier reads as insert, update, delete, create, drop, alter, truncate, grant, revoke, call (DO, CALL, EXECUTE) or other. Today the classifier:
- reads some writes as reads, so they pass:
EXPLAIN ANALYSEandEXPLAIN ("analyze")run the statement they explain,SELECT ... INTOcreates a table, andSELECT f()runs whateverfwrites. Give the client’s login only the privileges it needs:SELECTon its tables, noCREATE. - reads
SAVEPOINT,LISTENandDISCARD ALLasother, so they are refused. A client that opens a nested transaction on a read path loses its connection.
config_source = "document" and write the config. See the module’s README and Guardrail Rules.
From Agent Runtime
Thegcp-agent-runtime/postgres template is an ADK agent whose SQL tool reaches Postgres through the Sidecar. The Sidecar runs on GKE behind an internal load balancer, and the agent reaches it over PSC-I. The steps below are checked on Agent Runtime; the template’s README explains each file.
You need uv, agents-cli 1.11 or later (uv tool install google-agents-cli), and the requirements of On GKE. Keep the agent, the network attachment and the GKE cluster in one region: agents-cli deploy uses us-east1, where the demo cluster runs.
Run the steps in one shell: they share the variable ATTACHMENT.
The template installs its own Sidecar, named appdb-sidecar like the one in On GKE. On a cluster where you followed On GKE, run its Clean up first, or use another cluster.
1
Create the project
RELEASEis a hoop release tag, 1.281.1 or later. Older tags install an older Sidecar.--root-agent-name support_agentis required:app/agent.pydefines that agent.Gemini platform connection failed: Status 404 ... gemini-2.5-flashis harmless. agents-cli tests your login with its own default model; the agent uses its own model (gemini-3.8-flash, set byAGENT_MODEL).
2
Install the Sidecar and lock the database
Set your database as The last command prints the Sidecar’s internal IP, the agent’s
upstream in deploy/sidecar/sidecar.yaml, then:SIDECAR_IP. Set the podSelector in deploy/sidecar/database-policy.yaml to your database pods’ labels (app: appdb in the demo), then apply it in the database’s namespace. The file names no namespace:3
Create the network attachment
In the Sidecar’s VPC and the agent’s region, and let the Agent Platform service agent use it:
- Keep
192.168.10.0/28if it is free in your VPC. The subnet must reach the Sidecar’s IP on port15432. - The name is new each run. On an attachment whose name was deleted and created again in the project, Agent Runtime failed to deploy the agent and logged no cause.
- If
roles createsays the role exists in a deleted state, rungcloud iam roles undelete agentRuntimePsci --project PROJECT. If it says the role already exists, go on.
4
Deploy the agent
APPDB_PASSWORDis theagentlogin’s password,change-me-agentin the demo.- A project with no organization uses
agents.global.proj-PROJECT_NUMBER.system.id.goog.
5
Ask the agent
DELETE comes back as hoop: this agent may read appdb, not change it, and the Sidecar’s log holds the violation with the ADK session id in postgres.option.adk.session.id:deployment_metadata.json in the project directory records the agent’s id. After a time with no requests, the agent stops. The first call starts it again and can fail with Read timed out: run it again.*.run.app. A VPC Service Controls perimeter does.
Other databases and APIs
The module fronts more than Postgres. With the simple fields, setprotocol to mysql, mssql, mongodb, oracle, clickhouse or http: the Sidecar records every statement or request and refuses nothing. The read-only preset is for Postgres only. On the other protocols, the same rule refuses some reads and misses some writes.
To refuse something on another protocol, write the Sidecar’s config yourself. For example, a MySQL database where no client may drop or empty a table:
main.tf
sidecar.yaml
admin.listenmust be0.0.0.0:19000.- The Sidecar’s Service publishes each listener’s port. Clients connect to
appdb-sidecar-hoopsidecar-lanes.hoop-sidecar.svc.cluster.local:13306. - Lock the database the same way, with its port in the NetworkPolicy.
- One guardrail rule and one masking rule are free. One rule can name several operations.
Limits
- One database per install. The simple fields front one Postgres database. For more listeners, use a config document.
- Plain text inside the VPC. The listener has no TLS in the simple fields, so the module offers an internal load balancer and never a public one.
- The lock covers a database in the cluster. A database outside it, such as Cloud SQL or a VM, needs its own firewall rule. This page does not cover it.
- The audit trail is the pod’s log. Cloud Logging keeps it for its log bucket’s retention, 30 days by default.
Clean up
In the directory ofmain.tf:
AGENT_IDis indeployment_metadata.json. In a new shell, setATTACHMENTagain:gcloud compute network-attachments list --filter='name~^agent-psci-'lists it.- A project with no organization uses
agents.global.proj-PROJECT_NUMBER.system.id.googinAGENT.
Network Attachment with connected endpoints cannot be deleted. Run the two attachment lines again later.
The demo cluster: gcloud container clusters delete hoop-demo --location us-east1.
Free and Enterprise
Free tier: one Data Masking rule and one Guardrail per Sidecar are free, forever. Running more than one rule per feature, or managing rules centrally across Sidecars, requires Enterprise.
-
No license, no account. The Sidecar enforces one guardrail rule and one masking rule.
read-onlyis the guardrail rule. A config over the cap does not start. -
A license lifts the caps. Pass it in the module’s
licensevariable. See Licensing. -
The Control Plane needs an Enterprise license. It adds approvals, shared rule sets and one view of many Sidecars. The module’s
control_planemode connects the Sidecar to it. See Control Plane. -
The Sidecar logs its rule limits when it starts:
Next
Postgres
What the postgres listener inspects and refuses.
Guardrail Rules
operation, table and pattern_match rules.Kubernetes
The Sidecar’s Helm chart on any cluster.
Amazon Bedrock AgentCore
The same idea for an agent on AWS.