carlos / platform Public

edge: the import gate judges the decision's table; witnesses for the login re-resolution, the fallback's owner and a door outside the fallback (Task 2 fix round 1)

- Minor 1: importGateAdmit takes the table the request's decision was
  resolved in (local.table) and looks host's front row up there, so a
  reload between resolution and the gate cannot judge the request
  against a different front row. TestImportGateJudgesTheDecisionsTable:
  a commit landing after resolution does not admit a prefix the
  resolved table still gates.
- Minor 5: a login POST through ServeHTTP re-resolves in the
  decision's table (a password change after resolution does not change
  which row judges it); the fallback-after-reload case asserts the
  metered owner as well as the backend; a door that does not own the
  local wildcard fallback gets the unknown-host answer.
- Census: Edge.ServeHTTP and Edge.importGateAdmit re-pinned in place
  (rev3=PROPOSED-self-serve-moves-3), internal/edge envelope re-pinned,
  seal 6ae10fd1.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 8b1437be9b8d709320b19dd9cf9d3e78f2f59aec parent d88aaf3
7 files changed, +104 −9
  • docs/superpowers/notes/census-proposal-self-serve-moves-3.md +4 −0
  • internal/edge/decision_test.go +47 −1
  • internal/edge/edge.go +1 −1
  • internal/edge/importgate.go +7 −2
  • internal/edge/importgate_test.go +35 −0
  • internal/provision/plan14_root_reads_test.go +4 −2
  • internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv +6 −3

CI failed — run details

CI log
=== gate ===
sigwho: pid 2 ppid 1 pgid 0 sid 0; child pid 3
.amadan/test-gate.sh
gate: 2026-10-24T04:12:28Z on aarch64, 32 cpus, 61365 MB available, go1.26.8 linux/arm64
gate: +0s start launchctx
gate: +0s start vet
gate: +0s start gofmt
gate: +1s start rest
gate: +18s start internal_console@0
gate: +18s start internal_console@1
gate: +18s start internal_console@2
gate: +18s start internal_console@3
gate: +18s start internal_console@4
gate: +18s start internal_console@5
gate: +18s start internal_console@6
gate: +18s start internal_console@7
gate: +18s waiting for internal_console's shards before the next split package
gate: +185s start root@0
gate: +185s start root@1
gate: +185s start root@2
gate: +185s waiting for root's shards before the next split package

amadan: step gate failed: signal: terminated

amadan: job failed at step gate — its output is above, not at the tail of this log
diff --git a/docs/superpowers/notes/census-proposal-self-serve-moves-3.md b/docs/superpowers/notes/census-proposal-self-serve-moves-3.md
index a5b9b4491..53260c4b9 100644
--- a/docs/superpowers/notes/census-proposal-self-serve-moves-3.md
+++ b/docs/superpowers/notes/census-proposal-self-serve-moves-3.md
@@ -89,3 +89,7 @@ Envelopes re-pinned:
- `internal/host`: `agent.go`, `movespass.go` and `movesoffline.go` (the subscription).
The record-write census is unchanged by this task. The edge writes no record, and the subscription writes nothing.
+
+### Task 2 fix round 1
+
+The import gate now judges the front row in the table the request's decision was resolved in: `importGateAdmit` takes `local.table`, not the live table. `edge/edge.go:Edge.ServeHTTP` is re-pinned in place. `edge/importgate.go:Edge.importGateAdmit`, accepted legacy since census-ruling-seeded-instances, takes a new frozen fingerprint under `rev3=PROPOSED-self-serve-moves-3`. Its read sites are unchanged; offsets moved. The `internal/edge` envelope is re-pinned and the seal moves to `6ae10fd1…`. No root read, class or task is added, and the record-write census is unchanged.
diff --git a/internal/edge/decision_test.go b/internal/edge/decision_test.go
index 22959460a..ba484e933 100644
--- a/internal/edge/decision_test.go
+++ b/internal/edge/decision_test.go
@@ -12,12 +12,14 @@ import (
"io"
"net/http"
"net/http/httptest"
+ "net/url"
"slices"
"strings"
"sync"
"testing"
"time"
+ "github.com/carlosframework/platform/internal/gate"
"github.com/carlosframework/platform/internal/registry"
)
@@ -326,6 +328,39 @@ func TestDecisionCarriesTheTable(t *testing.T) {
wantBody(t, sc.serve("front.example.com", "/bab/x", via{door: door}), "app v1", "the door owns the prefix target")
wantUnknown(t, sc.serve("front.example.com", "/x", via{door: door}), "the door does not own the front route")
})
+ // Last: Upsert keeps a row's password, so the gated row stays gated.
+ t.Run("a login POST re-resolves in the decision's table", func(t *testing.T) {
+ h, err := gate.Hash("correct horse")
+ if err != nil {
+ t.Fatal(err)
+ }
+ other, err := gate.Hash("another password")
+ if err != nil {
+ t.Fatal(err)
+ }
+ sc.route("app.example.com", "app v1", "bab", "app", func(r *registry.Route) { r.Gate = h })
+ // A reload after resolution moves the prefix target's row to
+ // another password.
+ sc.e.afterResolve = func() {
+ sc.route("app.example.com", "app v2", "bab", "app", func(r *registry.Route) { r.Gate = other })
+ }
+ defer func() { sc.e.afterResolve = nil }()
+ form := url.Values{"password": {"correct horse"}, "next": {"/bab/x"}}
+ r := httptest.NewRequest(http.MethodPost, "https://front.example.com"+gate.LoginPath, strings.NewReader(form.Encode()))
+ r.Host = "front.example.com"
+ r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ w := httptest.NewRecorder()
+ sc.e.ServeHTTP(w, r)
+ var cookie string
+ for _, c := range w.Result().Cookies() {
+ if c.Name == gate.CookieName {
+ cookie = c.Value
+ }
+ }
+ if w.Code != http.StatusSeeOther || cookie == "" || !gate.Verify(cookie, "app.example.com", h, time.Now()) {
+ t.Fatalf("login = %d, cookie %q: the password is judged by the resolved table's row", w.Code, cookie)
+ }
+ })
}
func TestLocalCandidatesAreTheUpstreamTable(t *testing.T) {
@@ -487,11 +522,22 @@ func TestEveryRequestPathHonoursTheDecision(t *testing.T) {
sc.dropKey()
wantBody(t, sc.serve("x.example.com", "/", ordinary), "local wildcard", "ordinary, no mesh key")
+ // A door that does not own the local catch-all is refused, as a door
+ // is refused any route it does not own.
+ wantUnknown(t, sc.serve("x.example.com", "/", via{door: bab}), "a door that does not own the fallback")
+
// A reload between the remote resolution and the failed backhaul
- // moves the wildcard to another backend and owner.
+ // moves the wildcard to another backend and owner: the request is
+ // served, and billed, by the decision's.
+ meter := NewUsageMeter()
+ sc.e.EnableUsage(meter)
sc.e.afterResolve = func() { sc.route("*.example.com", "reloaded wildcard", "bab", "other") }
wantBody(t, sc.serve("x.example.com", "/", ordinary), "local wildcard", "the fallback is the decision's")
sc.e.afterResolve = nil
+ if row, ok := meter.Drain()["*.example.com"]; !ok || row.App != "wild" || row.Account != "bab" || row.Requests != 1 {
+ t.Fatalf("metered %+v (%v), want the decision's owner bab/wild", row, ok)
+ }
+ sc.e.EnableUsage(nil)
sc.route("*.example.com", "local wildcard", "bab", "wild")
// The same three with a live hold for x.example.com.
diff --git a/internal/edge/edge.go b/internal/edge/edge.go
index 37b31454f..8f4ac08ea 100644
--- a/internal/edge/edge.go
+++ b/internal/edge/edge.go
@@ -1748,7 +1748,7 @@ func (e *Edge) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// The import gate (importgate.go): after the door's scope, before the
// meter, the password gate, the cache and the activator.
- if !e.importGateAdmit(w, r, host, up.route) {
+ if !e.importGateAdmit(w, r, host, up.route, local.table) {
return
}
diff --git a/internal/edge/importgate.go b/internal/edge/importgate.go
index eaa8956b7..02908738a 100644
--- a/internal/edge/importgate.go
+++ b/internal/edge/importgate.go
@@ -32,11 +32,16 @@ const ImportVerifyHeader = "Carlos-Import-Verify"
// for the same reason. ImportGateClosed (a rollback) matches no token.
//
// Everything else gets 503 with Retry-After: 5 and Cache-Control: no-store.
-func (e *Edge) importGateAdmit(w http.ResponseWriter, r *http.Request, host string, rt registry.Route) bool {
+//
+// table is the table the request's decision was resolved in (self-serve
+// moves plan 3): host's own row is looked up there, never in the live
+// table, so a reload between resolution and this gate cannot judge rt
+// against a different front row.
+func (e *Edge) importGateAdmit(w http.ResponseWriter, r *http.Request, host string, rt registry.Route, table map[string]upstream) bool {
token := r.Header.Get(ImportVerifyHeader)
r.Header.Del(ImportVerifyHeader)
front := rt
- if up, ok := lookupIn(e.current(), host); ok {
+ if up, ok := lookupIn(table, host); ok {
front = up.route
}
if rt.ImportGate == "" && front.ImportGate == "" {
diff --git a/internal/edge/importgate_test.go b/internal/edge/importgate_test.go
index 0a0dac869..f7efdbd9e 100644
--- a/internal/edge/importgate_test.go
+++ b/internal/edge/importgate_test.go
@@ -378,3 +378,38 @@ func TestImportGateHoldsAcrossABackhaul(t *testing.T) {
t.Fatalf("backhauled verify: import reached %d times, header at the app %q", n, saw)
}
}
+
+// One decision and one table per request (self-serve moves plan 3 Task 2
+// fix round 1): the import gate judges the front row the request resolved
+// in, not the live table. A commit (the front row's gate cleared) landing
+// between resolution and the gate does not admit a prefix the resolved
+// table still gates.
+func TestImportGateJudgesTheDecisionsTable(t *testing.T) {
+ s := newGateScene(t, "")
+ targetSrv, target := newGateBackend(t, "prefix target", "")
+ if err := s.reg.Upsert(registry.Route{Host: "next.example.com", Kind: "service", Addr: targetSrv.Listener.Addr().String(),
+ Status: "ready", App: "shop", Account: "bbw"}); err != nil {
+ t.Fatal(err)
+ }
+ upsertPath(t, s.reg, gateHost, "shop", "next.example.com")
+ if err := s.e.Reload(); err != nil {
+ t.Fatal(err)
+ }
+ s.e.afterResolve = func() {
+ if err := s.reg.CommitImport(gateHost, gateSeedID); err != nil {
+ t.Error(err)
+ }
+ if err := s.e.Reload(); err != nil {
+ t.Error(err)
+ }
+ }
+ wantGateRefusal(t, gateDo(s.e, http.MethodGet, gateHost, "/shop/cart", ""), s.token, "a commit after resolution")
+ s.e.afterResolve = nil
+ if n, _ := target.seen(); n != 0 {
+ t.Fatalf("the prefix target was reached %d time(s)", n)
+ }
+ // The control: the next request resolves in the committed table.
+ if rec := gateDo(s.e, http.MethodGet, gateHost, "/shop/cart", ""); rec.Code != http.StatusOK || rec.Body.String() != "prefix target" {
+ t.Fatalf("after the commit = %d %q", rec.Code, rec.Body.String())
+ }
+}
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go
index 2cd4ababe..d67e33ded 100644
--- a/internal/provision/plan14_root_reads_test.go
+++ b/internal/provision/plan14_root_reads_test.go
@@ -1392,8 +1392,10 @@ func TestPlan14RootReadCensus(t *testing.T) {
// tick), which moves it again. Task 2 (the ranking, the holding page and
// the local edge) re-pins Edge.Reload, Edge.ServeHTTP and
// Edge.currentHosts and freezes two new edge units (localReady and
- // localHolds), which moves it once more.
- seal = "8b6572a0ef7ac6100409ba831a0ba04d761247bc14f13fb62f78a485e5e1fd0f"
+ // localHolds), which moves it once more. Its fix round 1 re-pins
+ // Edge.ServeHTTP and Edge.importGateAdmit in place (the import gate
+ // judges the decision's table), which moves it again.
+ seal = "6ae10fd10fe66f496baf5b82bdcbbf911340425f8550ab9619f2997dacbec74b"
errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...)
errs = append(errs, checkRootReadDeclarations(got, inv)...)
if len(errs) > 0 {
diff --git a/internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv b/internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv
index 9efff97e2..662ebf790 100644
--- a/internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv
+++ b/internal/provision/testdata/plan14-root-reads-self-serve-moves-3.tsv
@@ -2,6 +2,7 @@
# Task 1 (alias provenance and the writer's holding entries), rev3=PROPOSED-self-serve-moves-3: no record envelope changes and no root read, record read or task is added. The registry gains route_aliases.explicit (RouteAlias.Explicit, the tracked field Explicit in @fields below): the schema, its scanner, writer and fingerprint are re-pinned. The domains reconciler writes provenance (ReconcileAliases and deriveInstanceAliases re-pinned; keepDerived new). The edges-file writer publishes held rows as offline_moves holding entries (edgesFileWriteClaims re-pinned at F,J1; holdingEntries new at F,J1; edgesfile.AliasPublishable new) and its loop and runAgent wire the offline view in. Seven accepted-legacy units take new frozen fingerprints and two new ones are frozen, which moves the seal. Envelopes re-pinned: internal/domains, internal/edge (holding.go: the HoldSnapshot and HoldState DTOs, no read), internal/edgesfile (eligible.go), internal/host (edgesfile.go, movesoffline.go, movespass.go, agent.go, e2e_bridge.go) and internal/registry.
# Task 1 fix round 1, rev3=PROPOSED-self-serve-moves-3: a moves pass's listing failure fails only the offline view's holds (failHolds), the snapshot keep and elsewhere read staying the last good listing's; the edges file loop loads the view's snapshot once per tick. edgesFileLoop re-pinned in place (its read site unchanged), which moves the seal; the internal/host envelope re-pinned in place. No root read, class or task is added.
# Task 2 (candidates, the ranking, the holding page, the local edge and the agent wiring), rev3=PROPOSED-self-serve-moves-3: no record envelope changes and no root read, record read or task is added. The edge's serving reads move: Edge.Reload (hands its listed rows to the local hold rebuild), Edge.ServeHTTP (one decision per request, resolve and selectUpstream) and Edge.currentHosts (the box's platform holds) are re-pinned as accepted legacy, and two new edge units are frozen as accepted legacy (localReady, a table entry as a candidate; localHolds, the local holds from the rows Reload listed). runAgent (B,C,D,F: edge.WithBoxID, subscribeEdgeHolds) and armMovesPass (B,C: subscribeArmedHolds) are re-pinned as authority. Envelopes re-pinned: internal/edge (rank.go, holding.go, homemap.go, edge.go, certwarm.go) and internal/host (agent.go, movespass.go, movesoffline.go).
+# Task 2 fix round 1, rev3=PROPOSED-self-serve-moves-3: the import gate judges the front row in the request decision's table (importGateAdmit takes local.table). Edge.ServeHTTP is re-pinned in place and Edge.importGateAdmit (accepted legacy since census-ruling-seeded-instances) takes a new frozen fingerprint, which moves the seal; the internal/edge envelope is re-pinned. No root read, class or task is added.
@legacy 652c416a0cd27a153fa259d76ab20538480e2c3afd98cbec6af083902b17f5ff internal/domains/aliases.go ReconcileAliases legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos), from its fast-converge and instances loops: self-serve moves 1's body (rev3=J1, the catch-all's labels counted through domains.InstanceLabelCountsJudged), which now writes each alias row's provenance: an exact claim on the row's own name upserts it explicit (every pass, so a legacy row is upgraded on the first), and the synthetic "*.<host>" catch-all row not explicit; registry read sites unchanged, offsets moved; a report the edges file lists holding aliases by, not a decision registry:Account@15605,registry:Account@15618,registry:Alias@15220,registry:Alias@15260,registry:Alias@15330,registry:App@12165,registry:App@12365,registry:App@12521,registry:App@12722,registry:App@15631,registry:App@15640,registry:App@2026,registry:App@2222,registry:App@6036,registry:App@6235,registry:Host@15590,registry:Host@15879,registry:Host@15932,registry:Host@16004,registry:Host@16516,registry:Host@16608,registry:Host@16667,registry:Host@16748,registry:Host@406,registry:Prefix@16524,registry:Prefix@16622,registry:Prefix@16675,registry:Prefix@16756,registry:Status@8899,whole:Route@12134,whole:Route@12209,whole:Route@12356,whole:Route@12418,whole:Route@12561,whole:Route@12717,whole:Route@13613,whole:Route@1369,whole:Route@14825,whole:Route@14833,whole:Route@2066,whole:Route@2217,whole:Route@2273,whole:Route@359,whole:Route@388,whole:Route@399,whole:Route@417,whole:Route@5382,whole:Route@6080,whole:Route@6226,whole:Route@8229,whole:Route@8681,whole:Route@8843,whole:RouteAlias@14841,whole:RouteAlias@15195,whole:RoutePath@10426,whole:RoutePath@16409,whole:RoutePath@16451,whole:RoutePath@16565,whole:RoutePath@16595,whole:WildcardDomain@12933,whole:WildcardDomain@15520,whole:WildcardDomain@15569,whole:WildcardDomain@15732,whole:WildcardDomain@15838,whole:WildcardDomain@15867 edef8a1bf06cab5666a52ec19840c54f02913ddc67a32da7f6197db1606556a4
internal/domains/aliases.go ReconcileAliases legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos), from its fast-converge and instances loops: self-serve moves 1's body (rev3=J1, the catch-all's labels counted through domains.InstanceLabelCountsJudged), which now writes each alias row's provenance: an exact claim on the row's own name upserts it explicit (every pass, so a legacy row is upgraded on the first), and the synthetic "*.<host>" catch-all row not explicit; registry read sites unchanged, offsets moved; a report the edges file lists holding aliases by, not a decision registry:Account@15605,registry:Account@15618,registry:Alias@15220,registry:Alias@15260,registry:Alias@15330,registry:App@12165,registry:App@12365,registry:App@12521,registry:App@12722,registry:App@15631,registry:App@15640,registry:App@2026,registry:App@2222,registry:App@6036,registry:App@6235,registry:Host@15590,registry:Host@15879,registry:Host@15932,registry:Host@16004,registry:Host@16516,registry:Host@16608,registry:Host@16667,registry:Host@16748,registry:Host@406,registry:Prefix@16524,registry:Prefix@16622,registry:Prefix@16675,registry:Prefix@16756,registry:Status@8899,whole:Route@12134,whole:Route@12209,whole:Route@12356,whole:Route@12418,whole:Route@12561,whole:Route@12717,whole:Route@13613,whole:Route@1369,whole:Route@14825,whole:Route@14833,whole:Route@2066,whole:Route@2217,whole:Route@2273,whole:Route@359,whole:Route@388,whole:Route@399,whole:Route@417,whole:Route@5382,whole:Route@6080,whole:Route@6226,whole:Route@8229,whole:Route@8681,whole:Route@8843,whole:RouteAlias@14841,whole:RouteAlias@15195,whole:RoutePath@10426,whole:RoutePath@16409,whole:RoutePath@16451,whole:RoutePath@16565,whole:RoutePath@16595,whole:WildcardDomain@12933,whole:WildcardDomain@15520,whole:WildcardDomain@15569,whole:WildcardDomain@15732,whole:WildcardDomain@15838,whole:WildcardDomain@15867 edef8a1bf06cab5666a52ec19840c54f02913ddc67a32da7f6197db1606556a4
@legacy 887eab39c9584891268b9afedcd81c995849a97545258b3eacc506b5c0165b23 internal/domains/aliases.go deriveInstanceAliases legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): self-serve moves 1's body, whose already-the-row-we-want skip now also compares the existing row's provenance (registry:Explicit, one new site): a derived name left explicit by a detached exact claim on the same target and owner is rewritten not explicit, and both keep-as-it-is paths (a listing failure, an unjudged label) hand the kept row to keepDerived; the other registry read sites unchanged, offsets moved; no new route registry:Account@2163,registry:Account@3516,registry:Account@4587,registry:Alias@2225,registry:Alias@2293,registry:Alias@2337,registry:Alias@557,registry:App@1686,registry:App@2189,registry:App@3542,registry:App@4613,registry:Explicit@4632,registry:Host@1843,registry:Host@4566,registry:Host@4576,registry:Host@4787,registry:Provisioned@1636,registry:Replica@1672,registry:Slug@1775,whole:Route@1620,whole:Route@1726,whole:Route@3058,whole:RouteAlias@2143,whole:RouteAlias@2377,whole:RouteAlias@3498,whole:RouteAlias@3867,whole:RouteAlias@4548,whole:RouteAlias@509,whole:RouteAlias@539,whole:RouteAlias@552,whole:RouteAlias@568 42d5a40cccb38c31f306b49c00fde7482e4ee0b3cbf6a6a2dfbaac06592ef883
@@ -21,12 +22,14 @@ internal/registry/registry.go Registry.UpsertAlias legacy accepted-legacy accept
@legacy 1409145169f03f11766765601208065ab115929f8f773b23512c9465d6f6f8e3 internal/registry/registry.go Registry.migrate legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=the registry's SQLite side, as carlos: in the agent (/opt/carlos/carlos agent, carlos-edge.service) and in the registry child (carlos __registry-child, which a root pass forks as the service user), on every registry open; a box CLI verb that opens the registry directly runs it as whoever runs the verb: self-serve moves 1's body with route_aliases.explicit (INTEGER NOT NULL DEFAULT 0) in the create statement and one idempotent ALTER for a table created before it (the duplicate-column rule); self-serve moves 3 Task 1 file:Exec@3622,file:Exec@3946,file:Exec@4638,file:Exec@51,file:Exec@5111,file:Exec@5971,file:Exec@6250,file:Exec@7113,file:Exec@7289,registry:sql@3622,registry:sql@3946,registry:sql@4638,registry:sql@51,registry:sql@5111,registry:sql@5971,registry:sql@6250,registry:sql@7113,registry:sql@7289 76673b36d4f61499f9649f3c0cf195ae582b6d21bea50d26dce147d59fb9c990
internal/registry/registry.go Registry.migrate legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=the registry's SQLite side, as carlos: in the agent (/opt/carlos/carlos agent, carlos-edge.service) and in the registry child (carlos __registry-child, which a root pass forks as the service user), on every registry open; a box CLI verb that opens the registry directly runs it as whoever runs the verb: self-serve moves 1's body with route_aliases.explicit (INTEGER NOT NULL DEFAULT 0) in the create statement and one idempotent ALTER for a table created before it (the duplicate-column rule); self-serve moves 3 Task 1 file:Exec@3622,file:Exec@3946,file:Exec@4638,file:Exec@51,file:Exec@5111,file:Exec@5971,file:Exec@6250,file:Exec@7113,file:Exec@7289,registry:sql@3622,registry:sql@3946,registry:sql@4638,registry:sql@51,registry:sql@5111,registry:sql@5971,registry:sql@6250,registry:sql@7113,registry:sql@7289 76673b36d4f61499f9649f3c0cf195ae582b6d21bea50d26dce147d59fb9c990
@legacy f41bc7d6d0f544ba64ab40732c62e4d11c9515dc75032e79888478236843b08a internal/edge/edge.go Edge.Reload legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body, which also hands the rows and alias rows it listed, with the table it built from them, to the local hold rebuild (holdInputs, rebuildHolds) before the reload hook runs; read sites unchanged, offsets moved; serving only registry:Account@18079,registry:Alias@16388,registry:Alias@16475,registry:Alias@16571,registry:FleetBox@5526,registry:Health@7689,registry:Health@7807,registry:Hibernate@12450,registry:Hibernate@14360,registry:Hibernate@6049,registry:Host@12498,registry:Host@14422,registry:Host@14730,registry:Host@14757,registry:Host@15525,registry:Host@15791,registry:Host@16514,registry:Host@6098,registry:Host@8290,registry:Host@8385,registry:Host@8665,registry:Host@8691,registry:Host@8743,registry:Kind@10975,registry:Kind@10997,registry:Kind@12426,registry:Kind@14336,registry:Kind@6025,registry:Kind@8831,registry:Kind@8891,registry:Name@18069,registry:Prefix@15766,registry:Region@2252,registry:Socket@14738,registry:Socket@5633,registry:Socket@6162,registry:Status@1835,registry:Target@15778,registry:Tunnel@4574,registry:Tunnel@4630,registry:Version@10956,registry:Version@11058,whole:Fleet@18019,whole:Fleet@18048,whole:Route@130,whole:Route@14790,whole:Route@14797,whole:Route@19692,whole:Route@19700,whole:Route@4692,whole:Route@5590,whole:Route@6218,whole:Route@648,whole:Route@8808,whole:Route@8881,whole:Route@8941,whole:RouteAlias@16359,whole:RouteAlias@19708,whole:RouteAlias@19717,whole:RoutePath@15503 e4e7b4dbdef60f59e2d9b3f4ad1d087d2044791764681bd827b60466ce8f7943
-@legacy 97866c1d4c1160246a2d1d9bb052bf575ce2d21287ab8a8fb3552c4b2c50fdbe internal/edge/edge.go Edge.ServeHTTP legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body (import gate after the door's scope, a gated row left out of the response cache), with the request's one decision (resolve: this box's table, its local holds and the home map's candidates, ranked) in place of the table lookup and the exact-home probe; the prefix target, the login re-resolution and the door's check read the decision's own table (selectUpstream), and a holding decision answers the holding page; read sites unchanged, offsets moved; serving only registry:Gate@14320,registry:Gate@14380,registry:Gate@14438,registry:Host@14423,registry:ImportGate@17495,registry:Kind@12559,registry:Kind@17466,whole:Route@10577,whole:Route@10789,whole:Route@12562,whole:Route@13120,whole:Route@13215,whole:Route@13291,whole:Route@14323,whole:Route@14383,whole:Route@14426,whole:Route@14441,whole:Route@15254,whole:Route@17469,whole:Route@17498,whole:Route@18644 5bbb4bf8899f9ce962487943ad520d3d4d07926a6eb23c44f81b034090c67112
+@legacy c4da9b7096cb608a8a47bba1985da005162f38f5d8bf5ac0102acda4c468d79e internal/edge/edge.go Edge.ServeHTTP legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body (import gate after the door's scope, a gated row left out of the response cache), with the request's one decision (resolve: this box's table, its local holds and the home map's candidates, ranked) in place of the table lookup and the exact-home probe; the prefix target, the login re-resolution and the door's check read the decision's own table (selectUpstream), and a holding decision answers the holding page; read sites unchanged, offsets moved; serving only; Task 2 fix round 1: the import gate is handed the decision's table (local.table) and judges the front row there registry:Gate@14333,registry:Gate@14393,registry:Gate@14451,registry:Host@14436,registry:ImportGate@17508,registry:Kind@12572,registry:Kind@17479,whole:Route@10577,whole:Route@10789,whole:Route@12575,whole:Route@13133,whole:Route@13228,whole:Route@13304,whole:Route@14336,whole:Route@14396,whole:Route@14439,whole:Route@14454,whole:Route@15267,whole:Route@17482,whole:Route@17511,whole:Route@18657 4ca7adfcab187bf1f37144d90e5396be6efdaa3429393471c5184e77760764b7
@legacy 59d746cbafad658130c4c6be6d6597f0a57cd2bcb2a493e3f2f9c8a456ceb524 internal/edge/edge.go Edge.currentHosts legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body, which also lists this box's real holds of platform rows (holdSnapshot.platformNames, which reads no row) so a held row's wildcard parent stays loaded; read sites unchanged; serving only (the wildcard parent set) registry:Platform@123,registry:Provisioned@144,whole:Route@126,whole:Route@147 88419f01a8c80da7e11def22cd5512365c097ec17ee5afa895c1863d6c858ff1
@legacy 9cd24dc39fec3c4b8f569729b7a2a8fdd30a008e1064295afdf9e62937fde31c internal/edge/edge.go localReady legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): new, a table entry as a ranking candidate: reads the route the entry carries (its owner through release.RouteAccount, app, lifetime pair, class marks and, for an alias, its primary's host); serving only registry:App@258,registry:Host@498,registry:Incarnation@287,registry:Platform@357,registry:Provisioned@378,registry:ServingSeq@321,whole:Route@245,whole:Route@261,whole:Route@290,whole:Route@324,whole:Route@360,whole:Route@381,whole:Route@501 cabee4199c52c17849c70e7e6d7089d1e404c113008de3611083029bc4dd678d
@legacy c5d51734282888f6f4e90af026b8e369ce86f97e03e3867b1632ce70823bf1e9 internal/edge/holding.go localHolds legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): new, the local holds: reads the rows and alias rows the latest Reload listed (a row's status, hold op, host, owner, pair and class marks; an alias row's name, primary, provenance and DNS status through edgesfile.AliasPublishable) against the offline view's moves; serving only (the holding page and its certificate), writes nothing registry:Alias@1108,registry:Alias@1235,registry:Alias@1256,registry:Alias@1284,registry:App@532,registry:Explicit@1051,registry:HoldOp@286,registry:HoldOp@392,registry:Host@1030,registry:Host@1409,registry:Host@359,registry:Host@446,registry:Host@716,registry:Host@741,registry:Host@768,registry:Host@902,registry:Incarnation@574,registry:Incarnation@926,registry:Platform@464,registry:Provisioned@478,registry:ServingSeq@601,registry:ServingSeq@953,registry:Status@273,whole:Route@242,whole:Route@523,whole:RouteAlias@1003,whole:RouteAlias@1150 416a3342657d3672fea1fd5b31292148ee6b3b3a9601f5f48b8f436187944424
internal/edge/edge.go Edge.Reload legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body, which also hands the rows and alias rows it listed, with the table it built from them, to the local hold rebuild (holdInputs, rebuildHolds) before the reload hook runs; read sites unchanged, offsets moved; serving only registry:Account@18079,registry:Alias@16388,registry:Alias@16475,registry:Alias@16571,registry:FleetBox@5526,registry:Health@7689,registry:Health@7807,registry:Hibernate@12450,registry:Hibernate@14360,registry:Hibernate@6049,registry:Host@12498,registry:Host@14422,registry:Host@14730,registry:Host@14757,registry:Host@15525,registry:Host@15791,registry:Host@16514,registry:Host@6098,registry:Host@8290,registry:Host@8385,registry:Host@8665,registry:Host@8691,registry:Host@8743,registry:Kind@10975,registry:Kind@10997,registry:Kind@12426,registry:Kind@14336,registry:Kind@6025,registry:Kind@8831,registry:Kind@8891,registry:Name@18069,registry:Prefix@15766,registry:Region@2252,registry:Socket@14738,registry:Socket@5633,registry:Socket@6162,registry:Status@1835,registry:Target@15778,registry:Tunnel@4574,registry:Tunnel@4630,registry:Version@10956,registry:Version@11058,whole:Fleet@18019,whole:Fleet@18048,whole:Route@130,whole:Route@14790,whole:Route@14797,whole:Route@19692,whole:Route@19700,whole:Route@4692,whole:Route@5590,whole:Route@6218,whole:Route@648,whole:Route@8808,whole:Route@8881,whole:Route@8941,whole:RouteAlias@16359,whole:RouteAlias@19708,whole:RouteAlias@19717,whole:RoutePath@15503 e4e7b4dbdef60f59e2d9b3f4ad1d087d2044791764681bd827b60466ce8f7943
-internal/edge/edge.go Edge.ServeHTTP legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body (import gate after the door's scope, a gated row left out of the response cache), with the request's one decision (resolve: this box's table, its local holds and the home map's candidates, ranked) in place of the table lookup and the exact-home probe; the prefix target, the login re-resolution and the door's check read the decision's own table (selectUpstream), and a holding decision answers the holding page; read sites unchanged, offsets moved; serving only registry:Gate@14320,registry:Gate@14380,registry:Gate@14438,registry:Host@14423,registry:ImportGate@17495,registry:Kind@12559,registry:Kind@17466,whole:Route@10577,whole:Route@10789,whole:Route@12562,whole:Route@13120,whole:Route@13215,whole:Route@13291,whole:Route@14323,whole:Route@14383,whole:Route@14426,whole:Route@14441,whole:Route@15254,whole:Route@17469,whole:Route@17498,whole:Route@18644 5bbb4bf8899f9ce962487943ad520d3d4d07926a6eb23c44f81b034090c67112
+internal/edge/edge.go Edge.ServeHTTP legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body (import gate after the door's scope, a gated row left out of the response cache), with the request's one decision (resolve: this box's table, its local holds and the home map's candidates, ranked) in place of the table lookup and the exact-home probe; the prefix target, the login re-resolution and the door's check read the decision's own table (selectUpstream), and a holding decision answers the holding page; read sites unchanged, offsets moved; serving only; Task 2 fix round 1: the import gate is handed the decision's table (local.table) and judges the front row there registry:Gate@14333,registry:Gate@14393,registry:Gate@14451,registry:Host@14436,registry:ImportGate@17508,registry:Kind@12572,registry:Kind@17479,whole:Route@10577,whole:Route@10789,whole:Route@12575,whole:Route@13133,whole:Route@13228,whole:Route@13304,whole:Route@14336,whole:Route@14396,whole:Route@14439,whole:Route@14454,whole:Route@15267,whole:Route@17482,whole:Route@17511,whole:Route@18657 4ca7adfcab187bf1f37144d90e5396be6efdaa3429393471c5184e77760764b7
+@legacy 41ea8d89ccf9116ee6f6208c8edfa17bd6e79c8c2a7bd97c89c447af5dcc11a6 internal/edge/importgate.go Edge.importGateAdmit legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): the import gate (a gated row admits only a GET carrying the verify token; anything else is a 503), with host's own front row looked up in the table the request's decision was resolved in (self-serve moves plan 3 Task 2 fix round 1) rather than the live table; read sites unchanged, offsets moved; serving only registry:Host@364,registry:Host@378,registry:ImportGate@295,registry:ImportGate@318,registry:ImportGate@438,whole:Route@224,whole:Route@271,whole:Route@282 1207a7a3d9fa8c99debb563b456cfa4e472fd4c3ceba83157fb4189a12c53f70
+internal/edge/importgate.go Edge.importGateAdmit legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): the import gate (a gated row admits only a GET carrying the verify token; anything else is a 503), with host's own front row looked up in the table the request's decision was resolved in (self-serve moves plan 3 Task 2 fix round 1) rather than the live table; read sites unchanged, offsets moved; serving only registry:Host@364,registry:Host@378,registry:ImportGate@295,registry:ImportGate@318,registry:ImportGate@438,whole:Route@224,whole:Route@271,whole:Route@282 1207a7a3d9fa8c99debb563b456cfa4e472fd4c3ceba83157fb4189a12c53f70
internal/edge/edge.go Edge.currentHosts legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body, which also lists this box's real holds of platform rows (holdSnapshot.platformNames, which reads no row) so a held row's wildcard parent stays loaded; read sites unchanged; serving only (the wildcard parent set) registry:Platform@123,registry:Provisioned@144,whole:Route@126,whole:Route@147 88419f01a8c80da7e11def22cd5512365c097ec17ee5afa895c1863d6c858ff1
internal/edge/edge.go localReady legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): new, a table entry as a ranking candidate: reads the route the entry carries (its owner through release.RouteAccount, app, lifetime pair, class marks and, for an alias, its primary's host); serving only registry:App@258,registry:Host@498,registry:Incarnation@287,registry:Platform@357,registry:Provisioned@378,registry:ServingSeq@321,whole:Route@245,whole:Route@261,whole:Route@290,whole:Route@324,whole:Route@360,whole:Route@381,whole:Route@501 cabee4199c52c17849c70e7e6d7089d1e404c113008de3611083029bc4dd678d
internal/edge/holding.go localHolds legacy accepted-legacy accepted-legacy;rev3=PROPOSED-self-serve-moves-3;basis=root-route; the edge inside the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): new, the local holds: reads the rows and alias rows the latest Reload listed (a row's status, hold op, host, owner, pair and class marks; an alias row's name, primary, provenance and DNS status through edgesfile.AliasPublishable) against the offline view's moves; serving only (the holding page and its certificate), writes nothing registry:Alias@1108,registry:Alias@1235,registry:Alias@1256,registry:Alias@1284,registry:App@532,registry:Explicit@1051,registry:HoldOp@286,registry:HoldOp@392,registry:Host@1030,registry:Host@1409,registry:Host@359,registry:Host@446,registry:Host@716,registry:Host@741,registry:Host@768,registry:Host@902,registry:Incarnation@574,registry:Incarnation@926,registry:Platform@464,registry:Provisioned@478,registry:ServingSeq@601,registry:ServingSeq@953,registry:Status@273,whole:Route@242,whole:Route@523,whole:RouteAlias@1003,whole:RouteAlias@1150 416a3342657d3672fea1fd5b31292148ee6b3b3a9601f5f48b8f436187944424
@@ -36,7 +39,7 @@ internal/host/edgesfile.go edgesFileWriteClaims authority F,J1 rev3=PROPOSED-sel
internal/host/edgesfile.go holdingEntries authority F,J1 rev3=PROPOSED-self-serve-moves-3;basis=root-route;kept=F;scope=protected-decision-only; the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): new, the writer's holding entries: reads the registry rows edgesFileWriteClaims listed (Status, HoldOp, Host, Platform, Provisioned and the lifetime pair), their path prefixes (the owner projection, servedOwners) and their explicit alias rows through edgesfile.AliasPublishable, and the offline view's move for each held host; a held row whose hold op is the move id the view knows publishes one entry; a report, not a decision; no file, record, release or claim read registry:Alias@744,registry:Alias@799,registry:Explicit@663,registry:HoldOp@307,registry:HoldOp@404,registry:Host@1242,registry:Host@1279,registry:Host@375,registry:Host@438,registry:Host@467,registry:Host@646,registry:Host@766,registry:Host@790,registry:Host@886,registry:Host@931,registry:Host@992,registry:Incarnation@1070,registry:Platform@1010,registry:Provisioned@1024,registry:ServingSeq@1097,registry:Status@294,whole:Route@263,whole:Route@544,whole:Route@858,whole:RouteAlias@617,whole:RouteAlias@724,whole:RoutePath@558 da790578cea389cb43b649dea6b374236bed972f94d382ac85837de512625b2c
@fields A,Account,Accounts,AddedAt,Addr,Addrs,Alias,Aliases,Alive,AllowAbsent,App,Archives,Args,At,Attempts,B,Backing,Bucket,BuildState,Callback,CallbackError,CallbackFailed,Canary,Caps,CertExpiresAt,Changed,Channel,CheckedAt,ClaimIncarnation,ClaimOwner,ClaimOwnership,ClaimRowID,ClaimStamp,Class,Contents,CreatedAt,DBPath,DNSCheckedAt,DNSStatus,Deadline,Demand,Digest,DoneBefore,Due,DueAt,Env,EnvBefore,Environment,Error,ErrorKind,ExcludeHosts,ExpectGood,ExpectOp,ExpectPair,Expected,Explicit,F,Failed,FenceGen,Fleet,FleetBox,FleetBoxes,Fleets,From,FromDB,FromOp,FromSock,Gate,GoodDemand,GoodDigest,GoodVersion,Hash,Health,Held,Hibernate,HoldOp,Host,Hosts,ID,Id,IdValue,Idle,IdleSeconds,ImportGate,Incarnation,Journal,Keep,Kind,Label,LastAt,LastError,LastMs,LastStatus,Limit,MaxRun,MaxRunSeconds,Method,Ms,Name,Next,NextAt,NextDue,NextGood,NextValue,Now,Number,Number64,Observed,ObservedValue,Old,On,Op,Original,Owner,Ownership,P,Pair,Path,Paths,Platform,Pool,Prefix,Prev,PrevVersion,Provisioned,RecordKey,Region,Registration,Replica,Replicas,Replication,Retirement,Route,Routes,Row,Rows,Seed,SeedID,SeedSource,ServingSeq,ServingVersion,Slug,Socket,Spec,Sqid,State,Status,StatusNamed,Steering,StringMap,T,TValue,Tags,Target,Targets,Text,Timer,Timers,Times,To,ToDB,ToOp,ToSock,TokenHash,Tunnel,Unit,UpdateSettings,Ver,Version,W,WakeIncarnation,Wildcards,closed,conn,connect,ctx,d,dec,done,enc,full,input,mu,output,path,remote,sql,wait
@coverage internal/domains 0cf7748c97e4caa7d0daa73e251fdd2f2f03e425321599bf046292b0a86ebde8
-@coverage internal/edge 4c8497f4046f267d3238154db1a99ae6de4ccb6a71b0e678fbeac43149e1a372
+@coverage internal/edge 71e950c8159f277e3f5bf8af888057a088dec6d48f0f15e80a726bf78280d551
@coverage internal/edgesfile ee82e43c7d6363297f63abcde9c3e828a53d377573649f0d26a9853056aad4be
@coverage internal/host 27d077c9f63838516ad29fb69ea48ebbf85a7c17ade258a3b8ce2d57eb3cc21d
@coverage internal/registry ae19eaec5c4dde2c400fcb2fc6afd23c6b129e77306d6916fe55b2091b0fb5ed