carlos / platform Public

edge asset cache plan 3: far catch on the exact-remote path too (Astra plan round 5)

Summary

The far cache now also runs where a local wildcard yields to another box's exact host, and never for door callers or verified hops.
Agent notes
Findings committed unedited as CODEX-FINDINGS-ASSET-CACHE-PLANS-R5.md.
claude agent, operated by paul@keymail.dev fff230e79db33d95a490e0075fe490c5ab6b9e25 parent 10409cf
2 files changed, +16 −2
  • docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-3-serve.md +5 −2
  • docs/superpowers/specs/CODEX-FINDINGS-ASSET-CACHE-PLANS-R5.md +11 −0

CI cancelled — superseded by a newer push — run details

CI log
=== gate ===
.amadan/test-gate.sh
=== launchctx (0 s)
ok  	amadan.net/carlos/platform.git/launchctx	0.014s
?   	amadan.net/carlos/platform.git/launchctx/controlname	[no test files]
?   	amadan.net/carlos/platform.git/launchctx/wireid	[no test files]
=== vet (1 s)
=== gofmt (0 s)
gofmt -l .: clean
=== rest (147 s)
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	3.602s
ok  	github.com/carlosframework/platform/cmd/moveprobe	11.871s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.742s
ok  	github.com/carlosframework/platform/internal/activator	56.842s
ok  	github.com/carlosframework/platform/internal/agentsock	0.041s
ok  	github.com/carlosframework/platform/internal/alerting	0.007s
ok  	github.com/carlosframework/platform/internal/analytics	0.034s
ok  	github.com/carlosframework/platform/internal/appconfig	3.101s
ok  	github.com/carlosframework/platform/internal/apperr	0.151s
ok  	github.com/carlosframework/platform/internal/applog	0.275s
ok  	github.com/carlosframework/platform/internal/awsinv	0.007s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.006s
ok  	github.com/carlosframework/platform/internal/boxid	0.990s
?   	github.com/carlosframework/platform/internal/boxid/boxidtest	[no test files]
?   	github.com/carlosframework/platform/internal/boxid/internal/signtest	[no test files]
ok  	github.com/carlosframework/platform/internal/boxvolumes	0.012s
ok  	github.com/carlosframework/platform/internal/caps	0.014s
ok  	github.com/carlosframework/platform/internal/certwarm	0.002s
ok  	github.com/carlosframework/platform/internal/cgroup	0.761s
ok  	github.com/carlosframework/platform/internal/changelog	0.506s
ok  	github.com/carlosframework/platform/internal/claimactivation	1.358s
ok  	github.com/carlosframework/platform/internal/clamd	0.056s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
?   	github.com/carlosframework/platform/internal/controlname	[no test files]
?   	github.com/carlosframework/platform/internal/controlsock	[no test files]
ok  	github.com/carlosframework/platform/internal/dbdoor	8.009s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.011s
ok  	github.com/carlosframework/platform/internal/dbquery	0.006s
ok  	github.com/carlosframework/platform/internal/dbreads	2.173s
?   	github.com/carlosframework/platform/internal/dbreads/dbreadstest	[no test files]
ok  	github.com/carlosframework/platform/internal/dbverify	0.004s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.004s
ok  	github.com/carlosframework/platform/internal/docsite	0.007s
ok  	github.com/carlosframework/platform/internal/domains	2.923s
ok  	github.com/carlosframework/platform/internal/doorkey	0.009s
ok  	github.com/carlosframework/platform/internal/economics	0.008s
ok  	github.com/carlosframework/platform/internal/edge	45.374s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.008s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.213s
ok  	github.com/carlosframework/platform/internal/gate	1.817s
ok  	github.com/carlosframework/platform/internal/genselect	0.022s
ok  	github.com/carlosframework/platform/internal/geo	0.019s
ok  	github.com/carlosframework/platform/internal/geoip	0.037s
ok  	github.com/carlosframework/platform/internal/historyreadback	0.270s
ok  	github.com/carlosframework/platform/internal/host	143.854s
ok  	github.com/carlosframework/platform/internal/hostactors	6.292s
ok  	github.com/carlosframework/platform/internal/hostactors/awsmembership	0.004s
ok  	github.com/carlosframework/platform/internal/hostbox	3.075s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.867s
ok  	github.com/carlosframework/platform/internal/hostenv	0.008s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.011s
ok  	github.com/carlosframework/platform/internal/hostexec	5.576s
ok  	github.com/carlosframework/platform/internal/hostfence	0.075s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.268s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.022s
ok  	github.com/carlosframework/platform/internal/hostguard	1.084s
ok  	github.com/carlosframework/platform/internal/hostops	2.271s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.002s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.003s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.706s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.171s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.002s
ok  	github.com/carlosframework/platform/internal/infraview	0.004s
ok  	github.com/carlosframework/platform/internal/instancectl	5.249s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.795s
ok  	github.com/carlosframework/platform/internal/instancefs	0.006s
ok  	github.com/carlosframework/platform/internal/instancekey	0.004s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.012s
ok  	github.com/carlosframework/platform/internal/layout	1.818s
ok  	github.com/carlosframework/platform/internal/lbhealth	0.002s
ok  	github.com/carlosframework/platform/internal/lease	10.141s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.613s
ok  	github.com/carlosframework/platform/internal/ledger	0.013s
ok  	github.com/carlosframework/platform/internal/mail	0.301s
ok  	github.com/carlosframework/platform/internal/maintenance	0.098s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.573s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.078s
ok  	github.com/carlosframework/platform/internal/metrics	0.007s
ok  	github.com/carlosframework/platform/internal/moves	21.432s
ok  	github.com/carlosframework/platform/internal/moves/archive	1.089s
ok  	github.com/carlosframework/platform/internal/objstore	0.318s
ok  	github.com/carlosframework/platform/internal/ops	0.279s
ok  	github.com/carlosframework/platform/internal/park	0.262s
ok  	github.com/carlosframework/platform/internal/plans	0.009s
ok  	github.com/carlosframework/platform/internal/privatefile	0.006s
ok  	github.com/carlosframework/platform/internal/provision	14.382s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.009s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	5.634s
ok  	github.com/carlosframework/platform/internal/relay	7.233s
ok  	github.com/carlosframework/platform/internal/release	25.682s
ok  	github.com/carlosframework/platform/internal/rollout	3.748s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.003s
ok  	github.com/carlosframework/platform/internal/rootaws	0.007s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.060s
ok  	github.com/carlosframework/platform/internal/rootstate	0.029s
ok  	github.com/carlosframework/platform/internal/runs	0.576s
ok  	github.com/carlosframework/platform/internal/s3compat	0.007s
ok  	github.com/carlosframework/platform/internal/safefile	0.005s
ok  	github.com/carlosframework/platform/internal/schedule	2.766s
ok  	github.com/carlosframework/platform/internal/seeds	0.014s
ok  	github.com/carlosframework/platform/internal/sim/engines	2.738s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	10.756s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	3.767s
ok  	github.com/carlosframework/platform/internal/status	0.074s
ok  	github.com/carlosframework/platform/internal/steering	0.010s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.240s
ok  	github.com/carlosframework/platform/internal/svcrole	0.002s
ok  	github.com/carlosframework/platform/internal/telemetry	0.004s
ok  	github.com/carlosframework/platform/internal/termfence	0.004s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.006s
ok  	github.com/carlosframework/platform/internal/unixdial	0.005s
ok  	github.com/carlosframework/platform/internal/updatewords	0.004s
ok  	github.com/carlosframework/platform/internal/usage	0.013s
ok  	github.com/carlosframework/platform/internal/version	0.002s
ok  	github.com/carlosframework/platform/internal/vet	1.702s
=== internal_console@0 (86 s)
ok  	github.com/carlosframework/platform/internal/console	83.960s
=== internal_console@1 (44 s)
ok  	github.com/carlosframework/platform/internal/console	41.902s
=== internal_console@2 (41 s)
ok  	github.com/carlosframework/platform/internal/console	39.448s
=== internal_console@3 (41 s)
ok  	github.com/carlosframework/platform/internal/console	38.850s
=== internal_console@4 (42 s)
ok  	github.com/carlosframework/platform/internal/console	39.999s
=== internal_console@5 (41 s)
ok  	github.com/carlosframework/platform/internal/console	38.650s
=== internal_console@6 (37 s)
ok  	github.com/carlosframework/platform/internal/console	34.729s
=== internal_console@7 (45 s)
ok  	github.com/carlosframework/platform/internal/console	42.645s
=== root@0 (109 s)
ok  	github.com/carlosframework/platform	106.950s
=== root@1 (84 s)
ok  	github.com/carlosframework/platform	82.348s
=== root@2 (91 s)
ok  	github.com/carlosframework/platform	88.846s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64

amadan: job cancelled — a newer push superseded it
--- shard 1 of 5 ---
cross-compiling darwin/amd64

amadan: job cancelled — a newer push superseded it
--- shard 2 of 5 ---
cross-compiling linux/arm64

amadan: job cancelled — a newer push superseded it
--- shard 3 of 5 ---
cross-compiling windows/amd64

amadan: job cancelled — a newer push superseded it
--- shard 4 of 5 ---
cross-compiling windows/arm64

amadan: job cancelled — a newer push superseded it
diff --git a/docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-3-serve.md b/docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-3-serve.md
index 6913a8cca..a2cad4368 100644
--- a/docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-3-serve.md
+++ b/docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-3-serve.md
@@ -190,7 +190,7 @@ On the home, `serveAssetFillForHop` runs only for a verified hop carrying `X-Car
### Task 6: Far catch and fill
**Files:**
-- Modify: `internal/edge/assetcatch.go`, `internal/edge/edge.go` (lookup-miss branch: call `serveAssetFar` before `e.backhaul` at `:1561`; strip a client-sent `X-Carlos-Asset-Fill` from every non-hop request near `acceptHop` at `:1503`)
+- Modify: `internal/edge/assetcatch.go`, `internal/edge/edge.go` (call `serveAssetFar` on **both** forwarding paths: in the exact-remote precedence branch before its `backhaulTo` at `:1526-1530`, where a local wildcard matches but another box owns the exact hostname, and in the lookup-miss branch before `e.backhaul` at `:1561`; never for a verified hop or a door caller (`doorCallerFrom`, `:1454-1458`), which keep their existing guards and paths (astra P5-1); strip a client-sent `X-Carlos-Asset-Fill` from every non-hop request near `acceptHop` at `:1503`)
- Test: `internal/edge/asset_far_test.go`
**Interfaces:**
@@ -205,11 +205,13 @@ On the home, `serveAssetFillForHop` runs only for a verified hop carrying `X-Car
- `TestFarStaleAtForwards` (`at` 61 minutes old: plain backhaul, nothing stored).
- `TestFarFillCarriesNoCookie` (asserted at the fake home).
- `TestClientSentFillHeaderIsStripped`.
+ - `TestFarCatchesBehindALocalWildcard` (this box serves `*.example.com`, another box homes `shop.example.com` exactly: the second asset request is a local hit and dials neither backend).
+ - `TestDoorCallerAndVerifiedHopAreNeverCaught`.
- `TestFarUpgradeUnderAPrefixIsBackhauledIntact` (a WebSocket `GET` under `/assets/`: not caught, upgrade headers reach the fake home).
- `TestFarColdMissAnswersARangeFromTheEntry` and `TestFarColdHeadIsBuffered`.
- `TestFarFallbackETagMatchesTheHomes` (no app `ETag`: home and far entries carry the same value).
- `TestFarCoalescedWaitersGetTheEntry` and `TestFarRefusedFillReachesOnlyTheInitiator` (differing `Accept`, a `Set-Cookie` response, an unacknowledged fill; each waiter's request reaches the fake home on its own).
-- [ ] **Step 2:** Run `go test ./internal/edge/ -run 'TestFar|TestClientSentFillHeader' -count=1` (every Task 6 test name starts with one of these). Expected: FAIL.
+- [ ] **Step 2:** Run `go test ./internal/edge/ -run 'TestFar|TestClientSentFillHeader|TestDoorCallerAndVerifiedHop' -count=1`. Expected: FAIL.
- [ ] **Step 3:** Implement.
- [ ] **Step 4:** Run the same command. Expected: PASS.
- [ ] **Step 5:** Commit.
@@ -263,6 +265,7 @@ The authority table itself runs in plan 1 Task 8 (`internal/host`, the real `ass
- `TestAssetE2EStalledBodyIsAbandoned`: the home's backend stalls after headers; the far fill is abandoned at the deadline with both reservations released.
- `TestAssetE2EUndeclaringBuildIsNotStored`: during a generation switch where only the installed build declares the prefix, the serving build's cacheable fallback is stored on neither edge; and the reverse, where only the retiring (still serving) build declares it, a response from the installed build after the switch is stored on neither edge (astra P4-R1-15).
- `TestAssetE2EWarmSleepRestart`: a generation-managed instance warms, sleeps and restarts its edge process; hits continue on both edges with no wake.
+ - `TestAssetE2EFarCatchBehindALocalWildcard` (the same case through the scene, with `e2eDialCounter`).
- `TestAssetE2EOversizedResponseIsStoredNowhere` (a 33 MiB body, with and without `Content-Length`).
- `TestAssetE2EGenerationOrOwnerMismatchGetsNoAck` (the far edge's published view names a generation, then an owner, that the home no longer holds: no acknowledgement, metered once at the home, stored nowhere) (astra P2-R1-15).
- [ ] **Step 2:** Run `go test ./internal/edge/ -run 'TestAssetE2E' -count=1` with the sandbox off. Expected: PASS.
diff --git a/docs/superpowers/specs/CODEX-FINDINGS-ASSET-CACHE-PLANS-R5.md b/docs/superpowers/specs/CODEX-FINDINGS-ASSET-CACHE-PLANS-R5.md
new file mode 100644
index 000000000..39d74e195
--- /dev/null
+++ b/docs/superpowers/specs/CODEX-FINDINGS-ASSET-CACHE-PLANS-R5.md
@@ -0,0 +1,11 @@
+| Earlier finding | Disposition | Why |
+|---|---|---|
+| R4-1: Missing record-write census | Fixed | [Plan 1:44](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-1-declare-publish.md:44) assigns the recognizer and inventory updates; Task 11 runs both censuses. |
+| R4-2: New owner’s key filled through old backend | Fixed | [Plan 3:162](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-3-serve.md:162) requires owner, primary-route and generation agreement before filling or acknowledging, with a withheld-reload test. |
+| R4-3: Buffered initiating misses labelled hits | Fixed | [Plan 3:120](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-3-serve.md:120) adds an explicit result argument and tests cold `miss` followed by `hit`. |
+| R3-3: Failed retirement advances authority | Fixed | [Plan 1:230](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-1-declare-publish.md:230) includes failed retirement and failed fresh-walk confirmation in completeness, with tests for both. |
+| R1-15: Missing production-wired attacks | Fixed | [Plan 3:260](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-3-serve.md:260) adds missing Cache-Control, `no-store` and the retiring-build-only case to two-edge acceptance. |
+
+1. **P2 — An existing forwarding path bypasses the far asset cache entirely.** Plan 3 Task 6 inserts `serveAssetFar` only in the lookup-miss branch ([plan 3:193](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-3-serve.md:193)). But when a local wildcard matches and another box owns the exact hostname, `ServeHTTP` calls `backhaulTo` and returns **before** that branch ([edge.go:1526](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/internal/edge/edge.go:1526)). An armed far edge with such a wildcard therefore backhauls every asset request, even with a valid warm entry. **Smallest fix:** invoke the far-cache handler in the exact-remote precedence branch before `backhaulTo`, retaining its existing hop and door guards. Add a two-edge test with a local wildcard and an exact remote asset host; the second request must hit locally without dialing either backend.
+
+reject
\ No newline at end of file