docs: region flags, console env and release checklist (nearest-region D1-D10)
D1's example fence gains the sh info string and D9's record file now points at docs/site/cli.md, where it was placed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 files changed,
+70
−2
README.md+46 −0docs/site/cli.md+22 −0docs/superpowers/plans/2026-10-02-nearest-region-copy.json+2 −2
diff --git a/README.md b/README.md| index c01f67ea8..e9a98bd3f 100644 |
| --- a/README.md |
| +++ b/README.md |
| @@ -254,10 +254,44 @@ carlos system update --console <console> --version $v # then: carlos sys |
| Last, roll it: `carlos system update --console <console> --version vX.Y.Z`, then `carlos system status`. Every box should read `current` within the spread. A box that reads `rolled-back` or `refused` says why on the same line; fix that box, and do not re-cut the release. |
| +- Refresh the embedded timezone tables when a new IANA tzdata release is out: |
| + `scripts/update-tzgeo.sh <version>`, then `go test ./internal/geo`. |
| + |
| A release-specific note goes here when a release changes something an |
| operator needs to check or act on before rolling it. Read the note for |
| the release you are about to roll. |
| +**App regions rollback floor.** Once any app holds a region other than the |
| +home, do not roll the console binary below the first release that |
| +understands app regions: an older console places a no-region create at the |
| +home. That release's version is named here, in its release notes, and in the |
| +console roll recipe in carloku-infrastructure; check all three name the same |
| +version before turning `CONSOLE_APP_REGIONS` on. If a rollback below the |
| +floor happens anyway, write down when it started and ended: on its next |
| +start the console logs "possible placement discrepancy" lines, and the |
| +interval tells you which to review. Turning the switch off is the feature's |
| +own rollback: it stops new non-home regions and keeps existing ones in force. |
| + |
| +**Turning app regions on.** In order: |
| + |
| +1. Stage the deployment's `CARLOS_SECRETS_KEY` on every box in every region |
| + that should take placed instances, and set `CARLOS_SECRETS_PUBKEY` on the |
| + console. A box without the key runs placed instances with no config, no |
| + secrets and no store. |
| +2. Roll the box release that publishes `serves_shared_pool`, |
| + `accepts_inbound` and `secrets_key_fp`. Check every region's |
| + `control/edges/*.json` carries them, and that each `secrets_key_fp` equals |
| + the console's. |
| +3. Roll the console with the switch off. |
| +4. On dev, turn the switch on. Create an app and an instance, read their |
| + region, and run the service gate below for every region. |
| +5. On production, with the switch still off, create one probe instance per |
| + eligible region with an explicit `--region`, deploy a known build, and run |
| + `scripts/region-service-gate.sh <probe-host> <build> <every other edge EIP>` |
| + for each. Every region must pass. Delete the probes. |
| +6. Turn the switch on. Re-run step 5 after any period with the switch off, |
| + and whenever a new region becomes eligible. |
| + |
| ### Tenant email senders, plan 2 of 5 (2026-09-21) |
| This release turns the one implicit per-app email sender into an explicit |
| @@ -493,6 +527,15 @@ publish an inflated instance count. |
| (guidance text), so the two can never name different addresses. Default: |
| the box's public IP via IMDS, best-effort; if neither is knowable the |
| checks stand down and health reads unknown, never a guess. |
| +- `CONSOLE_APP_REGIONS`: `on` lets new apps be given a region other than the |
| + home. It needs `CONSOLE_HOME_REGION` set too. With the home unnamed, the |
| + console logs once and leaves app regions off. Default off. Automatic |
| + placement outside the home also needs `CARLOS_SECRETS_PUBKEY`, so the |
| + console can check that a box holds the deployment's secrets key. |
| +- `CONSOLE_GEOIP_DB`: the path to an operator-supplied MaxMind-format `.mmdb`. |
| + DB-IP Lite is recommended, and it requires the attribution the new-app page |
| + shows. Unset or unusable turns the IP step of the region guess off, and the |
| + console starts either way. Replace the file by atomic rename, then restart. |
| **Canonical-host caveat**, stated honestly: an app that pins its own host |
| (e.g. keymail's `--host keymail.dev`) will redirect or 404 on its platform |
| @@ -579,6 +622,9 @@ no single reader owns the whole contract: |
| operator-set, coarse region word (e.g. `"EU · Ireland"`) carried onto |
| this box's snapshot verbatim; unset omits the field. Free text the |
| operator chooses, never derived from cloud metadata. |
| + Keep it geographically consistent with the region's place name in the |
| + console's region picker (for example "EU · Ireland" beside |
| + "Ireland · Dublin"); it may stay coarser. |
| - `STATUS_HOST` / `STATUS_PUBLIC` — not read by the agent process itself; |
| consumed by ops/tofu to decide whether the edge fronts the bucket's |
| `status/` prefix with a public hostname. `STATUS_PUBLIC=1` publishes the |
diff --git a/docs/site/cli.md b/docs/site/cli.md| index 25ee987ec..0c3b5d553 100644 |
| --- a/docs/site/cli.md |
| +++ b/docs/site/cli.md |
| @@ -119,6 +119,14 @@ uses, so a name that form would refuse is refused here too. |
| carlos apps create --app hello |
| ``` |
| +`--region <code>` sets the region the app's instances start in. Without it, |
| +the console picks the region nearest you from your timezone, and the command |
| +prints one line saying which and why: |
| + |
| +```sh |
| +carlos apps create --app hello --region uk |
| +``` |
| + |
| A name is also refused when its platform apex, `<app>.<account>.<apps |
| domain>`, is already taken: by another app's instance, even one in the Trash, |
| or by any route there at all. The new app would otherwise never get an address |
| @@ -244,6 +252,11 @@ having no instances to resolve a channel from. |
| carlos deploy --app website --kind static --host www.example.com ./dist |
| ``` |
| +When `carlos deploy` creates your first app, it sends your timezone so the |
| +console can pick the region nearest you, and prints the choice. `--region |
| +<code>` chooses instead. On an app that already exists, `--region` must |
| +match the app's region, or deploy stops before shipping anything. |
| + |
| #### Deploying one checkout to several places |
| When one project deploys to more than one console, or as more than one app, |
| @@ -298,6 +311,10 @@ has to be both a channel segment and a hostname label, an explicit `--name` |
| is checked rather than quietly repaired — lowercase letters, digits and |
| dashes. |
| +`--region <code>` places a new canary in that region (default: the app's |
| +region). It is refused for an existing canary in another region, and for an |
| +app that runs on one of your fleets. |
| + |
| Promotion into a canary is never refused, on any app, from any rung. Nor |
| does a canary cost you a rung: canary channels sit outside every pipeline, so |
| a build that has been on one still enters the pipeline as a fresh build. |
| @@ -586,6 +603,8 @@ carlos instances enable --app hello |
| carlos instances list --app hello |
| ``` |
| +Without `--region`, a new instance starts in the app's region. |
| + |
| `--health <slug>` narrows the listing to one state: `running`, `asleep`, |
| `not-responding` and the rest. That is usually what you want when something |
| is wrong. |
| @@ -1408,6 +1427,9 @@ of its own. A person enables the app once and the credential provisions inside |
| those bounds afterwards, including minting a tenant's bucket it could never |
| have created directly. |
| +A manifest's `regions` allow-list compares the home as `""`, so listing the |
| +home's code does not admit the home. |
| + |
| The secret prints once, same as a fleet token, and the same distinction |
| applies at the other end: `rotate` refuses the credential's next call with |
| the old secret, `revoke` stops it whatever secret it holds. |
diff --git a/docs/superpowers/plans/2026-10-02-nearest-region-copy.json b/docs/superpowers/plans/2026-10-02-nearest-region-copy.json| index 19c76ea5e..19e4da7cf 100644 |
| --- a/docs/superpowers/plans/2026-10-02-nearest-region-copy.json |
| +++ b/docs/superpowers/plans/2026-10-02-nearest-region-copy.json |
| @@ -557,7 +557,7 @@ |
| { |
| "id": "D1", |
| "file": "docs/site/cli.md", |
| - "text": "`--region <code>` sets the region the app's instances start in. Without it,\nthe console picks the region nearest you from your timezone, and the command\nprints one line saying which and why:\n\n```\ncarlos apps create --app hello --region uk\n```" |
| + "text": "`--region <code>` sets the region the app's instances start in. Without it,\nthe console picks the region nearest you from your timezone, and the command\nprints one line saying which and why:\n\n```sh\ncarlos apps create --app hello --region uk\n```" |
| }, |
| { |
| "id": "D2", |
| @@ -596,7 +596,7 @@ |
| }, |
| { |
| "id": "D9", |
| - "file": "README.md", |
| + "file": "docs/site/cli.md", |
| "text": "A manifest's `regions` allow-list compares the home as `\"\"`, so listing the\nhome's code does not admit the home." |
| }, |
| { |