| 1 | # Brief: review the P7 edge-door plan |
| 2 | |
| 3 | Review `docs/superpowers/plans/2026-09-28-instance-moves-p7-edge-door.md` |
| 4 | against the code in this checkout (branch `instance-moves-p7`, based on |
| 5 | `main` with P0 merged) before anyone builds it. |
| 6 | |
| 7 | The design it implements is on branch `docs-instance-moves-design`: |
| 8 | `git show origin/docs-instance-moves-design:docs/superpowers/specs/2026-09-24-instance-moves-for-every-app-design.md`. |
| 9 | Read §0 (Paul's rulings, fixed: do not re-argue them), §8 and §10. Your own |
| 10 | recommendations that became those rulings are in |
| 11 | `CODEX-RULINGS-INSTANCE-MOVES.md` beside it. |
| 12 | |
| 13 | ## What was decided while planning, and why |
| 14 | |
| 15 | - **The credential is a console-minted random key whose SHA-256 sits in a |
| 16 | record under `control/door-keys/<account>/<app>.json`, signed with the |
| 17 | deployment's root release key.** Boxes verify it against |
| 18 | `CARLOS_RELEASE_PUBKEY`, which the edge already holds (`e.staticPub`). |
| 19 | Rejected: the relay's HKDF-per-box-master pattern (a master staged by hand |
| 20 | on every box, an infra change per box), and an unsigned hash record (any |
| 21 | bucket writer could add a key, discussion #171). |
| 22 | - **Remote ownership comes from new `account`/`app` fields in each box's |
| 23 | edges file**, so the request path never reads the bucket (the edge's rule). |
| 24 | A host whose publisher predates the fields is refused. |
| 25 | - **The door calls the ordinary `Edge.ServeHTTP`** after authenticating, |
| 26 | stripping the key and the hop headers, checking ownership, and setting a |
| 27 | context mark. The mark widens X-Forwarded-For trust in `setForwarded` and |
| 28 | `clientIP` only; the backhaul one-hop refusal still reads `hopVerified`. |
| 29 | - **Dormant:** no socket unless `CARLOS_EDGE_DOOR` names a path. |
| 30 | |
| 31 | ## What to look for |
| 32 | |
| 33 | 1. **Authorization holes.** Any request a door caller can shape that reaches |
| 34 | an instance of another app, or the tunnel host, the store door, the |
| 35 | steering probe, or any other `ServeHTTP` branch that runs before the route |
| 36 | lookup. Specifically check the login POST, which `ServeHTTP` re-resolves by |
| 37 | its `next` parameter (edge.go around 1400-1446), not by `r.URL.Path`, while |
| 38 | the door checks ownership by `r.URL.Path`. |
| 39 | 2. **Trust leaks.** Whether rewriting `RemoteAddr` from the caller's |
| 40 | X-Forwarded-For, or the `forwardedTrusted` widening, lets a door caller |
| 41 | spoof anything the edge or the gate relies on beyond the visitor address. |
| 42 | 3. **The signing choice.** Is signing door-key records with the root release |
| 43 | key (the key that moves `stable`) acceptable, or does it need a scoped |
| 44 | grant (`internal/release/grant.go`)? Does any existing verifier accept a |
| 45 | root-signed wrapper of a different kind, so that a door-key record could be |
| 46 | replayed as something else, or vice versa? |
| 47 | 4. **Revocation and failure behaviour.** The 30 s mirror; dropping records |
| 48 | that fail verification but keeping the last map on a list failure; what a |
| 49 | console with no signing key does. |
| 50 | 5. **Plan quality.** Tasks whose interfaces don't line up, steps that name |
| 51 | helpers or fixture fields that don't exist (check each against the code), |
| 52 | tests that would pass without the change, and anything the rulings require |
| 53 | that no task builds. |
| 54 | |
| 55 | Cite code as `path:line` and check each citation. Findings first, ranked |
| 56 | P1 (must fix before building) / P2 (should fix) / P3 (note), each with the |
| 57 | failure scenario and a suggested fix. Then one line on whether the plan is |
| 58 | ready to build once the P1s are fixed. Keep it under about 2,000 words. |
| 59 | |