package hostrelocate import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "os" "strings" "testing" "time" "github.com/carlosframework/platform/internal/genselect" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostfiles" "github.com/carlosframework/platform/internal/moves" "github.com/carlosframework/platform/internal/park" "github.com/carlosframework/platform/internal/release" ) const pinLocation = "instances/" + host + "/inc" func pinClaim() hostclaim.Observed { return hostclaim.Observed{Host: host, Claim: &hostclaim.Claim{Host: host, Gen: 12, Op: hostclaim.Op{Kind: hostclaim.KindRelocating, ID: "rel-1", Phase: hostclaim.PhasePinning, Journal: map[string]string{}}, Holder: &hostclaim.Holder{Location: pinLocation, Generation: "g3", Session: 3, Admissions: []hostclaim.AdmissionRecord{{Generation: "g2", Predecessor: "g1"}, {Generation: "g3", Predecessor: "g2"}}}}} } // keyed adds the host's instance.key (H6), as every manifest of a host // whose database is nonempty must carry (rev 18), and the platform items // every pinned manifest carries (withPlatformItems). func keyed(m hostfiles.Manifest) hostfiles.Manifest { sha := strings.Repeat("b", 64) m.Entries["instance.key"] = hostfiles.Entry{Object: pinLocation + "/files/g-g1/objects/0000000000000001-" + sha, SHA256: sha, Size: 32, Mode: 0o600, Seq: 1} return withPlatformItems(m) } // withPlatformItems adds the env file and the wake state, as a release // flush publishes them, where m has none (Task 8c: a pinned manifest // without either refuses). The pinner reads neither unless the key // evidence asks for the env. func withPlatformItems(m hostfiles.Manifest) hostfiles.Manifest { return withItem(withItem(m, hostfiles.EnvItem, "c"), hostfiles.WakeItem, "d") } func withItem(m hostfiles.Manifest, name, digit string) hostfiles.Manifest { if _, ok := m.Platform[name]; !ok { sha := strings.Repeat(digit, 64) m.Platform[name] = hostfiles.Entry{Object: pinLocation + "/files/g-g1/objects/0000000000000001-" + sha, SHA256: sha, Size: 10, Mode: 0o600, Seq: 1} } return m } // stagePlatformItems stages the env file and the wake state, as a release // flush does. func stagePlatformItems(t *testing.T, p *hostfiles.Publisher) { t.Helper() if err := p.StagePlatform(context.Background(), hostfiles.EnvItem, []byte("CARLOS_ADMIN_TOKEN=t\n")); err != nil { t.Fatal(err) } if err := p.StagePlatform(context.Background(), hostfiles.WakeItem, []byte(`{"v":1,"host":"`+host+`","timers":[]}`)); err != nil { t.Fatal(err) } } func putManifest(t *testing.T, data release.Store, gen string, m hostfiles.Manifest) (string, []byte) { t.Helper() raw, _ := json.Marshal(m) key := hostfiles.ManifestKey(moves.GenerationFilesPrefix(pinLocation, gen)) if _, err := data.Put(context.Background(), key, raw, ""); err != nil { t.Fatal(err) } return key, raw } func selectAt(txid uint64) func(context.Context, genselect.Request) (*genselect.Plan, error) { return func(_ context.Context, req genselect.Request) (*genselect.Plan, error) { return &genselect.Plan{Generation: req.Holder.Generation, MaxTXID: txid}, nil } } func TestStorePinnerPinsTheNewestFilesManifestInTheChain(t *testing.T) { ctx := context.Background() claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) // g3 (holder.generation) never published files; g2 did, then g1 earlier. for gen, seq := range map[string]int64{"g2": 5, "g1": 9} { putManifest(t, data, gen, keyed(hostfiles.Manifest{V: hostfiles.ManifestVersion, Seq: seq, Entries: map[string]hostfiles.Entry{}, Platform: map[string]hostfiles.Entry{}})) } selects := 0 p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: func(ctx context.Context, req genselect.Request) (*genselect.Plan, error) { selects++ return selectAt(41)(ctx, req) }} at := time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC) pin, err := p.Pin(ctx, pinClaim(), at) if err != nil { t.Fatal(err) } if pin.FilesManifest != hostfiles.ManifestKey(pinLocation+"/files/g-g2/") || pin.FilesSeq != 5 || pin.MaxTXID != 41 || pin.Key != PinKey(host, "rel-1", 12) || pin.Empty { t.Fatalf("pin: %+v", pin) } obj, err := ReadPin(ctx, claims, pin) if err != nil { t.Fatal(err) } if want := []string{pinLocation + "/files/g-g3/", pinLocation + "/files/g-g2/", pinLocation + "/files/g-g1/"}; !equalStrings(obj.Files.Origins, want) || obj.ClaimGen != 12 { t.Fatalf("origins %v gen %d", obj.Files.Origins, obj.ClaimGen) } // The exact bytes are retained in the control bucket before the pin. kept, _, kerr := claims.Get(ctx, obj.Files.Retained) if kerr != nil || obj.Files.Retained != RetainedManifestKey(host, "rel-1", obj.Files.SHA256) { t.Fatalf("retained copy: %s %v", obj.Files.Retained, kerr) } if sum := sha256.Sum256(kept); hex.EncodeToString(sum[:]) != obj.Files.SHA256 { t.Fatal("the retained copy is not the pinned bytes") } // A retry at the same observation reuses the object unchanged. again, err := p.Pin(ctx, pinClaim(), at.Add(time.Minute)) if err != nil || again != pin || selects != 1 { t.Fatalf("replay: %+v %v selects=%d", again, err, selects) } // The claim moved (the journal CAS lost): the next pin is a fresh // selection at the new generation; the old object is never reused. moved := pinClaim() moved.Claim.Gen = 13 next, err := p.Pin(ctx, moved, at.Add(2*time.Minute)) if err != nil || next.Key != PinKey(host, "rel-1", 13) || selects != 2 || next.PinnedAt.Equal(pin.PinnedAt) { t.Fatalf("after the claim moved: %+v %v selects=%d", next, err, selects) } // An object at the key that is not this operation's is refused. if _, err := claims.Put(ctx, PinKey(host, "rel-2", 12), []byte(`{"v":1,"host":"other","op":"rel-2","claim_gen":12,"db":{}}`), ""); err != nil { t.Fatal(err) } other := pinClaim() other.Claim.Op.ID = "rel-2" if _, err := p.Pin(ctx, other, at); !errors.Is(err, ErrRefused) { t.Fatalf("foreign pin object: %v", err) } // A rewritten object is caught by the journaled digest. tampered := pin tampered.SHA256 = strings.Repeat("0", 64) if _, err := ReadPin(ctx, claims, tampered); !errors.Is(err, ErrRefused) { t.Fatalf("a pin that does not hash to its digest was read: %v", err) } } func equalStrings(a, b []string) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true } func TestStorePinnerChecksTheBoundsBeforeItWrites(t *testing.T) { ctx := context.Background() at := time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC) release := func(txid uint64, seq int64) string { raw, _ := json.Marshal(hostclaim.Release{ReleasedBy: "src", Enrollment: "e", Generation: "g3", Session: 3, FinalTXID: txid, ManifestSeq: seq, Receipt: "r", ReceiptSHA256: strings.Repeat("d", 64)}) return string(raw) } for _, tc := range []struct { name string journal map[string]string txid uint64 seq int64 now time.Time }{ {"the listing is short of the release", map[string]string{"release": release(45, 5)}, 41, 5, at}, {"the files manifest is short of the release", map[string]string{"release": release(41, 6)}, 41, 5, at}, {"inside the failover window", map[string]string{"fence.src.observed_at": at.Format(time.RFC3339Nano)}, 41, 5, at.Add(29 * time.Second)}, } { t.Run(tc.name, func(t *testing.T) { claims, data := release_NewDirStore(t), release_NewDirStore(t) putManifest(t, data, "g3", keyed(hostfiles.Manifest{V: hostfiles.ManifestVersion, Seq: tc.seq, Entries: map[string]hostfiles.Entry{}, Platform: map[string]hostfiles.Entry{}})) o := pinClaim() o.Claim.Op.Journal = tc.journal p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: selectAt(tc.txid)} if _, err := p.Pin(ctx, o, tc.now); !errors.Is(err, ErrNotYet) { t.Fatalf("got %v, want ErrNotYet", err) } if keys, _ := claims.List(ctx, "control/relocations/"); len(keys) != 0 { t.Fatalf("a pin that cannot be journaled was written: %v", keys) } }) } } func release_NewDirStore(t *testing.T) release.Store { return release.NewDirStore(t.TempDir()) } func TestStorePinnerPinsEmptyOnlyWithEvidence(t *testing.T) { ctx := context.Background() at := time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC) empty := func(context.Context, genselect.Request) (*genselect.Plan, error) { return nil, genselect.ErrEmpty } released := func(txid uint64) string { raw, _ := json.Marshal(hostclaim.Release{ReleasedBy: "src", Enrollment: "e", Generation: "g3", Session: 3, FinalTXID: txid, Receipt: "r", ReceiptSHA256: strings.Repeat("d", 64)}) return string(raw) } for _, tc := range []struct { name string session uint64 journal map[string]string want string }{ {"a release at txid 0", 3, map[string]string{"release": released(0)}, "release"}, {"a never-woken host", 0, map[string]string{}, "session-0"}, {"a release at txid 40 and an empty listing", 3, map[string]string{"release": released(40)}, ""}, {"a failover of a host that had a writer", 3, map[string]string{}, ""}, } { t.Run(tc.name, func(t *testing.T) { claims, data := release_NewDirStore(t), release_NewDirStore(t) o := pinClaim() o.Claim.Holder.Session, o.Claim.Op.Journal = tc.session, tc.journal p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: empty} pin, err := p.Pin(ctx, o, at) if tc.want == "" { if !errors.Is(err, genselect.ErrEmpty) { t.Fatalf("an unevidenced empty listing was pinned: %+v %v", pin, err) } return } if err != nil || !pin.Empty || pin.MaxTXID != 0 || pin.Generation != "" { t.Fatalf("empty pin: %+v %v", pin, err) } obj, err := ReadPin(ctx, claims, pin) if err != nil || obj.Empty != tc.want || obj.DB != nil { t.Fatalf("object: %+v %v", obj, err) } }) } } func TestStorePinnerRefusesAnUnreadableManifest(t *testing.T) { ctx := context.Background() claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) if _, err := data.Put(ctx, hostfiles.ManifestKey(pinLocation+"/files/g-g3/"), []byte(`{"v":1}`), ""); err != nil { t.Fatal(err) } p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: selectAt(41)} if _, err := p.Pin(ctx, pinClaim(), time.Now()); !errors.Is(err, ErrRefused) { t.Fatalf("a version 1 manifest was pinned: %v", err) } } // pinnedManifest publishes gen's manifest whose one entry names an object // under origin (an earlier generation's prefix), and returns its pin. func pinnedManifest(t *testing.T, claims, data release.Store, gen, origin string, seq int64) (FilesPin, hostfiles.Manifest) { t.Helper() sum := sha256.Sum256([]byte("hello")) sha := hex.EncodeToString(sum[:]) object := origin + "objects/" + strings.Repeat("0", 15) + "1-" + sha if _, err := data.Put(context.Background(), object, []byte("hello"), ""); err != nil { t.Fatal(err) } m := hostfiles.Manifest{V: hostfiles.ManifestVersion, Seq: seq, Entries: map[string]hostfiles.Entry{"a.txt": {Object: object, SHA256: sha, Size: 5, Mode: 0o644, Seq: 1}}, Platform: map[string]hostfiles.Entry{}} _, raw := putManifest(t, data, gen, m) rawSum := sha256.Sum256(raw) manifestSHA := hex.EncodeToString(rawSum[:]) retained := RetainedManifestKey(host, "rel-1", manifestSHA) if _, err := claims.Put(context.Background(), retained, raw, ""); err != nil { t.Fatal(err) } prefix := moves.GenerationFilesPrefix(pinLocation, gen) return FilesPin{Prefix: prefix, Seq: seq, SHA256: manifestSHA, Origins: []string{prefix, origin}, Retained: retained}, m } func TestCopyPinnedManifestEnforcesThePin(t *testing.T) { ctx := context.Background() t.Run("a second relocation keeps objects of an earlier generation", func(t *testing.T) { claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) pin, want := pinnedManifest(t, claims, data, "P1", pinLocation+"/files/g-g1/", 3) got, err := CopyPinnedManifest(ctx, claims, data, pin, pinLocation+"/files/g-P2/") if err != nil || got.Seq != 0 || want.Seq != 3 || got.Entries["a.txt"] != want.Entries["a.txt"] { t.Fatalf("copy: %+v %v", got, err) } // A replay is a success; the copy's entries still name g1's object. if _, err := CopyPinnedManifest(ctx, claims, data, pin, pinLocation+"/files/g-P2/"); err != nil { t.Fatalf("replay: %v", err) } // Without the earlier generation among the origins the copy refuses. narrow := pin narrow.Origins = []string{pin.Prefix} if _, err := CopyPinnedManifest(ctx, claims, data, narrow, pinLocation+"/files/g-P3/"); !errors.Is(err, hostfiles.ErrOrigin) { t.Fatalf("an inherited object outside the origins: %v", err) } }) t.Run("a source manifest that moved after the pin is not read", func(t *testing.T) { // A late PUT to the source changes nothing: P is written from the // retained bytes, so an admitted pin stays usable (B2-4). claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) pin, m := pinnedManifest(t, claims, data, "P1", pinLocation+"/files/g-g1/", 3) m.Seq = 4 m.Entries["late.txt"] = m.Entries["a.txt"] raw, _ := json.Marshal(m) _, etag, _ := data.Get(ctx, hostfiles.ManifestKey(pin.Prefix)) if _, err := data.Put(ctx, hostfiles.ManifestKey(pin.Prefix), raw, etag); err != nil { t.Fatal(err) } got, err := CopyPinnedManifest(ctx, claims, data, pin, pinLocation+"/files/g-P2/") if _, late := got.Entries["late.txt"]; err != nil || got.Seq != 0 || late || len(got.Entries) != 1 { t.Fatalf("the pinned revision: %+v %v", got, err) } }) t.Run("a retained copy that is not the pinned bytes", func(t *testing.T) { claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) pin, m := pinnedManifest(t, claims, data, "P1", pinLocation+"/files/g-g1/", 3) e := m.Entries["a.txt"] e.Size = 6 m.Entries["a.txt"] = e raw, _ := json.Marshal(m) tampered := release.NewDirStore(t.TempDir()) if _, err := tampered.Put(ctx, pin.Retained, raw, ""); err != nil { t.Fatal(err) } if _, err := CopyPinnedManifest(ctx, tampered, data, pin, pinLocation+"/files/g-P2/"); !errors.Is(err, ErrRefused) { t.Fatalf("bytes the pin did not vouch for were copied: %v", err) } if _, _, err := data.Get(ctx, hostfiles.ManifestKey(pinLocation+"/files/g-P2/")); err == nil { t.Fatal("P's manifest was written from bytes the pin did not admit") } }) t.Run("a host that never published files", func(t *testing.T) { claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) pin := FilesPin{Prefix: pinLocation + "/files/g-g3/", Origins: []string{pinLocation + "/files/g-g3/"}} m, err := CopyPinnedManifest(ctx, claims, data, pin, pinLocation+"/files/g-P1/") if err != nil || m.Seq != 0 || len(m.Entries) != 0 { t.Fatalf("empty manifest: %+v %v", m, err) } if _, err := CopyPinnedManifest(ctx, claims, data, pin, pinLocation+"/files/g-P1/"); err != nil { t.Fatalf("replay: %v", err) } }) } // Round 11: holder.admissions keeps eight entries, but an unchanged file // published at g1 is still referenced by every later generation's // manifest (each copies the last, and unchanged content is never // rewritten). Ten real admissions and publications later, with g1 trimmed // from the admission list, the pin still admits g1's object and the copy // succeeds; a reference outside this incarnation's files refuses. func TestPinKeepsAFileOriginTheAdmissionListTrimmed(t *testing.T) { ctx := context.Background() claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) created := time.Date(2026, 10, 4, 1, 0, 0, 0, time.UTC) nh := hostclaim.NewHolder{Family: "instance", Key: "apps/bab/jam/instances/" + host + ".json", Account: "bab", App: "jam", Host: host, Region: "ie", RecordRegion: "ie", CreatedAt: created} body := []byte(`{"host":"` + host + `","account":"bab","app":"jam","region":"ie","created_at":"2026-10-04T01:00:00Z"}`) step := func(o hostclaim.Observed, tr func(*hostclaim.Claim) error) hostclaim.Observed { t.Helper() next, err := hostclaim.Update(ctx, claims, o, tr) if err != nil { t.Fatal(err) } return next } o, err := hostclaim.Read(ctx, claims, host) if err != nil { t.Fatal(err) } o = step(o, hostclaim.BeginCreate(nh, body, "c", "console", created)) o = step(o, hostclaim.CompleteCreate("c", "src")) loc := o.Claim.Holder.Location n := 0 newID := func() string { n++; return fmt.Sprintf("id%d", n) } var gens []string prev, prevSeq := "", int64(0) for i := 1; i <= 10; i++ { gen := fmt.Sprintf("g%d", i) o = step(o, hostclaim.AdmitWriter(hostclaim.Admission{Region: "ie", PredecessorRegion: "ie", ObservedSession: o.Claim.Holder.Session, ObservedGeneration: o.Claim.Holder.Generation, Box: "src", Mode: "cold", NewGeneration: gen, PredecessorCertified: true})) prefix := moves.GenerationFilesPrefix(loc, gen) gens = append(gens, prefix) if prev != "" { if _, err := hostfiles.CopyManifest(ctx, data, prev, prefix, prevSeq, gens); err != nil { t.Fatal(err) } } p, err := hostfiles.Open(ctx, data, prefix) if err != nil { t.Fatal(err) } // kept.txt and instance.key never change after g1; changed.txt // changes every time. if err := p.Stage(ctx, "kept.txt", []byte("written once"), 0o644, false); err != nil { t.Fatal(err) } if err := p.Stage(ctx, "instance.key", []byte("the host's key"), 0o600, false); err != nil { t.Fatal(err) } stagePlatformItems(t, p) if err := p.Stage(ctx, "changed.txt", []byte(gen), 0o644, false); err != nil { t.Fatal(err) } if prevSeq, err = p.Commit(ctx); err != nil { t.Fatal(err) } prev = prefix o = step(o, hostclaim.PublishGeneration("", "", o.Claim.Holder.Session, gen, "ie", newID)) } h := o.Claim.Holder if len(h.Admissions) != 8 { t.Fatalf("admissions not trimmed to eight: %d", len(h.Admissions)) } for _, a := range h.Admissions { if a.Generation == "g1" || a.Predecessor == "g1" { t.Fatalf("g1 is still in the admission list: %+v", h.Admissions) } } m, _, err := hostfiles.Load(ctx, data, loc+"/files/g-g10/") if err != nil || !strings.HasPrefix(m.Entries["kept.txt"].Object, loc+"/files/g-g1/objects/") { t.Fatalf("the unchanged file is not g1's object: %+v %v", m.Entries["kept.txt"], err) } obs := o obs.Claim.Op.ID = "rel-1" p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: selectAt(41)} pin, err := p.Pin(ctx, obs, time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC)) if err != nil { t.Fatal(err) } obj, err := ReadPin(ctx, claims, pin) if err != nil { t.Fatal(err) } got, err := CopyPinnedManifest(ctx, claims, data, obj.Files, loc+"/files/g-P1/") if err != nil || got.Entries["kept.txt"] != m.Entries["kept.txt"] { t.Fatalf("copy with g1 trimmed from the admissions: %+v %v", got.Entries, err) } // A reference outside this incarnation's files is never admitted. foreign := m foreign.Entries = map[string]hostfiles.Entry{"instance.key": m.Entries["instance.key"], "x.txt": {Object: "instances/" + host + "/other-inc/files/g-g1/objects/" + strings.Repeat("a", 64), Seq: 1, SHA256: strings.Repeat("a", 64)}} raw, _ := json.Marshal(foreign) _, etag, _ := data.Get(ctx, hostfiles.ManifestKey(loc+"/files/g-g10/")) if _, err := data.Put(ctx, hostfiles.ManifestKey(loc+"/files/g-g10/"), raw, etag); err != nil { t.Fatal(err) } obs.Claim.Gen++ if _, err := p.Pin(ctx, obs, time.Date(2026, 10, 7, 9, 1, 0, 0, time.UTC)); !errors.Is(err, hostfiles.ErrOrigin) { t.Fatalf("a foreign reference was pinned: %v", err) } } // Round 14 finding 1: the source's files manifest is at sequence 8; P's // prepared manifest is a create-only copy of its entries at sequence 0 // (claim rev 6.30 R18-1, moves rev 7.22 ยง7.6); the session publisher that // starts after the first admission takes it as its baseline and publishes // sequence 1, the inherited entries intact. func TestPreparedManifestIsSequenceZeroAndTheSessionPublishesOne(t *testing.T) { ctx := context.Background() claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) src := pinLocation + "/files/g-g3/" p, err := hostfiles.Open(ctx, data, src) if err != nil { t.Fatal(err) } for i := 1; i <= 7; i++ { if i == 7 { // The seventh revision also publishes the host's key (H6), and // its env file and wake state, as the release flush does. if err := p.Stage(ctx, "instance.key", []byte("the host's key"), 0o600, false); err != nil { t.Fatal(err) } stagePlatformItems(t, p) } if err := p.Stage(ctx, "a.txt", []byte(fmt.Sprintf("revision %d", i)), 0o644, false); err != nil { t.Fatal(err) } if _, err := p.Commit(ctx); err != nil { t.Fatal(err) } } pinner := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: selectAt(41)} ref, err := pinner.Pin(ctx, pinClaim(), time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC)) if err != nil || ref.FilesSeq != 10 { // seven revisions of a.txt, the key, the env and the wake state: ten objects t.Fatalf("pin: %+v %v", ref, err) } obj, err := ReadPin(ctx, claims, ref) if err != nil { t.Fatal(err) } prepared := pinLocation + "/files/g-P1/" m, err := CopyPinnedManifest(ctx, claims, data, obj.Files, prepared) if err != nil || m.Seq != 0 { t.Fatalf("prepared manifest: %+v %v", m, err) } stored, _, err := hostfiles.Load(ctx, data, prepared) if err != nil || stored.Seq != 0 || stored.Entries["a.txt"].Object == "" || !strings.HasPrefix(stored.Entries["a.txt"].Object, src+"objects/") { t.Fatalf("stored prepared manifest: %+v %v", stored, err) } session, err := hostfiles.Open(ctx, data, prepared) if err != nil { t.Fatal(err) } if err := session.Stage(ctx, "b.txt", []byte("first session write"), 0o644, false); err != nil { t.Fatal(err) } seq, err := session.Commit(ctx) if err != nil || seq != 1 { t.Fatalf("the first session publication: %d %v", seq, err) } after, _, err := hostfiles.Load(ctx, data, prepared) if err != nil || after.Seq != 1 || after.Entries["a.txt"] != stored.Entries["a.txt"] || after.Entries["b.txt"].Object == "" { t.Fatalf("after the first publication: %+v %v", after, err) } } // Two-box model finding F2: no files manifest anywhere in the chain, and // a writer was admitted (session 3): the pin refuses rather than pin an // empty set an install would mint a fresh instance.key over. A holder // that never admitted a writer pins empty. func TestStorePinnerRefusesAnEmptyFilesPinAfterAWriter(t *testing.T) { ctx := context.Background() at := time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC) for _, tc := range []struct { session uint64 want error }{{3, ErrFilesUnevidenced}, {0, nil}} { claims, data := release_NewDirStore(t), release_NewDirStore(t) o := pinClaim() o.Claim.Holder.Session = tc.session p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: selectAt(41)} pin, err := p.Pin(ctx, o, at) if !errors.Is(err, tc.want) { t.Fatalf("session %d: %+v %v", tc.session, pin, err) } } } // Round 17 finding 1 (P1): a manifest exists, copied before the app // started, without the instance.key the app minted afterwards; the writer // was admitted and the database is nonempty. The pin refuses (the worker // marks the operation stuck); with the key in the manifest, or an empty // database, it pins. func TestStorePinnerRefusesAKeylessManifestForANonemptyDatabase(t *testing.T) { ctx := context.Background() at := time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC) empty := func(context.Context, genselect.Request) (*genselect.Plan, error) { return nil, genselect.ErrEmpty } released0 := func() string { raw, _ := json.Marshal(hostclaim.Release{ReleasedBy: "src", Enrollment: "e", Generation: "g3", Session: 3, FinalTXID: 0, ManifestSeq: 4, Receipt: "r", ReceiptSHA256: strings.Repeat("d", 64)}) return string(raw) } for _, tc := range []struct { name string key bool db func(context.Context, genselect.Request) (*genselect.Plan, error) journal map[string]string want error }{ {"keyless, nonempty database", false, selectAt(41), map[string]string{}, ErrFilesUnevidenced}, {"keyed, nonempty database", true, selectAt(41), map[string]string{}, nil}, {"keyless, empty database at a release of 0", false, empty, map[string]string{"release": released0()}, nil}, } { t.Run(tc.name, func(t *testing.T) { claims, data := release_NewDirStore(t), release_NewDirStore(t) m := withPlatformItems(hostfiles.Manifest{V: hostfiles.ManifestVersion, Seq: 4, Entries: map[string]hostfiles.Entry{}, Platform: map[string]hostfiles.Entry{}}) if tc.key { m = keyed(m) } putManifest(t, data, "g3", m) o := pinClaim() o.Claim.Op.Journal = tc.journal p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: tc.db} if _, err := p.Pin(ctx, o, at); !errors.Is(err, tc.want) { t.Fatalf("got %v, want %v", err, tc.want) } }) } } // routeRelease stands in for the region controller's registry route // (registry.Route.Version), the pinner's only source for a pin's release. func routeRelease(_ context.Context, h string) (string, error) { if h != host { return "", fmt.Errorf("asked for %s's route", h) } return "v1.4.2", nil } // R9: the pin's release is the registry route's, read at pinning and // nowhere else; a reuse carries the stored value; no release refuses. func TestStorePinnerPinsTheRouteRelease(t *testing.T) { ctx := context.Background() at := time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC) claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) putManifest(t, data, "g3", keyed(hostfiles.Manifest{V: hostfiles.ManifestVersion, Seq: 5, Entries: map[string]hostfiles.Entry{}, Platform: map[string]hostfiles.Entry{}})) var asked []string route := "v1.4.2" p := StorePinner{Claims: claims, Data: data, Bucket: "data", Region: "ie", SelectDB: selectAt(41), Release: func(_ context.Context, h string) (string, error) { asked = append(asked, h); return route, nil }} // Everything a box or a request could have written says otherwise; the // pinner never reads any of it. o := pinClaim() o.Claim.Op.Journal["install_release"] = "v-box" o.Claim.Op.Journal["local_install"] = `{"box":"tgt","release":"v-box"}` o.Claim.Op.Intent = map[string]string{"release": "v-request", "version": "v-request"} pin, err := p.Pin(ctx, o, at) if err != nil { t.Fatal(err) } if pin.Release != "v1.4.2" || len(asked) != 1 || asked[0] != host { t.Fatalf("release %q, route asked for %v", pin.Release, asked) } if obj, err := ReadPin(ctx, claims, pin); err != nil || obj.Release != "v1.4.2" { t.Fatalf("object: %+v %v", obj, err) } // A journaled copy whose release is not the object's is refused, though // the bytes still hash to the digest. for _, rel := range []string{"v1.5.0", ""} { forged := pin forged.Release = rel if _, err := ReadPin(ctx, claims, forged); !errors.Is(err, ErrRefused) { t.Fatalf("read a pin whose journaled release %q is not the object's: %v", rel, err) } } // The route moves on; a retry at the same generation reuses the stored // object, its release included, and does not ask the route again. route = "v1.5.0" again, err := p.Pin(ctx, o, at.Add(time.Minute)) if err != nil || again != pin || again.Release != "v1.4.2" || len(asked) != 1 { t.Fatalf("reuse: %+v %v asked %v", again, err, asked) } // A route with no version, or an unreadable route, refuses before // anything is written. for name, rel := range map[string]func(context.Context, string) (string, error){ "empty": func(context.Context, string) (string, error) { return "", nil }, "unreadable": func(context.Context, string) (string, error) { return "", errors.New("registry unavailable") }, } { t.Run(name, func(t *testing.T) { claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) putManifest(t, data, "g3", keyed(hostfiles.Manifest{V: hostfiles.ManifestVersion, Seq: 5, Entries: map[string]hostfiles.Entry{}, Platform: map[string]hostfiles.Entry{}})) p := StorePinner{Claims: claims, Data: data, Bucket: "data", Region: "ie", SelectDB: selectAt(41), Release: rel} _, err := p.Pin(ctx, pinClaim(), at) if err == nil || (name == "empty" && !errors.Is(err, ErrRefused)) { t.Fatalf("pinned without a release: %v", err) } if keys, lerr := claims.List(ctx, "control/"); lerr != nil || len(keys) != 0 { t.Fatalf("a refused pin wrote %v (%v)", keys, lerr) } }) } // No route at all is unsupported, as every other missing provider is. p.Release = nil if _, err := p.Pin(ctx, o, at); !errors.Is(err, ErrUnsupported) { t.Fatalf("no release provider: %v", err) } // A stored object without a release is not this pinner's: refused. bare := pinClaim() bare.Claim.Gen = 20 if _, err := claims.Put(ctx, PinKey(host, "rel-1", 20), []byte(`{"v":1,"host":"`+host+`","op":"rel-1","claim_gen":20,"empty":"session-0","files":{}}`), ""); err != nil { t.Fatal(err) } p.Release = routeRelease if _, err := p.Pin(ctx, bare, at); !errors.Is(err, ErrRefused) { t.Fatalf("a pin object with no release was reused: %v", err) } } // Absence is never evidence: a pin with no database is an empty bootstrap // only when the object names its evidence and the journal agrees, and a // files pin is a host that published no files only when it names neither // a digest nor a retained copy. An object with neither a database nor // evidence, one the journal disagrees with about emptiness, or a files pin // with one of digest and retained copy but not the other, is refused, // never read as empty. func TestReadPinNeedsTheEmptyEvidence(t *testing.T) { ctx := context.Background() plan := &genselect.Plan{Generation: "g3", MaxTXID: 41} for _, tc := range []struct { name string obj PinObject empty bool // the journal's copy ok bool }{ {"an evidenced empty bootstrap", PinObject{Empty: "session-0"}, true, true}, {"a database", PinObject{DB: plan}, false, true}, {"no database and no evidence", PinObject{}, true, false}, {"a database and empty evidence", PinObject{DB: plan, Empty: "release"}, false, false}, {"an empty pin the journal calls a database", PinObject{Empty: "session-0"}, false, false}, {"a database the journal calls empty", PinObject{DB: plan}, true, false}, {"a files pin with its digest and retained copy", PinObject{Empty: "session-0", Files: FilesPin{SHA256: strings.Repeat("c", 64), Retained: "control/retained.json"}}, true, true}, {"a files digest without its retained copy", PinObject{Empty: "session-0", Files: FilesPin{SHA256: strings.Repeat("c", 64)}}, true, false}, {"a retained copy without its digest", PinObject{Empty: "session-0", Files: FilesPin{Retained: "control/retained.json"}}, true, false}, } { t.Run(tc.name, func(t *testing.T) { claims := release.NewDirStore(t.TempDir()) obj := tc.obj obj.V, obj.Host, obj.Op, obj.ClaimGen, obj.Release = 1, host, "rel-1", 12, "v1" raw, err := json.Marshal(obj) if err != nil { t.Fatal(err) } key := PinKey(host, "rel-1", 12) if _, err := claims.Put(ctx, key, raw, ""); err != nil { t.Fatal(err) } ref := pinRef(key, raw, obj) ref.Empty = tc.empty _, err = ReadPin(ctx, claims, ref) if tc.ok && err != nil { t.Fatalf("an evidenced pin was refused: %v", err) } if !tc.ok && !errors.Is(err, ErrRefused) { t.Fatalf("got %v, want ErrRefused", err) } }) } } // Task 8c fix round 1, M6: the install's rule at pin time. A pinned // manifest without the env file or without the wake state is no evidence // that the host has neither (platform items have no tombstone, and a // failover's manifest carries no wake state), so the pin refuses before // any build: no pin object, no retained copy. A host that never published // files still pins empty (TestStorePinnerRefusesAnEmptyFilesPinAfterAWriter). func TestStorePinnerRefusesAManifestWithoutTheEnvOrWakeState(t *testing.T) { ctx := context.Background() at := time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC) for _, tc := range []struct { name string missing string want error }{ {"both", "", nil}, {"no env file", hostfiles.EnvItem, ErrFilesUnevidenced}, {"no wake state", hostfiles.WakeItem, ErrFilesUnevidenced}, } { t.Run(tc.name, func(t *testing.T) { claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) m := keyed(hostfiles.Manifest{V: hostfiles.ManifestVersion, Seq: 5, Entries: map[string]hostfiles.Entry{}, Platform: map[string]hostfiles.Entry{}}) delete(m.Platform, tc.missing) putManifest(t, data, "g3", m) o := pinClaim() p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", SelectDB: selectAt(41)} _, err := p.Pin(ctx, o, at) if !errors.Is(err, tc.want) { t.Fatalf("got %v, want %v", err, tc.want) } if tc.want == nil { return } if _, _, err := claims.Get(ctx, PinKey(host, "rel-1", o.Claim.Gen)); !errors.Is(err, os.ErrNotExist) { t.Fatalf("a pin object was written: %v", err) } if keys, err := claims.List(ctx, ""); err != nil || len(keys) != 0 { t.Fatalf("the refused pin wrote %v %v", keys, err) } }) } } // Main's home-region parks: an admission moves recorded in the home names // it park.HomeRegion. The pinner's selection sees the holder through the // box's home binding (park.HolderRegionView), as main's serving baseline // does, so that admission is this region's; with no binding the holder is // read exactly as stored. func TestStorePinnerSelectsThroughTheHomeBinding(t *testing.T) { ctx := context.Background() for _, home := range []string{"ie", ""} { t.Run("home "+home, func(t *testing.T) { claims, data := release.NewDirStore(t.TempDir()), release.NewDirStore(t.TempDir()) putManifest(t, data, "g1", keyed(hostfiles.Manifest{V: hostfiles.ManifestVersion, Seq: 9, Entries: map[string]hostfiles.Entry{}, Platform: map[string]hostfiles.Entry{}})) var seen []string p := StorePinner{Claims: claims, Data: data, Release: routeRelease, Bucket: "data", Region: "ie", HomeRegion: home, SelectDB: func(ctx context.Context, req genselect.Request) (*genselect.Plan, error) { for _, a := range req.Holder.Admissions { seen = append(seen, a.Region+"/"+a.PredecessorRegion) } return selectAt(41)(ctx, req) }} obs := pinClaim() obs.Claim.Holder.Admissions[1].Region, obs.Claim.Holder.Admissions[1].PredecessorRegion = park.HomeRegion, "uk" if _, err := p.Pin(ctx, obs, time.Date(2026, 10, 7, 9, 0, 0, 0, time.UTC)); err != nil { t.Fatal(err) } want := "@home/uk" if home != "" { want = "ie/uk" } if len(seen) != 2 || seen[1] != want { t.Fatalf("selection saw %v, want second %s", seen, want) } if obs.Claim.Holder.Admissions[1].Region != park.HomeRegion { t.Fatal("the view changed the claim's own holder") } }) } }