region HA spec 2, rev 13: the provider boundary (core vs AWS adapters, import test, no-AWS path), at Paul's request
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+34
−1
docs/superpowers/specs/2026-10-15-region-ha-two-box-regions-design.md+34 −1
diff --git a/docs/superpowers/specs/2026-10-15-region-ha-two-box-regions-design.md b/docs/superpowers/specs/2026-10-15-region-ha-two-box-regions-design.md| index 927748fad..7d684ee4b 100644 |
| --- a/docs/superpowers/specs/2026-10-15-region-ha-two-box-regions-design.md |
| +++ b/docs/superpowers/specs/2026-10-15-region-ha-two-box-regions-design.md |
| @@ -1,6 +1,8 @@ |
| # Region HA, spec 2: two-box regions |
| -Design, 2026-10-15, **rev 12**. DRAFT. Nothing here is built. |
| +Design, 2026-10-16, **rev 13**. Approved by Paul on 2026-10-16, subject to this revision. Nothing here is built. |
| + |
| +**Rev 13** adds §25, the provider boundary, at Paul's request. Spec 2 must not make the platform depend on AWS: a deployment stays "one object-store bucket + N hosts running the `carlos` binary + DNS" (README), and its AWS-specific parts are adapters. |
| **Rev 12** answers Astra's round 11 (`CODEX-FINDINGS-REGION-HA-SPEC2-11.md`, **A11-1**, P2, accepted). Rollback's exclusion is set on the region's group, and every box the group launches inherits it at enrolment, so a replacement can't reopen placement. §24. |
| @@ -540,3 +542,34 @@ All three were answered in Astra's round 1 and folded in: |
| | Finding | Disposition | |
| |---|---| |
| | A11-1 P2: rollback's exclusion doesn't survive box replacement | Accepted: a role on the region's group entry in the roster, inherited at enrolment in the same write, and applied to current members when changed (§7, §9) | |
| + |
| +## 25. The provider boundary (rev 13) |
| + |
| +Spec 2 is AWS-first because carloku.com runs on AWS. The platform isn't: a self-hosted deployment is one object-store bucket (S3-compatible, MinIO included), N hosts running `carlos`, and DNS. So every part of spec 2 is either **provider-neutral core** or an **AWS adapter** behind a small interface. The core never imports the AWS SDK. |
| + |
| +**Core.** These work on any hosts, behind any load balancer or none: |
| + |
| +| Piece | Without AWS | |
| +|---|---| |
| +| Placement policy, the `legacy`/`platform` box roles, the group role, pending creates (§7) | Unchanged. None of it calls a cloud API | |
| +| The health answer `/.carlos/lb-health` (§6) | Plain HTTP. Any load balancer (HAProxy, nginx, a hosting provider's) or a monitor can use it | |
| +| Forwarding by private address in a region, own public address between regions (§6) | Addresses come from an **address source**. Without AWS: `CARLOS_PRIVATE_IP` and `CARLOS_PUBLIC_IP` in `host.env`, or the primary interface's address when unset | |
| +| Joining and hosting modes (§5) | Generic. Enrolment uses the **membership confirmer** interface. Without AWS, there is none, and enrolment is operator-approved through the existing `hostactors.Enroll` path | |
| +| `host.env` (§5) | A file, as today. The SSM reader is one way of writing it | |
| +| The channel-release install and the guard (§5) | Unchanged. They read the bucket and the release key | |
| +| Single-box assumptions (§8) | Fixed in core, so they apply to every multi-box deployment | |
| + |
| +**AWS adapters.** They are loaded only when the deployment declares AWS, and they load the SDK through `internal/rootaws`, as every AWS client does since #267: |
| +- the **membership confirmer**: Auto Scaling `DescribeAutoScalingInstances` (H16, H17); |
| +- the **SSM and KMS parameter reader** that writes `host.env`; |
| +- the **IMDS address source**; |
| +- **termination evidence** (S1, spec 3); |
| +- the **`region-host` tofu module's group mode**: launch template, Auto Scaling group, NLB, alarms (§4). |
| + |
| +**What a deployment without AWS doesn't get.** |
| +- A sick box isn't replaced or enrolled automatically. An operator replaces it and approves its enrolment. |
| +- No automatic failover (spec 3). It triggers on a box's verified termination (H1), which has no equivalent without a provider's record. A provider adapter could supply one later, behind the same interface. |
| + |
| +**Enforcement.** |
| +- An import test: the core packages spec 2 touches (`internal/boxchoice`, `internal/edge`, `internal/host` outside its adapter files, `internal/console`, `internal/hostactors`) import no `github.com/aws/...` package. Adapter files live in clearly named files or packages, for example `internal/host/awsaddr.go` and `internal/hostactors/awsmembership`. |
| +- A test runs the core path with no AWS configuration at all: two boxes with `CARLOS_PRIVATE_IP`/`CARLOS_PUBLIC_IP`, operator-approved enrolment, placement, forwarding and the health answer. |