Getting Started
Get PondPilot Proxy running and execute your first cross-database query. This guide covers deployment with Docker Compose and connecting via Flight SQL.
Prerequisites
Section titled “Prerequisites”- Docker with Docker Compose
- A database to connect to (PostgreSQL, MySQL, or SQLite)
- A Flight SQL client (PondPilot app, DuckDB with
airportextension, or any Arrow Flight client)
Quick Start with Docker Compose
Section titled “Quick Start with Docker Compose”1. Create Configuration Files
Section titled “1. Create Configuration Files”Create a docker-compose.yml:
services: proxy: image: ghcr.io/pondpilot/proxy:latest ports: - "8080:8080" # HTTP (health, AI endpoints) - "8081:8081" # gRPC (Flight SQL) environment: - JWT_SECRET=${JWT_SECRET} - CONTAINER_NETWORK=pondpilot - CLAUDE_API_KEY=${CLAUDE_API_KEY} # Optional: for AI features volumes: - /var/run/docker.sock:/var/run/docker.sock - ./config.yaml:/config.yaml networks: - pondpilot
networks: pondpilot: driver: bridgeCreate a config.yaml:
server: port: 8080 grpc_port: 8081 host: "0.0.0.0"
containers: image: "ghcr.io/pondpilot/sqlflite:latest" idle_timeout: 5m memory_limit: "512m" cpu_limit: 0.5
duckdb: extensions: [arrow, postgres, mysql] attached_databases: - alias: "mydb" type: "postgres" connection_string: "${DATABASE_URL}"2. Set Environment Variables
Section titled “2. Set Environment Variables”Create a .env file:
# Required: JWT secret (minimum 32 characters)JWT_SECRET="your-secret-key-at-least-32-characters-long"
# Database connectionDATABASE_URL="postgresql://user:pass@host:5432/database"
# Optional: AI featuresCLAUDE_API_KEY="sk-ant-..."3. Start the Proxy
Section titled “3. Start the Proxy”docker compose up -d4. Verify Installation
Section titled “4. Verify Installation”Check health endpoint:
curl http://localhost:8080/healthExpected response:
{"status":"healthy"}Check detailed health:
curl http://localhost:8080/health/detailedAuthentication
Section titled “Authentication”PondPilot Proxy requires JWT authentication. All requests must include a valid JWT token.
Getting a Demo Token
Section titled “Getting a Demo Token”For testing, request a demo token:
curl -X POST http://localhost:8080/auth/demo-tokenResponse:
{ "token": "eyJhbGciOiJIUzI1NiIs...", "expires_at": "2026-01-26T12:00:00Z"}Demo tokens have restricted rate limits and short expiration times.
Using JWT Tokens
Section titled “Using JWT Tokens”Include the token in requests:
HTTP endpoints:
curl -H "Authorization: Bearer <token>" http://localhost:8080/ai/chatFlight SQL (gRPC):
The token is passed via gRPC metadata with key authorization.
Connecting with Flight SQL
Section titled “Connecting with Flight SQL”Using PondPilot App
Section titled “Using PondPilot App”PondPilot automatically connects to the proxy when configured with a remote database connection. See Remote Sources for setup.
Using DuckDB with Airport Extension
Section titled “Using DuckDB with Airport Extension”Connect from any DuckDB client using the airport extension:
-- Load the airport extensionLOAD airport;
-- Attach the proxy as a remote databaseATTACH 'flight://localhost:8081?token=<your-jwt-token>' AS remote;
-- Query attached databases through the proxySELECT * FROM remote.mydb.public.users LIMIT 10;Using Arrow Flight Clients
Section titled “Using Arrow Flight Clients”Any Arrow Flight SQL client can connect to port 8081. The JWT token should be passed in the authorization metadata field.
Python example:
from pyarrow import flight
# Connect with authenticationclient = flight.connect("grpc://localhost:8081")options = flight.FlightCallOptions(headers=[ (b"authorization", b"Bearer <your-jwt-token>")])
# Execute queryinfo = client.get_flight_info( flight.FlightDescriptor.for_command(b"SELECT * FROM mydb.public.users"), options)reader = client.do_get(info.endpoints[0].ticket, options)table = reader.read_all()Your First Query
Section titled “Your First Query”Once connected, query your attached databases:
-- Query a PostgreSQL table through the proxySELECT customer_name, COUNT(*) as order_count, SUM(amount) as total_revenueFROM mydb.public.ordersGROUP BY customer_nameORDER BY total_revenue DESCLIMIT 10;The query is executed in your isolated DuckDB container, which connects to the PostgreSQL database you configured.
Container Lifecycle
Section titled “Container Lifecycle”Understanding how containers work:
- First request — Container spawns (~2-5 seconds)
- Subsequent requests — Uses cached container (~10-50ms)
- Idle timeout — Container stops after 5 minutes of inactivity
- Next request after idle — Container respawns
Each user (identified by JWT sub claim) gets their own isolated container.
AI Endpoints
Section titled “AI Endpoints”The proxy includes AI integration for chat and embeddings:
# Chat completion (streaming)curl -X POST http://localhost:8080/ai/chat \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "messages": [{"role": "user", "content": "Write a SQL query to find top customers"}], "model": "claude-sonnet-4-20250514" }'
# Embeddingscurl -X POST http://localhost:8080/ai/embed \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "input": "customer revenue analysis", "model": "text-embedding-3-small" }'Troubleshooting
Section titled “Troubleshooting”Container not starting
Section titled “Container not starting”Check Docker socket permissions:
ls -la /var/run/docker.sockThe proxy needs access to the Docker socket to manage containers.
Connection refused on port 8081
Section titled “Connection refused on port 8081”Ensure the gRPC port is exposed and not blocked by firewall:
docker compose logs proxy | grep -i grpcJWT validation failed
Section titled “JWT validation failed”Verify your JWT secret matches and token hasn’t expired:
# Decode JWT (without verification) to check claimsecho "<token>" | cut -d'.' -f2 | base64 -d 2>/dev/null | jqNext Steps
Section titled “Next Steps”- Configuration Reference — All configuration options
- Cross-Database Queries — JOIN across databases
- Deployment — Production setup