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.
2 files changed,
+16
−2
docs/superpowers/plans/2026-10-24-edge-asset-cache-plan-3-serve.md+5 −2docs/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 |