# Pre-flight Checklist

## not.bot™ Verify: Pre-flight Checklist

Complete every item before you open the Deployment Checklist. Missing prerequisites cause most first-deploy failures.

---

### Infrastructure

| # | Requirement | How to verify | Done |
|---|-------------|---------------|------|
| 1 | Kubernetes cluster with a configured kubectl context | `kubectl cluster-info` returns a running control plane | ☐ |
| 2 | Helm 3 installed | `helm version` shows v3.x | ☐ |
| 3 | helm-diff plugin installed | `helm plugin list` includes `diff` | ☐ |
| 4 | PostgreSQL 14 or later, reachable from inside the cluster | `kubectl run pgcheck --rm -i --restart=Never --image=postgres:16 -- pg_isready -h <HOST> -p 5432` reports "accepting connections" | ☐ |
| 5 | Keycloak 22 or later, served at a public HTTPS hostname and reachable from inside the cluster at that same hostname | `kubectl run kccheck --rm -i --restart=Never --image=curlimages/curl -- curl -fsS -o /dev/null -w "%{http_code}" https://<KEYCLOAK_PUBLIC_HOSTNAME>/realms/master` prints 200 — see the note below if Keycloak uses a private CA | ☐ |
| 6 | A container registry your cluster can pull from | `docker pull <YOUR_REGISTRY>/any-existing-image` succeeds | ☐ |
| 7 | Cluster supports internal LoadBalancer Services | Managed K8s (EKS/GKE/AKS) supports this by default. On bare-metal, `kubectl get pods -n metallb-system` shows running pods | ☐ |

> Items 4 and 5 say *from inside the cluster* deliberately: run the probe as a pod, not from a host shell. Host-level reachability does not imply pod reachability (a database bound to `127.0.0.1`, or a docker proxy on the host, passes a host probe and fails every pod).
>
> Use the **public hostname operators will log in at** in item 5, not an internal service address. Keycloak stamps that hostname into every token it issues, and the admin service checks it (Deployment Checklist §5.1), so the admin pod has to reach Keycloak by that name.
>
> Item 5 fails in two different ways and they need different fixes, so read the failure rather than just the missing 200 (`curl -fsS` prints `000` for both). A **TLS/certificate error** (`SSL certificate problem`, `unable to get local issuer certificate`) means the probe reached Keycloak but does not trust its CA. A **timeout or connection refusal** means the pod could not reach the hostname at all; if it resolves to an address the cluster cannot hairpin to (AWS EC2 Elastic IPs are the common case), you need an in-cluster DNS override before deploying — Deployment Checklist Appendix B §B.12.
>
> **If Keycloak uses a private CA**, this probe cannot print 200 on its own — it carries no CA, so it will always report a certificate error. That is expected, and it tells you that you will need the admin chart's `extraCaCerts` values. Two ways to satisfy this item instead: re-run with `-k` to confirm the endpoint is otherwise healthy (a 200 with `-k` means reachability and DNS are fine and only trust is missing), or run the Deployment Checklist §B.12 preflight probe, which mounts a CA into the probe pod and checks reachability and trust together — substitute the path to your own CA certificate for the one shown there. Prefer the §B.12 probe: it is the same check the admin pod will make.

### Workstation tools

| # | Requirement | How to verify | Done |
|---|-------------|---------------|------|
| 8 | openssl | `openssl version` returns a version | ☐ |
| 9 | OpenBao CLI (`bao`) installed | `bao --version` returns a version | ☐ |

> If you intend to rebuild the OpenBao image from source for supply-chain validation, also install Docker with buildx. See Deployment Checklist Appendix A. The mainline flow uses the pre-built image shipped in the chart and requires no compiler toolchain.

### Access

| # | Requirement | How to verify | Done |
|---|-------------|---------------|------|
| 10 | DNS control for the domain in your deployment-config.json | You can add a TXT record at the root of that domain | ☐ |
| 11 | DNS control for the internal zone holding your admin service hostname | You can add a CNAME for Decision H in that zone | ☐ |
| 12 | Permission to create namespaces in the Kubernetes cluster | `kubectl auth can-i create namespaces` returns `yes` | ☐ |
| 13 | Permission to create secrets in the Kubernetes cluster | `kubectl auth can-i create secrets` returns `yes` | ☐ |

### Deployment package

| # | Requirement | How to verify | Done |
|---|-------------|---------------|------|
| 14 | deployment-config.json from your welcome email | File contains your customerId, organizationName, apiKey, billingServerUrl, and domain | ☐ |
| 15 | not.bot_verify_deployment.zip from your welcome email | Unzips to a `helm/` directory with three subdirectories: admin-service, openBao, signer-service | ☐ |

### Decisions to make before you start

You will need these values during deployment. Decide on them now so you are not stopping mid-checklist.

| # | Decision | Your value |
|---|----------|------------|
| A | Kubernetes namespace for OpenBao | _______________ |
| B | Kubernetes namespace for the admin service | _______________ |
| C | Kubernetes namespace for signature servers | _______________ |
| D | OpenBao namespace name (internal to OpenBao, not Kubernetes) | _______________ |
| E1 | PostgreSQL database name for the admin service | _______________ |
| E2 | PostgreSQL database name for the signature server — must be a different database from **E1** | _______________ |
| F | PostgreSQL admin service user — name (default `notbot_admin`) and password. Owns database **E1**. | name: _______________   password: _______________ |
| G1 | PostgreSQL signature server user — name (default `notbot_signer`) and password. Owns database **E2**. | name: _______________   password: _______________ |
| G2 | PostgreSQL read-only user — name (default `notbot_signer_reader`) and password. Lets the admin service read verification counts from **E2**. | name: _______________   password: _______________ |
| H | Internal hostname operators use to reach the admin UI (e.g. admin.internal.example.com) | _______________ |
| I | Internal hostname for the signature server load balancer (e.g. signer.internal.example.com) | _______________ |
| J | TLS certificate for the admin LB (cloud-managed cert ARN/resource ID, or path to BYO cert + key) | _______________ |
| K | TLS certificate for the signature server LB (Decision **I**'s hostname). May be the same multi-SAN cert as Decision **J** or a separate cert. | _______________ |

Decision H appears in Keycloak redirects, Helm values, and your internal DNS zone. Decision I appears in Helm values, your internal DNS zone, and the SDK configuration. Use the exact same value everywhere each one appears.

not.bot Verify uses **two** PostgreSQL databases, because the admin service and the signature server each create and migrate their own schema. Database **E1** belongs to the admin service, database **E2** belongs to the signature server, and each is owned by its own user. The admin service also reads verification counts out of **E2** for the dashboard and for usage reporting, which is what the read-only user **G2** is for. Both databases can live on the same PostgreSQL instance; you will need an account there that can create databases and roles.

---

If any item above is incomplete, resolve it before proceeding. The Deployment Checklist assumes all of these are in place.
