package host import ( "context" "encoding/json" "os" "path/filepath" "testing" "time" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostevidence" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/hostops" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/release" "golang.org/x/sys/unix" ) func migrationHistoricFixture(t *testing.T, store release.Store, r ClaimRuntimeResources, owners ClaimOwnerBindings, actor hostbox.Actor, plan *hostops.BackfillPlan) (hostguard.FenceTarget, []byte) { t.Helper() ctx := t.Context() at := time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC) rec := provision.Record{Schema: 1, Host: "historic.example", Account: "bab", App: "app", Channel: "stable", Region: "ie", CreatedAt: at} raw, _ := json.Marshal(rec) key := provision.RecordKey(rec.Account, rec.App, rec.Host) tag, err := store.Put(ctx, key, raw, "") if err != nil { t.Fatal(err) } plan.Hosts[rec.Host] = hostops.BackfillHost{Key: key, ETag: tag} inc, err := hostclaim.Incarnation(rec.Account, rec.App, rec.Host, at) if err != nil { t.Fatal(err) } claim := hostclaim.Claim{V: 2, Host: rec.Host, Gen: 1, Holder: &hostclaim.Holder{Family: "instance", Key: key, Account: rec.Account, App: rec.App, CreatedAt: at, Incarnation: inc, Location: "instances/" + rec.Host, Generation: "generation", Stamp: 1, Session: 9, WriterBox: actor.Box, Admissions: []hostclaim.AdmissionRecord{{Region: "ie", Session: 9, Box: actor.Box, Generation: "generation", Mode: "cold", Covered: true}}}, Op: hostclaim.Op{Kind: hostclaim.KindServing, Exec: actor.Box, Participants: []string{actor.Box}}} raw, _ = json.Marshal(claim) ckey, _ := hostclaim.Key(rec.Host) ctag, err := store.Put(ctx, ckey, raw, "") if err != nil { t.Fatal(err) } if _, err := hostclaim.Read(ctx, store, rec.Host); err != nil { t.Fatal("claim fixture", err) } target := hostguard.FenceTarget{Host: rec.Host, Incarnation: inc, Stamp: 1, Session: 9, Box: actor.Box, Enrollment: actor.Enrollment, Region: "ie", DirectoryRevision: "1", BootID: "historic-boot", ControllerID: "historic-controller", ClaimGen: 1, ClaimETag: ctag, FenceID: "historic-fence"} if err := r.Registry.UpsertTimer(registry.Timer{Host: rec.Host, Name: "refresh", Kind: "cron", Spec: "0 0 * * *", Path: "/refresh"}); err != nil { t.Fatal(err) } legacy := filepath.Join(r.DataDir, ".hostclaims") launches, err := hostbox.OpenLaunchJournal(filepath.Join(legacy, "launches.db")) if err != nil { t.Fatal(err) } launch := hostbox.LaunchInventory{Target: target, ControllerPID: 999999, ProcessStart: "1", NoState: true, Groups: []hostbox.LaunchGroup{{RelativePath: "historic-session", Device: 1, Inode: 2}}} if err := launches.Create(ctx, launch); err != nil { t.Fatal(err) } if err := launches.CloseWriters(ctx, target); err != nil { t.Fatal(err) } journal, err := launches.ReadStopKernelJournal(ctx, target) if err != nil { t.Fatal(err) } if err := launches.Close(); err != nil { t.Fatal(err) } path := filepath.Join(r.DataDir, "historic-data") if err := os.WriteFile(path, []byte("retained tenant bytes"), 0600); err != nil { t.Fatal(err) } var st unix.Stat_t if err := unix.Lstat(path, &st); err != nil { t.Fatal(err) } in := hostbox.StopInventory{Complete: true, Host: rec.Host, Incarnation: inc, InstallationStamp: 1, Box: actor.Box, Enrollment: actor.Enrollment, DirectoryRevision: "1", Entries: []hostbox.StopInventoryEntry{{Kind: "path", ArtifactID: "historic-data", ManagedRoot: "data", RelativePath: "historic-data", Ownership: "historic-owner", Materialization: "materialized", Device: uint64(st.Dev), Inode: st.Ino, Type: st.Mode & unix.S_IFMT}, {Kind: "timer", ArtifactID: "timer", ManagedRoot: "timers", RelativePath: rec.Host + "/refresh", Ownership: "historic-owner", Materialization: "materialized"}}} in.Entries[0].Identity = testPersistentInventory(t, path) inventory, err := openLegacyInventory(t, filepath.Join(legacy, "inventory.db")) if err != nil { t.Fatal(err) } if err := inventory.WriteStopInventory(ctx, in); err != nil { t.Fatal(err) } if err := inventory.Close(); err != nil { t.Fatal(err) } receipt := StopReceipt{V: 1, ID: "historic-stop", Kind: "stopped", Host: target.Host, Incarnation: inc, InstallationStamp: 1, WriterSession: 9, Box: actor.Box, Enrollment: actor.Enrollment, Region: "ie", BootID: target.BootID, ObservedBootID: target.BootID, ControllerID: target.ControllerID, DirectoryRevision: "1", Binding: "1", ClaimGen: 1, ClaimETag: ctag, FenceID: target.FenceID, EvidenceAt: at, RequestLimitNS: int64(hostbox.RequestLimit), InventorySHA256: digestStop(in), LocalJournalSHA256: digestStop(journal), CgroupIdentities: []string{"historic-session"}} raw, _ = json.Marshal(receipt) if err := hostevidence.Publish(ctx, store, owners.EvidenceKey, stopEvidenceKey(target.Host, "", actor.Box, receipt.ID), raw); err != nil { t.Fatal(err) } return target, raw } func TestPlan14RootMigrationHistoricEvidence(t *testing.T) { store, r, owners, _ := fix4JournalFixture(t) actor, err := owners.Identity.Verify(t.Context()) if err != nil { t.Fatal(err) } plan := hostops.BackfillPlan{Hosts: map[string]hostops.BackfillHost{}} target, raw := migrationHistoricFixture(t, store, r, owners.ClaimOwnerBindings, actor, &plan) j, err := hostbox.OpenLaunchJournal(filepath.Join(t.TempDir(), "root.db")) if err != nil { t.Fatal(err) } defer j.Close() service, err := hostbox.NewJournalService(j) if err != nil { t.Fatal(err) } if err := service.MigrateLegacy(t.Context(), filepath.Join(r.DataDir, ".hostclaims"), actor.Box, actor.Enrollment, func(context.Context) (func(), error) { return func() {}, nil }, legacyJournalProof(store, owners.RetirementAuthority.Directory)); err != nil { t.Fatal("signed legacy import", err) } launch, err := j.Read(t.Context(), target) if err != nil { t.Fatal(err) } result, err := readMigrationStop(t.Context(), store, owners.RetirementAuthority.Directory, j, service.Inventory, launch) if err != nil || string(result.Bytes) != string(raw) { t.Fatal("exact historic evidence", err) } signatureKey := result.Key + ".sig" validSignature, signatureETag, err := store.Get(t.Context(), signatureKey) if err != nil { t.Fatal(err) } badETag, err := store.Put(t.Context(), signatureKey, []byte("forged"), signatureETag) if err != nil { t.Fatal(err) } if _, err := readMigrationStop(t.Context(), store, owners.RetirementAuthority.Directory, j, service.Inventory, launch); err == nil { t.Fatal("forged historic signature accepted") } if _, err := store.Put(t.Context(), signatureKey, validSignature, badETag); err != nil { t.Fatal(err) } if _, err := readMigrationStop(t.Context(), store, owners.RetirementAuthority.Directory, j, service.Inventory, launch); err != nil { t.Fatal("valid evidence after refusal", err) } launch.Target.Session++ if _, err := readMigrationStop(t.Context(), store, owners.RetirementAuthority.Directory, j, service.Inventory, launch); err == nil { t.Fatal("foreign session evidence") } }