Skip to content
Getting started

Getting started

quicSQL is one static binary. This page takes you from nothing to a running server your language can query, in about a minute.

Install

docker run -p 7775:7775 -v quicsql-data:/data \
  -v ./quicsql.yaml:/etc/quicsql/quicsql.yaml \
  ghcr.io/quicsql/quicsql:latest

1. Run the daemon

One YAML file describes the whole server: listeners (one per transport), databases (one per line), and — when you want them — secrets, TLS, principals, and grants.

# quicsql.yaml
server:
  data_dir: ./data
secrets:
  - {name: keys, type: file, dir: ./data/keys}     # "keys:<name>" reads ./data/keys/<name>
tls:
  dev: {mode: self_signed, hosts: [localhost, 127.0.0.1]}   # use mode: files in production
listeners:
  - {name: h1,   transport: h1,   address: 127.0.0.1:7775}
  - {name: h2,   transport: h2,   address: 127.0.0.1:7777, tls: dev}
  - {name: h3,   transport: h3,   address: 127.0.0.1:7778, tls: dev}   # HTTP/3 over QUIC
  - {name: unix, transport: unix, address: ./data/quicsql.sock, socket_mode: "0600"}
databases:
  - {name: users,  backend: file, path: users.db, mode: rwc, pragmas_preset: recommended}
  - name: orders                                   # encrypted + compressed at rest
    backend: vault
    path: orders.vault
    vault: {compression: default, cipher: adiantum, key: keys:orders}
quicsql --config quicsql.yaml
With no principals or grants configured, the server runs in open mode — every caller is read-write. That’s the right default for a loopback dev server; bind to 127.0.0.1 and nothing else. To lock it down, add a principal, a grant, and a listener auth: list — see Auth & authorization.

Every listener serves the same endpoints: POST /<db>/query (native JSON), /<db>/v2|v3/pipeline and /<db>/v3/cursor (Hrana), /<db>/export, /<db>/changeset/*, /<db>/blob/*, plus the server-scoped /_health, /_metrics, /_admin/*, and /_auth/challenge. The canonical port is 7775 (h1); the sequence continues h2c 7776, h2 7777, h3 7778.

2. Talk to it — from your language

The native JSON endpoint takes {sql, args} — or a statements batch, which runs as one explicit transaction, all-or-nothing:

curl -s http://127.0.0.1:7775/users/query \
  -d '{"sql":"CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT)"}'

curl -s http://127.0.0.1:7775/users/query \
  -d '{"statements":[
        {"sql":"INSERT INTO users(name) VALUES (?)","args":["ada"]},
        {"sql":"SELECT * FROM users"}
      ]}'

Integers stay exact on the wire; blobs are boxed as {"base64": …}. Full shapes in the HTTP API reference.

These are dev-mode snippets (open mode, no token). Once you add principals, every SDK passes its token via authToken / auth_token, which quicSQL receives as standard bearer auth — see Clients & languages.

3. Embed it (Go)

serverd.Run assembles the whole pipeline in-process — for tests, custom SQL functions, or shipping a bundled server inside your own binary:

import "quicsql.net/serverd"

inst, _ := serverd.Run(cfg, log)   // cfg is a *config.Config; returns an *Instance
defer inst.Shutdown(ctx)

Where next

  • Clients & languages — your language’s path in, with CI-tested examples: JavaScript/TypeScript, Python, PHP, Go, and more.
  • Databases & backends — every open mode gosqlite has, over the wire: files, in-memory, mvcc snapshots, and vault containers in every shape, plus pragmas, pool tuning, and secrets.
  • Auth & authorization — six authentication methods, the none < read-only < read-write < admin capability model, and why read-only cannot be talked around.
  • The Hrana pipeline — transactions, batches, batons, and production limits.
  • Runnable examples — every language above, asserting its results against a real server in CI.