| 1 | # Nearest region by default: plan summary for review |
| 2 | |
| 3 | Plan: `2026-10-02-nearest-region-default.md` (rev 7). Spec: |
| 4 | `../specs/2026-10-01-nearest-region-default-design.md` (rev 6.2). Scope is |
| 5 | placement only (R8): no DNS writes, no steering, no latency routing. |
| 6 | |
| 7 | ## What ships, by release |
| 8 | |
| 9 | | Release | Tasks | What changes for people | |
| 10 | | --- | --- | --- | |
| 11 | | Box release | 1, 1b | Nothing visible. Each box's edges file also says whether it takes ordinary instances (`serves_shared_pool`, `accepts_inbound`) and which secrets key it holds (`secrets_key_fp`). | |
| 12 | | Console release, switch off | 2 to 17, 23 | Apps gain a stored region (the home for now). `GET …/regions` gains place names and `app_default` (with `region_known`, `placement_known`, `instance_domain`). A new guess endpoint. An explicit create into a region whose boxes provably lack the secrets key is refused (C31). Nothing non-home is written. | |
| 13 | | Switch on (flagship) | none (config) | New apps get the region nearest the person (timezone, then IP, then home), shown in a picker when two or more regions are eligible. New instances default to their app's region. | |
| 14 | | CLI release | 18 to 22 | `--region` on `apps create`, `deploy` (new apps only) and `canary`; the CLI sends a timezone hint and prints the place. | |
| 15 | | Docs | 24 | `docs/site/cli.md` flags; README release checklist, floor version and "Turning app regions on". | |
| 16 | | Later, own repo | none here | titogo adopts the guess endpoint and place names. | |
| 17 | |
| 18 | ## Tasks (one line each) |
| 19 | |
| 20 | 0. Copy inventory and review; write the approved copy record. Nothing else starts before it. |
| 21 | 1. Boxes publish shared-pool and inbound eligibility, from the reconciler's own predicates. |
| 22 | 1b. Boxes publish their secrets-key fingerprint (R9). |
| 23 | 2. `internal/geo`: points, distance, nearest pick. |
| 24 | 3. `internal/geo`: embedded tz zone table and country points. |
| 25 | 4. AWS region table and its 34 place names. |
| 26 | 5. Optional GeoIP locator (MaxMind format, pinned test fixtures). |
| 27 | 6. Console page copy. |
| 28 | 7. Console API messages, including the empty-list variants. |
| 29 | 8. Store: `apps.region` and `apps.region_source`, carried through every rebuild. |
| 30 | 9. Console config: the switch, the named home, the expected secrets fingerprint, test seams. |
| 31 | 10. Strict edges read, region candidates, one-box eligibility (incl. R9), app policy. |
| 32 | 11. The guess, with its invariant property test. |
| 33 | 12. Region resolution inside `claimApp`; the API create door takes region and timezone. |
| 34 | 13. The new-app page picker and browser guess route; no-picker form byte-identical. |
| 35 | 14. The instance create door: fill from the app region, eligibility, the R9 refusal, place names. |
| 36 | 15. `app_default`, place names and `POST …/regions/guess`. |
| 37 | 16. The Instances tab region lines. |
| 38 | 17. The roll-forward discrepancy log. |
| 39 | 18. CLI copy. |
| 40 | 19. Timezone detection on the person's machine. |
| 41 | 20. Create-app wire, `apps create --region`, the onboarding line. |
| 42 | 21. `deploy --region` on an existing app, checked before anything ships. |
| 43 | 22. `canary --region`, read-only checks before any write; instance output names the place. |
| 44 | 23. The region service-gate script, proved against a real backhaul. |
| 45 | 24. Docs and the release checklist. |
| 46 | 25. Whole-branch gate (build, vet, gofmt, tests, five cross-compile targets, copy checks). |
| 47 | |
| 48 | ## The copy-review gate (Task 0) |
| 49 | |
| 50 | Every string the plan writes is reviewed with the copy-review skill before |
| 51 | any product file holds it, and Task 25 proves the approved text landed byte |
| 52 | for byte. The inventory is **121 strings**: |
| 53 | |
| 54 | - 44 user-facing messages and labels (C and R entries, including C16b to |
| 55 | C19b and C30c added during review); |
| 56 | - 34 place names (C23); |
| 57 | - 10 doc passages (D1 to D10); |
| 58 | - 20 operator log lines (O1 to O20); |
| 59 | - 13 script, usage and fixture strings (S1 to S3, G1 to G6, U1 to U3, F1). |
| 60 | |
| 61 | Two notes for the review: C25's draft says `carlos canary remove`, but the |
| 62 | verb is `carlos canary rm`; and C24 needs DB-IP's required attribution |
| 63 | wording confirmed. |
| 64 | |
| 65 | ## Rollout (plan "Rollout"; spec §17) |
| 66 | |
| 67 | | # | Step | Needs Paul | |
| 68 | | --- | --- | --- | |
| 69 | | 1 | Audit manifests' `regions`; boxes publish their AWS region; set `CONSOLE_HOME_REGION` and `CARLOS_SECRETS_PUBKEY` on the console; name the floor version in README, release notes and the roll recipe. | approve the console config | |
| 70 | | 2 | Stage `CARLOS_SECRETS_KEY` on every box that should take placed instances. **Done for the flagship (2026-10-03).** | only for other deployments | |
| 71 | | 3 | Box release everywhere; check every `secrets_key_fp` equals the console's. | approve the production roll | |
| 72 | | 4 | Console release with the switch unset. | approve the production roll | |
| 73 | | 5 | Dev: switch on, browser and API creates, `/regions` diff, service gate. | no | |
| 74 | | 6 | Production service gate with the switch off: probe, deploy, gate from outside and through every edge. | no (report to Paul) | |
| 75 | | 7 | Flagship: switch on. The R7 rollback floor applies from here. | **yes: his call** | |
| 76 | | 8 | CLI release. | approve the release | |
| 77 | | 9 | titogo adopts the guess and names (its own repo). | yes | |
| 78 | |
| 79 | ## Risks |
| 80 | |
| 81 | - **A placed instance on a box without the secrets key runs with no config, |
| 82 | secrets or store** (R9, the kpmg outage). Automatic choices need the key on |
| 83 | one box and explicit creates are refused when it is provably absent. |
| 84 | Rollout step 3 checks the fingerprints. The flagship now has the key |
| 85 | everywhere. |
| 86 | - **Rolling the console back below the floor** makes non-home apps create |
| 87 | instances at home again. The rule is a runbook floor (R7), and the |
| 88 | discrepancy log (Task 17) catches what slips through. |
| 89 | - **No latency routing yet** (R8). A non-home instance is reached through the |
| 90 | home's DNS and the backhaul until the follow-on spec. The service gate |
| 91 | (Task 23) proves reachability, not speed. |
| 92 | - **Mixed versions.** Every wire change is additive. An old CLI is treated as |
| 93 | sending no hints. A new CLI against an old console skips deploy's check, |
| 94 | and refuses `canary --region` rather than guess. |
| 95 | - **Guess quality.** Timezone first, then a GeoIP database if configured, then |
| 96 | home. The tz table is a release-checklist refresh; a stale one only misses |
| 97 | new zone names. |
| 98 | - **Local test environment.** On this machine the root package's full suite |
| 99 | is blocked by the operator's `~/.carlos/config`, and a few `internal/edge` |
| 100 | and `internal/activator` tests fail identically on the unmodified base. CI |
| 101 | is the real gate (Task 25). |
| 102 | - **Review size.** 121 strings is a long copy review, and it gates every |
| 103 | task after Task 0. |
| 104 | |