docs: instance moves — direct-mode route ownership blocks custom domains (G17); the edge door admits every enrolled process (G18)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+11
−4
docs/superpowers/specs/2026-09-24-instance-moves-for-every-app-design.md+11 −4
CI cancelled — superseded by a newer push — run details
diff --git a/docs/superpowers/specs/2026-09-24-instance-moves-for-every-app-design.md b/docs/superpowers/specs/2026-09-24-instance-moves-for-every-app-design.md| index 408085190..dc76a001b 100644 |
| --- a/docs/superpowers/specs/2026-09-24-instance-moves-for-every-app-design.md |
| +++ b/docs/superpowers/specs/2026-09-24-instance-moves-for-every-app-design.md |
| @@ -543,8 +543,10 @@ the edge backhauls to whichever box homes a host (`homemap.go:611`, |
| the edge instead of the socket**: |
| - **The door.** A box-local unix socket, `/run/carlos/edge-local.sock`, owned |
| - by the edge, mode 0660, group = a per-app group the app's router unit |
| - joins (`SupplementaryGroups=`). A request on it carries `Host: <instance |
| + by the edge, mode 0660, group = a per-app group that **every enrolled |
| + process of the app** joins (`SupplementaryGroups=`): its router, and |
| + anything else on the box that talks to instances (Tito has two more, a |
| + payments relay and an import-sync loop, G18). A request on it carries `Host: <instance |
| host>` and is handled exactly as if it had arrived from the internet at |
| this edge: local socket if this box homes the host (with the wake), signed |
| backhaul if another box does, holding 503 if it is moving. It is refused |
| @@ -711,6 +713,8 @@ in the region its data is already in, and then moves like any other. |
| | G14 | A replica record passes the move gate and then wedges in `preparing`: reconcile never mints a replica on a platform box, so `OwnsRoute` is false | `apiinstancemove.go:53-72` (no `Role` or `Replicas` check); `reconcile.go:210` | |
| | G15 | Instance-registered once-timers and `NextDue` live in the source box's registry and are lost on a move, silently | `internal/schedule/control.go:28` | |
| | G16 | An old console accepts DELETE on `landing`, which empties the instance | `provision.go:1008-1022` | |
| +| G17 | **A direct-mode `carlos add` files the route under the deployment's operator account, so console claims made as the app's own account are refused.** Every custom-domain claim on Tito's 71 controld hosts fails: `route elatromme.go.tito.io belongs to bab/titogo, claim says bae/titogo` (box journal, 2026-09-24; registry rows show `account=bab`). Adoption fixes it for those hosts by re-owning the row, but direct-mode adds need an explicit account (or to inherit the app's) for everyone else | `internal/domains/aliases.go:104-107`; `carlos add` direct mode | |
| +| G18 | An app's router is not the only process that dials instance sockets: Tito's relay (delivery) and its import-sync loop do too | titogo `internal/relay/flow.go:423-441`; `move_mirrored.go:32-35` | |
| ## 13. Slices |
| @@ -726,7 +730,7 @@ mirror) as early as it can safely be. |
| | **P3** | `CARLOS_HOME_PLACEMENT`; moves to and from home; home children get `CARLOS_REGION` | infra: add `ie` to each sibling's `sibling_staging_arns` | rollback | |
| | **P4** | Move log, terminal state on `GET …/move?history=1`, `landing` + verify, `carlos instances move`; once-timers carried (G15) | P1 | apps stop inferring done | |
| | **P5** | Source retention fence and deletion, re-checked at fire time | P4 | the residency promise | |
| -| **P6** | Adoption: import door (home region), `imports/` restore fallback, `import/stopped`, addr-row swap | P0, and P9 on any box where isolation matters (all of them) | every controld-style instance | |
| +| **P6** | Adoption: import door (home region), `imports/` restore fallback, `import/stopped` and `import/abort` (restores the addr row), addr-row swap that re-owns the route under the record's account (G17) | P0, and P9 on any box where isolation matters (all of them) | every controld-style instance | |
| | **P7** | Edge-local door for app routers | — (uses the existing `carlos-sockets` enrolment) | apps with their own router; makes P9 safe for them | |
| | **P8** | Per-placement store backend; private store and prefix stores follow, with the move-scoped credential | P4, P5 | uploads residency | |
| | **P9** | The isolated layout pass and `carlos system layout migrate` (isolated-exec plan 4, written, not built), then run on carlos-tito-ie | P7 on any deployment whose app router computes socket paths | isolation for adopted instances (#126) | |
| @@ -774,7 +778,10 @@ not a platform one. |
| can read one tenant store for the length of a move. Acceptable, or |
| should store copies run as the tenant's own store credential, which the |
| instance already holds, in a hook the app runs? |
| -7. **Adoption downtime.** Adoption stops the instance for the snapshot, |
| +7. **Route ownership for direct-mode adds (G17).** Should `carlos add` |
| + without a console take an explicit `--account`, inherit the app's |
| + account, or refuse when the host's app belongs to another account? |
| +8. **Adoption downtime.** Adoption stops the instance for the snapshot, |
| upload, swap and first wake (§9). For Tito's accounts that is a minute |
| or two each. Is a scheduled, per-account window acceptable, or does |
| adoption need a replicate-then-cut shape of its own? |