|
| 1 | +--- |
| 2 | +name: create-rpc-api-key |
| 3 | +description: Add a new public RPC API key consumer to an Aztec RPC environment (mainnet or testnet) — mint the GCP Secret Manager secret with the helper script, wire it into Kong via Terraform, apply, verify it is live, and open the PR. Use when asked to add/create an RPC API key, onboard a new RPC client/consumer, or "give <someone> a mainnet/testnet rpc key". |
| 4 | +--- |
| 5 | + |
| 6 | +# Create a public RPC API key |
| 7 | + |
| 8 | +Adds one API key for the public RPC gateway. A key is a value stored in GCP Secret Manager; Terraform turns it into a Kong consumer that can call every keyed route in the environment. Externally the consumer is just `clientN`; the real recipient is recorded only as a private annotation on the secret. |
| 9 | + |
| 10 | +The two RPC environments live in `spartan/terraform/deploy-rpc/environments/`: |
| 11 | + |
| 12 | +| Environment | dir | secret prefix | namespace / release prefix | |
| 13 | +|---|---|---|---| |
| 14 | +| mainnet | `environments/mainnet` | `mainnet-rpc-consumer` | `mainnet-rpc` / `mainnet` | |
| 15 | +| testnet | `environments/testnet` | (check that env's `CONSUMERS` entries) | `testnet-rpc` / `testnet` | |
| 16 | + |
| 17 | +The examples below use **mainnet**. For testnet, swap the dir, prefix, namespace, and context. Always read the target env's existing `CONSUMERS` entries and copy their exact prefix rather than assuming. |
| 18 | + |
| 19 | +## Before you start — access and tooling |
| 20 | + |
| 21 | +- **Working directory**: agents in this repo often run with `yarn-project` as the shell CWD, but the script and Terraform live under the git root's `spartan/`. Use absolute paths (or the correct git-root-relative path). Do **not** `cd` — the Bash working directory persists across calls and a stray `cd` will break later relative paths. |
| 22 | +- **gcloud**: authenticated, project `testnet-440309` (`gcloud config get-value project`). Terraform's GCS backend and google provider need Application Default Credentials — if you hit auth errors, `gcloud auth application-default login`. |
| 23 | +- **kubectl**: the GKE context `gke_testnet-440309_us-west1-a_aztec-gke-public` must exist (`kubectl config get-contexts`). |
| 24 | +- **Terraform version**: the remote state may have been written by a newer Terraform than your local binary, and Terraform refuses to operate on newer state. Check and match: |
| 25 | + ```bash |
| 26 | + # state version (mainnet) |
| 27 | + gcloud storage cat gs://aztec-terraform/aztec-gke-public/mainnet-rpc/deploy-rpc/terraform.tfstate/default.tfstate | jq -r .terraform_version |
| 28 | + terraform version |
| 29 | + ``` |
| 30 | + If local `< state`, install a matching-or-newer Terraform before continuing. |
| 31 | + |
| 32 | +## Step 1 — mint the secret |
| 33 | + |
| 34 | +`spartan/scripts/create_api_key.sh <prefix> <internal-owner-name>` |
| 35 | + |
| 36 | +- `<prefix>` = the environment's consumer prefix, e.g. `mainnet-rpc-consumer`. |
| 37 | +- `<internal-owner-name>` = a private annotation naming the real recipient (e.g. `vitalik`, `fairies`). Kept for our records only — **do not put it in the public PR.** Match the lowercase style of existing annotations. |
| 38 | +- The script lists existing `<prefix>-client*` secrets, takes the most recently created, and creates the next index (`clientN+1`). If none exist it starts at `client1`. |
| 39 | + |
| 40 | +```bash |
| 41 | +/abs/path/to/spartan/scripts/create_api_key.sh mainnet-rpc-consumer fairies |
| 42 | +# -> Created version [1] of the secret [mainnet-rpc-consumer-client9]. |
| 43 | +``` |
| 44 | + |
| 45 | +Verify (note the index and annotation): |
| 46 | +```bash |
| 47 | +gcloud secrets describe mainnet-rpc-consumer-client9 --format="value(name.basename(), annotations)" |
| 48 | +``` |
| 49 | + |
| 50 | +Caveat: the "next index" is derived from the most-recently-*created* secret, not the max number. This is only a problem if secrets were created out of order — normally fine. |
| 51 | + |
| 52 | +## Step 2 — add the consumer to Terraform |
| 53 | + |
| 54 | +Edit `spartan/terraform/deploy-rpc/environments/<env>/main.tf` and append a `clientN` entry to the `CONSUMERS` map, matching the existing entries exactly: |
| 55 | + |
| 56 | +```hcl |
| 57 | + client9 = { |
| 58 | + username = "mainnet-rpc-consumer-client9" |
| 59 | + gcp_secret_manager_secret_name = "mainnet-rpc-consumer-client9" |
| 60 | + rate_limit_minute = 0 |
| 61 | + } |
| 62 | +``` |
| 63 | + |
| 64 | +`rate_limit_minute = 0` means unlimited (no rate-limit plugin is generated). Use a positive value only if a cap is intended. |
| 65 | + |
| 66 | +## Step 3 — plan and verify (the guardrail) |
| 67 | + |
| 68 | +```bash |
| 69 | +TF=/abs/path/to/spartan/terraform/deploy-rpc/environments/mainnet |
| 70 | +terraform -chdir="$TF" init -reconfigure -input=false |
| 71 | +terraform -chdir="$TF" plan -input=false -out=tfplan |
| 72 | +``` |
| 73 | + |
| 74 | +The plan **must** be exactly: |
| 75 | + |
| 76 | +``` |
| 77 | +Plan: 2 to add, 0 to change, 0 to destroy. |
| 78 | +``` |
| 79 | + |
| 80 | +The two additions are the new consumer's resources: |
| 81 | +- `module.environment.module.rpc_gateway.kubernetes_manifest.consumer["clientN"]` — the KongConsumer |
| 82 | +- `module.environment.module.rpc_gateway.kubernetes_manifest.consumer_key_external_secret["clientN"]` — the ExternalSecret that pulls the key from Secret Manager into Kong |
| 83 | + |
| 84 | +If the plan shows **anything else** — a change/replace/destroy on an RPC node, helm release, domain, or another consumer — STOP and find out why. Common cause: a deployment change (e.g. a version promotion) has been merged to the branch but not yet applied to live, so applying now would also push that change. You do **not** need to pass image `-var`s if the env's image defaults match the live pods (`kubectl -n <ns> get pods -o jsonpath=... <node>`). If you must add a key without triggering such a pending deployment, temporarily revert that change in your working tree so the plan is clean — but do not commit the revert (see Step 6). |
| 85 | + |
| 86 | +## Step 4 — apply |
| 87 | + |
| 88 | +```bash |
| 89 | +terraform -chdir="$TF" apply -input=false tfplan |
| 90 | +``` |
| 91 | + |
| 92 | +The ExternalSecret has a `wait { Ready }` block, so the GCP secret from Step 1 must already exist or the apply will block waiting for it to sync. |
| 93 | + |
| 94 | +## Step 5 — verify it is live |
| 95 | + |
| 96 | +```bash |
| 97 | +CTX=gke_testnet-440309_us-west1-a_aztec-gke-public |
| 98 | +kubectl --context "$CTX" -n mainnet-rpc get kongconsumer mainnet-client9 # PROGRAMMED=True |
| 99 | +kubectl --context "$CTX" -n mainnet-rpc get externalsecret mainnet-client9-rpc-key-auth # Ready/SecretSynced=True |
| 100 | +``` |
| 101 | + |
| 102 | +(Resource names are `<release-prefix>-clientN` and `<release-prefix>-clientN-rpc-key-auth`.) |
| 103 | + |
| 104 | +## Step 6 — open the PR |
| 105 | + |
| 106 | +The committed Terraform is a record of what is already applied. Keep the PR to a single-purpose, one-line addition. |
| 107 | + |
| 108 | +- **Commit only the `clientN` addition.** If you made a temporary revert in Step 3 to get a clean plan, undo it so it is not committed — the diff must be the `clientN` block and nothing else. Verify with `git diff <base>...HEAD -- <main.tf path>`. |
| 109 | +- Commit message: `chore: add new client` (the established convention). Author is the git author — no Claude attribution. |
| 110 | +- **Base branch: `merge-train/spartan`** for these `spartan/` deploy changes (not `next`). Branch off it, and if the base has moved, rebase the single commit onto it so the PR diff stays client-only. |
| 111 | + |
| 112 | +```bash |
| 113 | +git checkout -b stack/chore-add-new-client-N origin/merge-train/spartan |
| 114 | +# edit main.tf to add clientN |
| 115 | +git add /abs/path/.../environments/mainnet/main.tf |
| 116 | +git commit -m "chore: add new client" |
| 117 | +git push -u origin stack/chore-add-new-client-N |
| 118 | +gh pr create --base merge-train/spartan --head stack/chore-add-new-client-N \ |
| 119 | + --title "chore: add new client" \ |
| 120 | + --body "Adds \`clientN\` to the mainnet RPC consumers. Secret created and already \`terraform apply\`-ed; the plan was the two expected resources (KongConsumer + ExternalSecret). Rate limit 0 = unlimited." |
| 121 | +``` |
| 122 | + |
| 123 | +**Print the PR URL back to the user.** Do not name the private owner in the PR title or body. |
| 124 | + |
| 125 | +If the previous client-add PR merged while you were working (and its branch was deleted), target `merge-train/spartan` directly and rebase your commit onto it so the diff is only the new client. |
| 126 | + |
| 127 | +## Handing the key to the recipient |
| 128 | + |
| 129 | +The key value was never printed (the script piped it straight into Secret Manager). Retrieve it with: |
| 130 | + |
| 131 | +```bash |
| 132 | +gcloud secrets versions access latest --secret=mainnet-rpc-consumer-client9 |
| 133 | +``` |
| 134 | + |
| 135 | +It is a bearer credential — share it over a secure channel, not plain chat/email. The recipient uses it either way: |
| 136 | + |
| 137 | +- Path form: `https://canonical.mainnet.rpc.aztec-labs.com/<KEY>` |
| 138 | +- Header form: `x-aztec-api-key: <KEY>` against `https://canonical.mainnet.rpc.aztec-labs.com/` |
| 139 | + |
| 140 | +The consumer is defined at the environment level, so the key authenticates on **every** keyed route in that environment (e.g. mainnet `v4`, `canonical`, and `v5` hosts) — one key, all routes. |
0 commit comments