# Deploy and qualify a bucket

> Status: Available. What a deployment is, what the bucket must guarantee, and how nodes are secured.

A deployment is one bucket prefix and the `gauntlet serve` nodes that point at it. The supported shape in pre-beta is `gauntlet serve` on Linux x86_64 (glibc 2.34 or newer), from a systemd unit or the container image.

One rule comes first: **the bucket is the database.** Never delete, expire or edit objects under the deployment's prefix, and never set a lifecycle rule on it.

## Qualify the bucket

Gauntlet needs atomic create-if-absent (`If-None-Match: *`), read-after-write consistency and a strongly consistent, ordered LIST. An S3-compatible endpoint alone is not enough.

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

`gauntlet qualify` exits 0 on a pass, 2 when a check fails and 3 when the result is inconclusive. Every node start runs the same checks. Never run `qualify` against a live prefix.

| Provider | Status |
|---|---|
| Tigris | Run by the project |
| Cloudflare R2 | Run by the project. Slower conditional writes in those runs |
| AWS S3 | Unverified. No recorded run |
| Google Cloud Storage | Unverified. Qualification is planned |
| MinIO | Unverified. No recorded run |

One strongly consistent, **single-region** bucket is the only supported shape. A multi-region or eventually consistent bucket is not safe as the only arbiter.

## Bucket settings

| Variable | Default | Meaning |
|---|---|---|
| `GAUNTLET_S3_ENDPOINT` | required | The service URL |
| `GAUNTLET_S3_BUCKET` | required | The bucket name |
| `GAUNTLET_S3_ACCESS_KEY_ID`, `GAUNTLET_S3_SECRET_ACCESS_KEY` | required | A key that can read and write this bucket only |
| `GAUNTLET_S3_REGION` | `auto` | The signing region |
| `GAUNTLET_S3_PATH_STYLE` | `true` | Set `false` for virtual-hosted bucket addressing |
| `GAUNTLET_S3_TIMEOUT_MS` | `10000` | Deadline for one control request |

## Security

- **TLS on every listener.** A listener that is not on loopback must use TLS, or the node refuses to start. The deployment has its own private CA; each node's certificate is both its server certificate and its client certificate to the other nodes.
- **SCRAM-SHA-256** for PostgreSQL clients. One token per database; a separate admin token.
- **Tokens are at least 32 characters** and never go in the config file. Each can come from a file named by `GAUNTLET_<NAME>_FILE`.
- **No SQL roles** or table privileges.
- **The bucket is trusted.** Objects are not encrypted or signed by Gauntlet.

A node refuses to start, before it binds or writes anything, if TLS is half configured, the key file is readable by others, the certificate does not cover the advertised name, a token is missing or short, or the bucket fails qualification.

## Health endpoints

| Endpoint | Auth | Meaning |
|---|---|---|
| `GET /health` | none | Liveness. Answers `ok` while the process runs |
| `GET /ready` | none | Readiness. 200 when the node can serve; 503 when it cannot |
| `GET /db/<name>/ready` | database token | That database's readiness. Does not wake an idle database |

Point a load balancer's health check at `GET /ready`.

## Configuration

The config file's keys are the flag names of `gauntlet serve --help`. A flag on the command line wins over an option's environment variable, which wins over the file. A misspelt key, a value of the wrong type, or a key that looks like a secret stops the start.

## Coming

Managed Gauntlet Cloud and packaged bring-your-own-cloud installs are designed and not built. Qualification of AWS S3 and Google Cloud Storage is planned.

---
Source: https://gauntlet-db.stavros.xyz/docs/operations/deploy
