carlos / platform Public

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>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev 85c29834e24e528f6af6a24543794871720e708a parent ea3f487
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?