Connect as server — overview
Run the engine behind a Postgres-wire listener and anything that speaks Postgres can connect — no Twill-specific driver. This page covers what's shared across every client: starting the server, the connection string, and the one rule that governs all of them. Then each language and ORM has its own page with a full breakdown.
Start the server
One binary serves either storage backend, chosen by the --db URL.
# Local file backend on port 5433
cargo run -p twill-server -- --listen 127.0.0.1:5433 --db file://./srv.db
# Or disaggregated on object storage
cargo run -p twill-server -- --listen 0.0.0.0:5433 --db s3://my-bucket/mydb
The container image (ghcr.io/bihaviour/twill-db) runs the same binary for self-host / managed deployments.
Metrics & health (self-hosted)
Pass --metrics HOST:PORT to expose an operator-facing /metrics endpoint in Prometheus text format, plus a /healthz liveness probe, on a separate address from the Postgres-wire listener. It is off by default and self-hosted — the server never sends anything outbound, so there is no telemetry to opt out of.
# Wire on 5433, metrics on 9100 (scrape http://host:9100/metrics)
cargo run -p twill-server -- --listen 0.0.0.0:5433 --db file://./srv.db --metrics 0.0.0.0:9100
curl -s http://127.0.0.1:9100/metrics # Prometheus exposition
curl -s http://127.0.0.1:9100/healthz # -> ok
Two families are exported. Wire-level counters come from the listener: twilldb_connections_total / twilldb_connections_active, twilldb_queries_total, twilldb_query_errors_total, twilldb_rows_returned_total, and twilldb_uptime_seconds. Engine / storage gauges are read live from the engine: twilldb_engine_commits_total, twilldb_engine_committed_lsn, twilldb_engine_write_acquires_total / …_write_handoffs_total (the serialized-handoff signal — see hot-row contention), and the storage seam's twilldb_storage_wal_appends_total / …_wal_bytes_total / …_cache_hits_total / …_cache_misses_total / …_fsyncs_total. Point Prometheus at the endpoint and graph error rate (rate(twilldb_query_errors_total[5m])), throughput, and cache hit ratio in Grafana.
A scrape is the same cost as one client connecting; 15s is a sane interval. The same SHOW twill.stats surface is available in-band over the wire if you'd rather pull the engine gauges through a SQL client.
A ready-to-run Prometheus + Grafana stack (with an auto-provisioned dashboard) lives in packaging/observability/ — docker compose up and open Grafana at localhost:3000. See its README.md for the metric reference.
The connection string
Every client uses an ordinary Postgres DSN. The database name is accepted but nominal — the server serves the database given by --db.
postgresql://[email protected]:5433/main?sslmode=disable
Always sslmode=disable
The listener speaks Protocol 3.0 startup with trust auth over cleartext. TLS termination and SCRAM auth are deliberate non-goals for now — terminate TLS at a proxy if you need it on an untrusted network. Connect with sslmode=disable (or the driver's equivalent) everywhere.
The one rule: connected ≠ every query runs
Twill implements both the simple and the full extended query protocol (Parse / Bind / Describe / Execute), which is exactly what prepared-statement clients and ORMs use — so they connect with no special driver. But the engine's SQL surface is a focused (growing) subset of Postgres. The rule of thumb:
- Connect The wire protocol gets any Postgres tool talking to the engine.
- Run Whether a given query executes depends on the supported SQL subset. Anything unsupported returns a clean
ENGINE_ERR_SQL— reject, never mis-parse. - Schema Runtime CRUD generally maps cleanly; ORM migration/introspection tooling that probes full Postgres catalog behaviour is the rough edge. Prefer authoring schema with plain validated SQL.
Pick your client
Node / Bun
Bun.sql and pg (node-postgres) — parameterized queries and pooling.
Python
psycopg v3 — cursors, parameter style, transactions.
Go
pgx — connect, query, and pooling with pgxpool.
Drizzle ORM
Query builder over postgres-js, plus the migration caveat.
Prisma
Datasource setup and the manage-schema-yourself approach.
PostgREST
Auto-generated REST API over the engine, unmodified — with FK embedding.
Connections & scale
The engine is single-writer with one OS thread per connection. For serverless or many-client workloads, put a transaction-mode pooler (PgBouncer / pgcat) in front to absorb connection bursts — see Connection pooling. The storage backend (and thus scale-to-zero) is independent of the client — see Storage backends.