Files
warmbly/deploy/split-cloud/README.md
T

54 lines
2.5 KiB
Markdown

# Split deployment
The control plane on a container host, the fleet on machines you own, and the
database, root key and object store in a cloud region next to the control
plane. Three providers, one instance.
```
container host your machines cloud region
────────────── ───────────── ────────────
backend worker Postgres
consumer worker KMS key
realtime nats + redis S3 bucket
tracking SES
forms
web / admin
```
The full walkthrough, with costs and the order to build it in, is
[Split deployment](https://docs.warmbly.com/development/split-deployment/).
## What is here
| Path | What it is |
|------|------------|
| `control-plane.env.example` | Every setting the container host needs, annotated |
| `bus/setup.sh` | One command that stands the whole bus box up, and re-runs safely |
| `bus/docker-compose.yml` | NATS JetStream and Redis, both over TLS |
| `bus/nats.conf` | The bus config the compose file mounts |
| `bus/certbot-deploy-hook.sh` | Publishes renewed certificates where the containers can read them |
| `node/docker-compose.yml` | A worker run by hand, for the version-controlled path |
| `node/worker.env.example` | What a worker needs, and what it deliberately does not |
| `../../scripts/aws-bootstrap.sh` | The KMS key, bucket, database and IAM policies |
## Two things that are not optional
**Redis over TLS.** The cache holds each organization's decrypted data key for
the life of its entry. A plaintext connection across the internet publishes key
material, and a password does not change that.
**S3 rather than filesystem blobs.** A worker reads the message body the
backend wrote. On another machine it has neither the disk nor the permissions,
so sends fail at the last step with everything else looking healthy.
## What a node does not get
A joining node is handed `KMS_PROVIDER=brokered` and `BLOB_PROVIDER=brokered`
rather than the control plane's own AWS providers, so no machine in the fleet
carries a cloud credential. It opens sealed keys and signs blob operations
through the internal API, with the instance-scoped token it already holds.
Blob bytes still travel directly between the node and the object store.
A worker also never receives `PRIMARY_DB`. A consumer does, because it is
control plane and updates relational state itself.