carlos / platform Public

lease: BindRoot, the root layout's per-request authority (Task 5 fix round 1, ruling 2)

A root-generation checkpoint checked its authority once and then ran every
replica request through an ordinary client. BindRoot wraps the client at
the lease's own path in the same checking client BindGeneration installs,
so an authority withdrawn between two requests refuses the second before it
reaches storage. Immutable, exclusive with BindGeneration, retention off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 37a5d350135a3ac1c8bffdc58131e0325c7ce816 parent cb9928d
2 files changed, +82 −0
  • internal/lease/claimgeneration.go +33 −0
  • internal/lease/claimgeneration_test.go +49 −0
diff --git a/internal/lease/claimgeneration.go b/internal/lease/claimgeneration.go
index a2f987404..d02f72154 100644
--- a/internal/lease/claimgeneration.go
+++ b/internal/lease/claimgeneration.go
@@ -46,6 +46,39 @@ func (h *Held) BindGeneration(prefix string, check func(context.Context) error)
return nil
}
+// BindRoot is BindGeneration for the root layout (hostclaim.RootGeneration):
+// the replica data sits directly below the held lease's location, so no
+// prefix changes, but every replica request still first passes check, the
+// authority read before each one (moves §5.3 step 5, ruling G3; Task 5 fix
+// round 1, ruling 2). Without it a root-generation checkpoint would check
+// its authority once and then run every request unchecked. As immutable as
+// BindGeneration, and exclusive with it. Retention stays off under it, as
+// under a bound generation: the checking client refuses DeleteLTXFiles.
+func (h *Held) BindRoot(check func(context.Context) error) error {
+ if h == nil || h.leaser == nil || check == nil {
+ return ErrGenerationBinding
+ }
+ base := strings.TrimSuffix(path.Clean(h.leaser.Path), "/")
+ if base == "." || base == "" {
+ return ErrGenerationBinding
+ }
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ if h.busy || h.spent || h.generationPrefix != "" {
+ return ErrGenerationBinding
+ }
+ client := h.replicaClient
+ if client == nil {
+ client = newReplicaClient(h.leaser.Bucket, base, h.region)
+ }
+ h.replicaClient = &generationClient{client: client, check: check}
+ // The lease's own path marks the binding, so neither BindGeneration nor
+ // a second BindRoot can change the client under a running checkpoint,
+ // and newStore keeps retention off.
+ h.generationPrefix = base
+ return nil
+}
+
type generationClient struct {
client litestream.ReplicaClient
check func(context.Context) error
diff --git a/internal/lease/claimgeneration_test.go b/internal/lease/claimgeneration_test.go
index 805fa7942..68b87a421 100644
--- a/internal/lease/claimgeneration_test.go
+++ b/internal/lease/claimgeneration_test.go
@@ -194,3 +194,52 @@ func TestGenerationClientRealRetentionMonitorsDoNotDelete(t *testing.T) {
t.Fatal(err)
}
}
+
+// BindRoot gives the root layout the same per-request authority a bound
+// generation has (moves Task 5 fix round 1, ruling 2): every replica request
+// below the lease's own path first passes check, so an authority withdrawn
+// between two requests refuses the second before it reaches storage. It is
+// as immutable as BindGeneration, and the two exclude each other.
+func TestBindRootChecksEveryRequest(t *testing.T) {
+ leaser := lss3.NewLeaser()
+ leaser.Path = "instances/shop.example"
+ f := &generationFake{}
+ h := &Held{leaser: leaser, replicaClient: f}
+ withdrawn := errors.New("the claim moved")
+ calls := 0
+ check := func(context.Context) error {
+ calls++
+ if calls > 1 {
+ return withdrawn
+ }
+ return nil
+ }
+ if err := h.BindRoot(check); err != nil {
+ t.Fatal(err)
+ }
+ if err := h.replicaClient.Init(t.Context()); err != nil {
+ t.Fatalf("first request: %v", err)
+ }
+ if _, err := h.replicaClient.LTXFiles(t.Context(), 0, 0, false); !errors.Is(err, withdrawn) {
+ t.Fatalf("second request after the authority was withdrawn: %v", err)
+ }
+ if f.calls != 1 {
+ t.Fatalf("storage saw %d requests, want the first only", f.calls)
+ }
+ if err := h.BindRoot(check); err != ErrGenerationBinding {
+ t.Fatalf("rebind=%v", err)
+ }
+ if err := h.BindGeneration("instances/shop.example/g-a", check); err != ErrGenerationBinding {
+ t.Fatalf("a generation over the root binding=%v", err)
+ }
+ h2 := &Held{leaser: leaser, replicaClient: &generationFake{}}
+ if err := h2.BindGeneration("instances/shop.example/g-a", check); err != nil {
+ t.Fatal(err)
+ }
+ if err := h2.BindRoot(check); err != ErrGenerationBinding {
+ t.Fatalf("the root over a generation binding=%v", err)
+ }
+ if err := (&Held{leaser: leaser}).BindRoot(nil); err != ErrGenerationBinding {
+ t.Fatalf("nil check=%v", err)
+ }
+}