Skip to content
Sign up GitHub

Security Architecture — CDC as an Air Gap

Audience: CISOs, security architects, platform teams evaluating pg-cdc for AI agent data access.

For deployment-specific hardening, see Least-Privilege Deployment and HIPAA Deployment.

The Problem

Every approach to “give AI agents database access” looks like this:

AI Agent ──── credentials ────> Production Database
(live, mutable, operational)

No matter how many read replicas, connection pools, or row-level policies you add, the agent has a network path to production. One prompt injection, one hallucinated DROP TABLE, one credential leak — and you’re in an incident.

How pg-cdc Solves It

pg-cdc creates a physical air gap between production data and AI consumption:

Production Database
|
| WAL (one-way, append-only)
v
pg-cdc ──> S3 (immutable Parquet)
|
| IAM (no database credentials exist)
v
AI Agent

There is no return path. The agent cannot write to production — not because of a policy, but because the protocol doesn’t exist. The WAL is one-way. Parquet is immutable. S3 has no UPDATE verb. The agent doesn’t have a connection string because there is no connection.

And when an agent needs to act — “cancel my order”, “update address” — it still doesn’t get a return path. It requests a bounded, authorized command through a separate relay that the agent cannot drive. See Taking Action below.

Three Security Zones

+--------------------------------------------------------------+
| PRODUCTION ZONE |
| |
| PostgreSQL (operational, mutable, sensitive) |
| | |
| | WAL (one-way) |
| v |
| pg-cdc (runs in production VPC) |
| | |
+---------|----------------------------------------------------+
| S3 PUT (cross-account, write-only from here)
v
+--------------------------------------------------------------+
| GOVERNED DATA ZONE |
| |
| S3 (immutable Parquet) + Glue Catalog + LF Tags |
| DynamoDB ACL (intent store) + Audit Trail |
| | | |
| v v |
| +-----------+ +--------------+ |
| |MCP Server | | pg-warehouse | |
| |(agents) | | (developers) | |
| +-----------+ +--------------+ |
| |
+--------------------------------------------------------------+
| |
v v
+--------------------------------------------------------------+
| CONSUMPTION ZONE |
| |
| Claude, GPT, custom agents, notebooks, dashboards |
| (can read governed data, cannot reach production) |
| |
+--------------------------------------------------------------+

Three zones. Two boundaries. One direction. The consumption zone has no network path, no credentials, and no protocol to reach production.

Five Security Properties

1. Physical Write Isolation

Read replica (Neon, RDS, etc.)pg-cdc + S3
ProtocolPostgres wire protocol (supports writes)S3 GET (read-only by design)
Write preventionPolicy must block writesProtocol has no write verb
Failure modeMisconfigured policy = writes succeedNo configuration can enable writes

Policy-based security fails when policy is misconfigured. Protocol-based isolation fails only if you rewrite the protocol.

2. Blast Radius Containment

If an agent is compromised (prompt injection, credential theft, supply chain attack):

Attack vectorRead replicapg-cdc + S3
Read all tablesYes (unless RLS is perfect)Only tables with matching LF tags
Read historical dataCurrent snapshot onlyOnly epochs in S3 (governed)
Write or delete dataPossible if policy misconfiguredImpossible — no write path exists
Escalate to productionSame network, same auth systemDifferent network, different auth system (IAM vs Postgres)
Exfiltrate connection stringReusable against productionNo connection string exists
DoS the databaseConnection exhaustionS3 is effectively infinite; production unaffected

3. Temporal Isolation

The agent sees data as of the last flush, not as of right now. This is a security feature:

  • An agent cannot race against transactions in progress
  • An agent cannot observe partial writes (no dirty reads — ever)
  • An agent cannot trigger lock contention on production tables
  • An agent querying historical data has zero impact on production IOPS

The CDC flush interval (default 10s) is a tunable knob between freshness and isolation. For most agent use cases (analytics, monitoring, anomaly detection), 10-second-old data is indistinguishable from live.

4. Credential Elimination

Read replicapg-cdc + S3
Credential typePOSTGRES_URL (long-lived connection string)IAM Role (assumed via STS)
Credential lifetimeUntil rotated (days, weeks, never)Temporary (1hr default, auto-rotated)
Credential scopeFull database access per pg_hba.confSpecific S3 prefix + Glue tables
Leak surfaceLogs, error messages, crash dumps, env varsNo credential to leak
RotationManual or scheduledAutomatic (STS)
Auditpg_stat_activity (if enabled)CloudTrail (always on)

There is no connection string to leak because there is no connection.

5. Governance as Physics, Not Policy

Traditional governance:

DBA writes GRANT/REVOKE --> hopes app respects it --> audits after the fact

pg-cdc governance:

Security tags table "PII" in DynamoDB
--> pg-cdc syncs tag to Lake Formation
--> LF physically prevents unauthorized reads
--> Agent never sees the column (not filtered -- absent)
--> DynamoDB Streams --> S3 audit log (immutable, COMPLIANCE lock)

The governance isn’t a layer on top. It’s the only path to the data. You can’t bypass it because there’s nothing to bypass — no alternative route to the bits.

Taking Action — Writes Without a Return Path

The air gap is one-way by design — but real agents must act: “cancel my order”, “update my address”, “issue a refund”. The naïve fix is to hand the agent a write path to production, which destroys every property above. pg-cdc keeps reads one-way and resolves this with CQRS: the read model is the governed zone; the write model is a separate, narrow command relay the agent can only request through, never drive.

The agent never writes. It submits a typed intent to an out-of-band command queue. A trusted relay — the only component holding a (scoped) production write credential — consumes the command, re-authorizes it server-side, validates it against authoritative state, applies it through the application’s domain API, and audits it. The result flows back through CDC, so the agent observes its action in the governed zone.

PRODUCTION ZONE GOVERNED DATA ZONE (read model)
PostgreSQL ──WAL(one-way)──▶ pg-cdc ──▶ S3/Iceberg + Glue/LF
▲ │ IAM read · no DB creds
│ scoped app write ▼
Command Relay ◀── queue ◀── Command API / MCP submit_command ◀── AI agent
(sole write cred) (durable, (enqueues intent; NO DB creds) (decides
ordered) from reads)
└─ the write ▶ WAL ▶ pg-cdc ▶ governed zone ▶ agent sees the result

A “cancel order” never touches the database from the agent’s side. The agent emits {type:"cancel_order", order_id, idempotency_key, based_on_epoch}; the command API (queue-publish only — no DB credential) enqueues it; the relay re-checks authorization, confirms the order is still cancellable against live state (rejecting if it moved since the agent’s epoch), and applies it via the order service. The agent gets an async acknowledgement and sees status=cancelled on its next read.

Why this is not a hole in the air gap:

  • The agent still has no DB connection, no write verb, no connection string — Property #1 is untouched. It can only request an allowlisted, typed command set; the command vocabulary is a small, reviewed surface, never “run this SQL.”
  • Every command is re-authorized server-side against the same governance plane that gates reads (Property #5), validated against authoritative state, idempotent, and audited end-to-end. High-blast-radius commands route to a human approval gate.
  • A compromised agent can only request commands it is authorized for, which the relay re-validates and may reject — it cannot escalate to arbitrary writes (Property #2 holds).

The rule of thumb: reads are physics (one-way); writes are policy (a bounded, authorized, audited command channel) — and never the same channel. The relay is the one small trust boundary that replaces “trust the agent with the database.”

How This Composes with the Two Governance Models

pg-cdc supports two governance models (see Data Governance), both operating within this security architecture:

ModelWhat it gatesWhere enforcedAir gap maintained?
Postgres-ACL profilesWhich tables/columns a developer seespg-warehouse at pull timeYes — developer never touches production
Lake Formation tagsWhich tables/columns an agent seesLF at query time (MCP server, Athena)Yes — agent never touches production

Both models enforce access within the Governed Data Zone. Neither model requires or permits access to the Production Zone.

Comparison: pg-cdc vs Common Alternatives

ApproachWrite isolationCredential modelProduction impactGovernanceTime travel
Direct DB accessNoneConnection stringFull (shared compute)RLS/RBACNone
Read replicaPolicy-basedConnection stringReduced (separate compute)RLS/RBACNone
DB proxy (PgBouncer)Policy-basedConnection stringReduced (pooling)Proxy rulesNone
ETL to warehousePhysicalWarehouse credentialsNone after extractWarehouse ACLsIf warehouse supports it
pg-cdc + S3PhysicalIAM (no DB creds)ZeroLF tags (default-deny)Built-in (CDC epochs)

The Enterprise Question

Every CISO evaluating AI agents asks:

“How do I let agents see our data without giving them access to our databases?”

The answer today is either “you can’t” or “build a bespoke ETL pipeline and hope.”

pg-cdc makes the answer:

pg-cdc init — and your production database has a governed, immutable, auditable, read-only projection that any AI agent can query without ever touching production.