# Quickstart

> Status: Available. Run one node on your own machine, then three nodes on one bucket with TLS, and connect a client.

| | |
|---|---|
| **Works** | One node on loopback with a local directory; three nodes on one qualified bucket with TLS; psql, psycopg 3, node-postgres, pgjdbc and HTTP clients. |
| **Differs** | The local `fs:` store is for development only. It says nothing about how a real bucket behaves. |
| **Missing** | A public download, a macOS or Windows server binary, a managed service. |
| **Operational impact** | Creating a database reserves an immutable ID. Dropping a database does not reclaim its data in pre-beta. |
| **Evidence** | Steps follow the project's beta guide. The beta acceptance run has not passed yet. |

> **You need a build first.** There is no public download yet. Pre-beta builds are a release directory (binary, checksums, build record, example config and systemd unit) handed to invited testers. [Join the waitlist](https://gauntlet-db.stavros.xyz/start).

## What you need

- **The release directory**, all files from the same release.
- **Linux x86_64 with glibc 2.34 or newer** (Ubuntu 22.04, Debian 12, RHEL 9 or later). The binary does not run on macOS.
- **For part 2:** three hosts that can reach each other on TCP 7101, and one bucket.

Check and install the binary:

```sh
sha256sum --check --ignore-missing SHA256SUMS
install -d -m 0755 "$HOME/.local/bin"
install -m 0755 gauntlet-*-linux-x86_64 "$HOME/.local/bin/gauntlet"
export PATH="$HOME/.local/bin:$PATH"
gauntlet --version        # must list "engines: turso"
```

## Part 1: one node on loopback

This runs one node against a directory on local disk. It needs no bucket and no certificates, because every listener is on 127.0.0.1.

```sh
mkdir -p "$HOME/gauntlet-dev" && cd "$HOME/gauntlet-dev"
export GAUNTLET_CLIENT_TOKEN=$(openssl rand -hex 32)
export GAUNTLET_PEER_TOKEN=$(openssl rand -hex 32)
export GAUNTLET_ADMIN_TOKEN=$(openssl rand -hex 32)

gauntlet --cache-dir "$PWD/cache" --cache-mib 512 serve \
  --store "fs:$PWD/bucket" --id n1 \
  --listen 127.0.0.1:7101 --pg-listen 127.0.0.1:7151 \
  --memory-mib 4096
```

`--memory-mib 4096` is the smallest budget allowed. The node checks its store before it serves:

```sh
curl -s http://127.0.0.1:7101/health     # "ok": the process answers
curl -si http://127.0.0.1:7101/ready     # 200 when it can serve, 503 when it cannot
```

In a second terminal, with the same three tokens exported, create a database and get its token:

```sh
export GAUNTLET_URL=http://127.0.0.1:7101
gauntlet admin create-database app > app-create.json   # keep this file
gauntlet admin list
TOKEN=$(gauntlet admin token app)
```

Connect. The database token is the password, for any user name:

```sh
PGPASSWORD=$TOKEN psql "host=127.0.0.1 port=7151 dbname=app user=app" \
  -c "CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT NOT NULL)" \
  -c "INSERT INTO items VALUES (1, 'first')" \
  -c "SELECT * FROM items"
```

Stop the node with Ctrl-C. Start it again with the same command and the row is still there: everything it needs is under `bucket/`. The `cache/` directory is disposable. A restart with the same `--id` takes 17–18 s to become ready, because it waits out the previous run's 15 s lease.

## Part 2: three nodes on one bucket

The examples use hosts `n1.example.net`, `n2.example.net` and `n3.example.net`, and an admin machine that is none of them.

### Choose a bucket

Gauntlet needs more than the S3 API. It needs atomic create-if-absent (`If-None-Match: *`), read-after-write consistency and a strongly consistent, ordered LIST. `gauntlet qualify` tests this for you, and every node start runs the same checks again. A bucket that fails is refused.

| Provider | Status |
|---|---|
| Tigris | Run by the project. Recommended: one region, next to the hosts |
| Cloudflare R2 | Run by the project. Conditional writes were slower in the project's runs, so commits are slower |
| AWS S3 | **Unverified.** No run is recorded. Qualify it first |
| MinIO | **Unverified.** No run is recorded. Qualify it first |

Details, including the environment variables, are in [deploy and qualify a bucket](https://gauntlet-db.stavros.xyz/docs/operations/deploy.md).

### Secrets, certificates and config

On the admin machine, write one secrets file that every node uses, test the bucket, and issue certificates from the deployment's private CA:

```sh
umask 077
cat > secrets.env <<ENV
GAUNTLET_S3_ENDPOINT=https://t3.storage.dev
GAUNTLET_S3_REGION=auto
GAUNTLET_S3_BUCKET=my-gauntlet-bucket
GAUNTLET_S3_ACCESS_KEY_ID=<access key id>
GAUNTLET_S3_SECRET_ACCESS_KEY=<secret access key>
GAUNTLET_CLIENT_TOKEN=$(openssl rand -hex 32)
GAUNTLET_PEER_TOKEN=$(openssl rand -hex 32)
GAUNTLET_ADMIN_TOKEN=$(openssl rand -hex 32)
ENV

(set -a; . ./secrets.env; gauntlet qualify --store 's3:#qualify-check')

gauntlet tls init --out ca
for n in 1 2 3; do
  gauntlet tls issue --ca ca --node n$n --dns n$n.example.net --dns localhost --out n$n
done
```

Write `/etc/gauntlet/gauntlet.toml` on each host. Only `id` and `advertise` differ between nodes:

```toml
store = "s3:#gauntlet/beta"          # GAUNTLET_S3_* plus this prefix; same on every node
id = "n1"                            # unique per node
listen = "0.0.0.0:7101"              # HTTP: clients, admin and peers
advertise = "n1.example.net:7101"    # a name in this node's certificate
pg-listen = "0.0.0.0:7151"           # PostgreSQL wire protocol
tls-cert = "/etc/gauntlet/tls/node.crt"
tls-key = "/etc/gauntlet/tls/node.key"
tls-peer-ca = "/etc/gauntlet/tls/ca.crt"
cache-dir = "/var/lib/gauntlet/cache"
cache-mib = 8192
memory-mib = 16384
log-format = "json"
```

Start all three with the packaged systemd unit. They find each other through leases in the bucket. A node answers neither `/health` nor `/ready` until its start-up bucket check passes.

### Create a database

```sh
export GAUNTLET_URL=https://n1.example.net:7101 GAUNTLET_TLS_CA=ca/ca.crt
set -a; . ./secrets.env; set +a
umask 077
gauntlet admin create-database app > app-create.json
TOKEN=$(gauntlet admin token app)
```

If `create-database` reports an unknown outcome, retry with `gauntlet admin create-database --reservation app-create.json`. Never retry by name: that reserves a second ID.

## Connect

Any node serves any database. Use the database name as `dbname`, any user name, and the database token as the password.

```sh
PGPASSWORD=$TOKEN psql "host=n1.example.net port=7151 dbname=app user=app \
  sslmode=verify-full sslrootcert=ca/ca.crt channel_binding=require" \
  -c "SELECT 1"
```

Driver examples are in [PostgreSQL wire](https://gauntlet-db.stavros.xyz/docs/clients/pgwire.md); the JSON API is in [HTTP](https://gauntlet-db.stavros.xyz/docs/clients/http.md).

## Next

- Read [transactions](https://gauntlet-db.stavros.xyz/docs/concepts/transactions.md): your client must handle `40001` and `40003`.
- Take a first [backup](https://gauntlet-db.stavros.xyz/docs/operations/backups.md) and practise a restore.
- Read the [limits](https://gauntlet-db.stavros.xyz/docs/limits.md) before you put data in.

---
Source: https://gauntlet-db.stavros.xyz/docs/quickstart
