Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Key capabilities:
- **Credential injection** — Resolve credentials from environment variables, static values, host command output, AWS Secrets Manager, GCP Secret Manager, GCP service account keys, GitHub App keys, or RFC 8693 token exchange, then inject them as HTTP headers for matching hosts
- **Postgres data plane** — Credential-injecting Postgres proxy on a second listener: routes by TLS SNI, authenticates clients with their run token (sent as the Postgres password inside gatekeeper's TLS), and resolves per-branch Neon passwords (or a static password) on the fly so no database secret lives in the sandbox
- **TLS interception** — MITM proxy with per-host certificate generation from a configured CA
- **PROXY protocol** — Per-listener v1/v2 header parsing (`proxy.proxy_protocol`, `postgres.proxy_protocol`) recovers the real client IP behind a TCP-terminating load balancer, fail-open on headerless connections
- **Single-port multiplexing** — When `postgres.port` equals `proxy.port` (same host), gatekeeper serves the HTTP/CONNECT and Postgres planes on one shared listener, classifying each connection by its first bytes; no config flag, the matching ports are the trigger
- **MCP relay** — Forward Model Context Protocol requests with credential injection and SSE streaming
- **Network policy** — Allow/deny traffic by host pattern
- **LLM policy** — Evaluate Anthropic API responses against Keep policy rules
Expand All @@ -29,6 +31,8 @@ proxy/ Core TLS-intercepting proxy engine
relay.go HTTP relay for non-CONNECT requests
stream.go Chunked, per-write-flushed response streaming (SSE and other incremental bodies)
postgres.go Postgres data-plane listener (TLS termination, run-token auth, SCRAM upstream, message relay)
proxyproto.go Shared PROXY protocol v1/v2 listener wrapping (fail-open, malformed-header logging) for both listeners
demux.go Single-port HTTP+Postgres demultiplexer: classifies connections by first bytes when proxy.port == postgres.port
otel.go OpenTelemetry handler wrapper, metrics instruments, span helpers
server.go Proxy server lifecycle (start/stop/listen)

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ Policies: `permissive` (allow all), `strict` (deny all, allow listed).

Behind a TCP-terminating load balancer (e.g. GCP's global TCP Proxy LB), every connection's peer address is the load balancer's, not the real client. PROXY protocol parsing recovers the real client address, and it's configured per listener: `proxy.proxy_protocol: true` for the HTTP/CONNECT listener, and `postgres.proxy_protocol: true` for the Postgres data-plane listener. Each parses a PROXY protocol v1/v2 header from the LB and uses its advertised source as the `client_ip` recorded in request logs. Both are fail-open (headerless connections, like LB health checks, fall back to the raw peer address) and should only be enabled when that listener's port is reachable solely through the load balancer, since the header is otherwise forgeable by any direct client. See [Deploying behind a TCP load balancer](docs/content/guides/11-load-balancer-proxy-protocol.md).

**Single-port multiplexing:** if `postgres.port` equals `proxy.port` (same host), gatekeeper serves both planes on one shared listener instead of binding two, classifying each connection by its first bytes (Postgres startup signatures vs. everything else). There's no flag — the matching ports are the trigger. This lets one load balancer front both planes with a single backend service and health check. See [Single load balancer, one shared port](docs/content/guides/11-load-balancer-proxy-protocol.md#single-load-balancer-one-shared-port).

## MCP relay

For MCP clients that can't route through `HTTP_PROXY`, Gatekeeper relays Model Context Protocol requests directly, injecting credentials and streaming SSE responses:
Expand Down Expand Up @@ -216,6 +218,8 @@ PGPASSWORD=<run-token> psql \

Moat sets `PGPASSWORD` in the container so the token never appears in the agent's command line. See [`examples/gatekeeper-postgres.yaml`](examples/gatekeeper-postgres.yaml) for a complete example.

**Tracing a connection:** the Postgres request log records the authenticated `run_id` alongside the client-set `application_name` startup parameter (set via `PGAPPNAME` or a driver's connection option), sanitized and length-bounded. `run_id` is the trusted identity; `application_name` is a correlation slug for telling apart multiple connections within one run, and it's forwarded upstream unchanged so it also shows up in Neon's own `pg_stat_activity`.

## Observability

Gatekeeper supports OpenTelemetry for traces, metrics, and logs. No YAML configuration needed — use standard `OTEL_*` environment variables:
Expand Down
Loading