carlos / platform Public

spec: instance sizes and egress allows, rev 7 (Astra R6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev db65cac044523e48ca77c7b4a94c1173e1c8a3e0 parent 1f53efd
2 files changed, +59 −6
  • docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md +34 −6
  • docs/superpowers/specs/CODEX-FINDINGS-instance-sizes-egress-R6.md +25 −0
diff --git a/docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md b/docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md
index 897b35397..35d4d276b 100644
--- a/docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md
+++ b/docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md
@@ -1,9 +1,9 @@
# Instance sizes and egress allows
Amadan #328 (an isolated instance reaching a private service its operator
-runs) and #329 (operator-set CPU and memory per instance). Revision 6,
-2026-10-21. Revisions 2 to 6 answer the builder check, Astra rounds
-1 to 5, and Paul's ruling on fleet addresses (`CODEX-FINDINGS-instance-sizes-egress-R<n>.md`); the
+runs) and #329 (operator-set CPU and memory per instance). Revision 7,
+2026-10-21. Revisions 2 to 7 answer the builder check, Astra rounds
+1 to 6, and Paul's ruling on fleet addresses (`CODEX-FINDINGS-instance-sizes-egress-R<n>.md`); the
dispositions are §9.
Both are the same mechanism: **an operator-set policy for an app and its
@@ -141,9 +141,29 @@ that way.
purges alike. As a second guard, the console's app-create door deletes
any policy object still at that key before the name is usable. A new
app of an old name therefore never inherits an egress grant or a
- default size from its predecessor. Residual: only a console downgraded
- to a version without this sweep, after policies exist, could leave one
- behind; the release notes say so.
+ default size from its predecessor.
+- **Older consoles** (Astra R6, R5-1): a console without this sweep that
+ shares the bucket could purge an app and recreate its name while a
+ policy exists (its purge, `ownership.go:514`, never touches
+ `control/app-policy/`; its create, `server.go:4130`, never clears it).
+ Two rails:
+ - **Rollout rule:** no policy is granted until every console that
+ writes the deployment's bucket runs this version. A deployment runs
+ one console per bucket today (temporary static sites spec, "one
+ console per deployment bucket, which is how every deployment runs"),
+ so the console that serves the policy door is the one that purges
+ and creates; a deployment with more than one upgrades them all first.
+ A console downgraded below this version after policies exist must
+ delete them first; the release notes and the downgrade runbook say
+ so.
+ - **With host claims on, the policy is bound to the app's incarnation:**
+ it carries the app's `app_id` (`ClaimAppID`, the identity the claim
+ purge journal and admissions already use, `hostops/admission.go`),
+ and root ignores a policy whose `app_id` is not the current app's,
+ reporting `policy for another incarnation`. Any purge and recreate,
+ by any console version, then fails safe under claims. (Before claims
+ there is no store-visible app incarnation; the rollout rule is the
+ rail there.)
- **Precondition the builder must verify and record in the plan:** the
key is under the store's control boundary (`hostops/mutation_boundary.go`
and the bucket policy), so no app, build, instance or member
@@ -458,6 +478,8 @@ default and the overrides.
- An older console cannot erase the policy: it never writes
`control/app-policy/`.
- Boxes before consoles, dev first (standing roll order).
+- No policy is granted until every console writing the bucket runs this
+ version (§3, older consoles).
- Root code changes: a census overlay and a #280 ruling before merge.
- Nothing is on by default. With no policy object, every unit, slice and
probe line is byte-identical to today.
@@ -534,6 +556,12 @@ does exist, at `internal/activator/`.
| new 1 | A recreated app of the same name inherits the policy | Accepted: the purge sweeps the policy object, and app creation clears a lingering one (§3) |
| new 2 | A test still required root to enforce other boxes' addresses | Accepted: test and anti-goal reworded to ruling 8 (§2, §11) |
+**Astra round 6** (REJECT; everything else resolved, no new findings):
+
+| # | Finding | Disposition |
+|---|---|---|
+| R5-1 | An older console in a mixed rollout can purge and recreate under a live policy | Accepted: rollout rule (every console upgraded before the first grant), and an `app_id` binding root checks under claims (§3) |
+
## 10. Left to the builder
- Column names, the call shapes, and the status snapshot fields.
diff --git a/docs/superpowers/specs/CODEX-FINDINGS-instance-sizes-egress-R6.md b/docs/superpowers/specs/CODEX-FINDINGS-instance-sizes-egress-R6.md
new file mode 100644
index 000000000..87170b407
--- /dev/null
+++ b/docs/superpowers/specs/CODEX-FINDINGS-instance-sizes-egress-R6.md
@@ -0,0 +1,25 @@
+R1-1: **RESOLVED** — Populated-slice skips and resize serialization remain.
+R1-2: **RESOLVED** — Growth updates generation limits and captured tier.
+R1-3: **RESOLVED** — Requested memory reductions require an empty slice.
+R1-4: **RESOLVED** — Failed writes are retried against read-back.
+R1-5: **RESOLVED** — Superseded by ruling 8.
+R1-6: **RESOLVED** — All claim validators use the shared size table.
+R1-7: **RESOLVED** — Separate storage protects policy from older manifest writers.
+R1-8: **RESOLVED** — The declined finding matches the retained size trust model.
+R1-9: **RESOLVED** — `HighOf` remains authoritative.
+R2-1: **RESOLVED** — Memory reductions wait for an empty slice.
+R2-2: **RESOLVED** — Publication requirements cover region HA being off.
+R2-3: **RESOLVED** — Instance overrides are bound to `CreatedAt`.
+R3-1: **RESOLVED** — Pending reductions preserve the incumbent’s size through swaps.
+R3-2: **RESOLVED** — Superseded by ruling 8.
+R3-3: **RESOLVED** — Account-scoped policy reads through the fleet door are specified.
+R4-1: **RESOLVED** — Missing publishers are explicitly accepted and reported.
+R4-2: **RESOLVED** — Root no longer depends on cached fleet completeness.
+
+R5-1: **PARTLY (P1)** — Upgraded purge/create paths are covered, but the [downgrade-only residual](docs/superpowers/specs/2026-10-21-instance-sizes-and-egress-design.md#L134) is factually too narrow. During the permitted mixed-version rollout, a new console can grant policy while an older console still handles an account owner’s purge and recreation. The older purge calls `release.PurgeApp` and frees the name ([ownership.go:514](internal/console/ownership.go#L514), [ownership.go:544](internal/console/ownership.go#L544)); its sweep excludes `control/app-policy/` ([trash.go:272](internal/release/trash.go#L272)). The older create path checks the app prefix and recreates the row without clearing policy ([server.go:4130](internal/console/server.go#L4130), [server.go:4193](internal/console/server.go#L4193)). The replacement therefore inherits the grant without any downgrade or new operator decision. **Smallest fix:** require every console capable of purge/create to be upgraded before policy grants are enabled, or bind policy to a verified app incarnation.
+
+R5-2: **RESOLVED** — The anti-goal and tests now distinguish local-address removal from reporting another box’s later acquisition.
+
+No new findings.
+
+REJECT
\ No newline at end of file