carlos / platform Public

host: origins are proven against the claim's recorded admissions, never by the manifest (Task 6 fix round 3, platform-1e's ruling A)

Manifest.Origins carried the trust it was meant to add: the credentials
that write a manifest write its Origins too. The set stays as a carried
hint only. Every origin a manifest names or carries must now be proven
against admissions the claim has recorded, behind movesOriginProof:
today chainOriginProof, the retained admission chain (plus the
generation the admission in hand opens, which the claim's AdmitWriter
CAS recorded before Materialize runs); later claim Plan 15's durable
per-incarnation record, through the same interface (movesFiles.Proof).
Anything unprovable refuses with ErrOrigin: a host past the pruning
window refuses until Plan 15 lands, as ruled.

Tests: the pruned ancestor (g1..g10, admissions g3..g10) now refuses
with the retained chain alone and passes with a proof that records g1,
as Plan 15's record will; a manifest that adds a never-granted origin
to its own Origins is refused, with the object named or not.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev c707325e0391b862929a612ad785c91948c7958b parent 404f81f
2 files changed, +120 −23
  • internal/host/movesfiles.go +78 −22
  • internal/host/movesfiles_test.go +42 −1
diff --git a/internal/host/movesfiles.go b/internal/host/movesfiles.go
index a3778611e..c6057687e 100644
--- a/internal/host/movesfiles.go
+++ b/internal/host/movesfiles.go
@@ -13,6 +13,7 @@ import (
"os"
"path/filepath"
"reflect"
+ "slices"
"sort"
"strings"
@@ -76,6 +77,11 @@ type movesFiles struct {
// FilesDir is the host's files directory and env file on this box
// (moveCfg.FilesDir).
FilesDir func(host string) (dir, envPath string)
+ // Proof proves an origin against admissions the claim has recorded
+ // (platform-1e's ruling, fix round 3): nil means chainOriginProof, the
+ // retained admission chain, until claim Plan 15's durable
+ // per-incarnation record slots in behind movesOriginProof.
+ Proof movesOriginProof
// Evidence reads the claim pin's files evidence for a host, when the
// claim carries a pin: region HA's ReadPin over the op journal's pin
// reference. false means the claim carries no pin. nil means no pin
@@ -374,7 +380,7 @@ func (f *movesFiles) manifestVersion(ctx context.Context, key string) (string, e
// malformed or not (a malformed newest manifest refuses, never an older
// one in its place). found is false when no generation of the chain has a
// manifest.
-func (f *movesFiles) newestManifest(ctx context.Context, location string, chain []string) (hostfiles.Manifest, []string, bool, error) {
+func (f *movesFiles) newestManifest(ctx context.Context, o hostclaim.Observed, in hostclaim.Admission, location string, chain []string) (hostfiles.Manifest, []string, bool, error) {
for _, g := range chain {
prefix := movesFilesPrefix(location, g)
key := hostfiles.ManifestKey(prefix)
@@ -392,7 +398,7 @@ func (f *movesFiles) newestManifest(ctx context.Context, location string, chain
if !found {
return hostfiles.Manifest{}, nil, false, fmt.Errorf("host: the files manifest %s@%s is not readable", key, version)
}
- allowed, err := movesFilesAllowed(location, chain, m)
+ allowed, err := f.movesFilesAllowed(ctx, o, in, location, chain, m)
if err != nil {
return hostfiles.Manifest{}, nil, false, fmt.Errorf("host: the files manifest %s@%s: %w", key, version, err)
}
@@ -401,34 +407,84 @@ func (f *movesFiles) newestManifest(ctx context.Context, location string, chain
return hostfiles.Manifest{}, nil, false, nil
}
+// movesOriginProof proves that a generation of the holder's incarnation
+// was admitted, by the claim's own records (platform-1e's ruling, fix
+// round 3): provenance comes from the claim, never from a manifest, whose
+// writer holds the bucket credentials that could write its Origins too.
+// Today's implementation is chainOriginProof, the retained admission
+// chain; claim Plan 15's durable per-incarnation record (platform-1e's,
+// with a read API) slots in behind this interface. Anything unprovable
+// refuses: a host past the pruning window refuses until Plan 15 lands,
+// and that is accepted.
+type movesOriginProof interface {
+ Proven(ctx context.Context, o hostclaim.Observed, in hostclaim.Admission, generation string) (bool, error)
+}
+
+// chainOriginProof proves a generation by the retained admission chain
+// (movesFilesChain: the holder's generation and each cold admission's
+// predecessor in this region), and the generation the admission in hand
+// opens: the claim's AdmitWriter CAS recorded that admission before the
+// start's Materialize runs.
+type chainOriginProof struct{ home, region string }
+
+func (p chainOriginProof) Proven(_ context.Context, o hostclaim.Observed, in hostclaim.Admission, generation string) (bool, error) {
+ if generation != "" && generation == in.NewGeneration {
+ return true, nil
+ }
+ if o.Claim == nil || o.Claim.Holder == nil {
+ return false, nil
+ }
+ return slices.Contains(movesFilesChain(o.Claim.Holder, p.home, p.region), generation), nil
+}
+
+func (f *movesFiles) proof() movesOriginProof {
+ if f.Proof != nil {
+ return f.Proof
+ }
+ return chainOriginProof{home: f.HomeRegion, region: f.Region}
+}
+
// movesFilesAllowed is the origin set an admission allows for m, the
-// manifest it establishes from (fix round 2, finding 3; platform-1e's
-// ruling): the set m carries plus the generations admitted in the
-// current chain. An old manifest with no carried set is judged by the
-// chain alone. Every origin m's entries name must lie in it (a generation
-// never admitted and never carried is ErrOrigin, whatever its name), after
-// the location rule (movesFilesOrigins) and ValidateManifest. The new
-// manifest's set is this plus its own prefix (SetOrigins).
-func movesFilesAllowed(location string, chain []string, m hostfiles.Manifest) ([]string, error) {
+// manifest it establishes from: every origin m's entries name, every
+// origin m carries (Manifest.Origins, a hint and nothing more), and the
+// generations admitted in the current chain, each proven against the
+// claim's records (movesOriginProof) after the location rule
+// (movesFilesOrigins). A named or carried origin the claim cannot prove is
+// ErrOrigin, whatever its name and whether an entry uses it: a manifest
+// that adds a never-granted origin to its own set is refused. Then
+// ValidateManifest over the proven set. The new manifest's set is this plus
+// its own prefix (SetOrigins).
+func (f *movesFiles) movesFilesAllowed(ctx context.Context, o hostclaim.Observed, in hostclaim.Admission, location string, chain []string, m hostfiles.Manifest) ([]string, error) {
set := map[string]bool{}
for _, g := range chain {
set[movesFilesPrefix(location, g)] = true
}
- for _, o := range m.Origins {
- set[o] = true
- }
named, err := movesFilesOrigins(location, "", m)
if err != nil {
return nil, err
}
- for _, o := range named {
- if !set[o] {
- return nil, fmt.Errorf("%w: %s is neither admitted in the chain nor carried by the manifest", hostfiles.ErrOrigin, o)
+ base := strings.TrimSuffix(location, "/") + "/files/g-"
+ for _, origin := range append(append([]string(nil), named...), m.Origins...) {
+ if set[origin] {
+ continue
+ }
+ rest, under := strings.CutPrefix(origin, base)
+ gen, slash := strings.CutSuffix(rest, "/")
+ if !under || !slash || !movesFilesGeneration(gen) {
+ return nil, fmt.Errorf("%w: %s is not a generation's files prefix of %s", hostfiles.ErrOrigin, origin, location)
+ }
+ proven, err := f.proof().Proven(ctx, o, in, gen)
+ if err != nil {
+ return nil, fmt.Errorf("prove the origin %s: %w", origin, err)
+ }
+ if !proven {
+ return nil, fmt.Errorf("%w: %s is not proven by the claim's recorded admissions", hostfiles.ErrOrigin, origin)
}
+ set[origin] = true
}
allowed := make([]string, 0, len(set))
- for o := range set {
- allowed = append(allowed, o)
+ for origin := range set {
+ allowed = append(allowed, origin)
}
sort.Strings(allowed)
if err := hostfiles.ValidateManifest(m, allowed); err != nil {
@@ -490,7 +546,7 @@ func movesFilesOrigins(location, prefix string, m hostfiles.Manifest) ([]string,
// establishment of them (a retry after a crash past the commit: the same
// entries, the env bytes on disk, no adoption item, which the publisher
// then leaves as it is). Anything else refuses before any write.
-func (f *movesFiles) openPrepared(ctx context.Context, files release.Store, o hostclaim.Observed, gen, prefix string, env []byte) (*hostfiles.Publisher, error) {
+func (f *movesFiles) openPrepared(ctx context.Context, files release.Store, o hostclaim.Observed, in hostclaim.Admission, gen, prefix string, env []byte) (*hostfiles.Publisher, error) {
if f.DataDir == "" {
return nil, hostwrite.ErrUnavailable
}
@@ -525,7 +581,7 @@ func (f *movesFiles) openPrepared(ctx context.Context, files release.Store, o ho
if len(prepared.Origins) == 0 {
prepared.Origins = []string{prefix}
}
- allowed, err := movesFilesAllowed(h.Location, append(movesFilesChain(h, f.HomeRegion, f.Region), gen), prepared)
+ allowed, err := f.movesFilesAllowed(ctx, o, in, h.Location, append(movesFilesChain(h, f.HomeRegion, f.Region), gen), prepared)
if err != nil {
return nil, fmt.Errorf("host: the prepared files manifest %s@%s: %w", key, pm.Version, err)
}
@@ -729,7 +785,7 @@ func (f *movesFiles) Establish(ctx context.Context, o hostclaim.Observed, in Cla
var p *hostfiles.Publisher
switch {
case in.Prepared:
- p, err = f.openPrepared(ctx, files, o, gen, prefix, env)
+ p, err = f.openPrepared(ctx, files, o, in.Admission, gen, prefix, env)
if err != nil {
return err
}
@@ -740,7 +796,7 @@ func (f *movesFiles) Establish(ctx context.Context, o hostclaim.Observed, in Cla
}
p.SetOrigins([]string{prefix})
default:
- base, origins, found, err := f.newestManifest(ctx, h.Location, movesFilesChain(h, f.HomeRegion, f.Region))
+ base, origins, found, err := f.newestManifest(ctx, o, in.Admission, h.Location, movesFilesChain(h, f.HomeRegion, f.Region))
if err != nil {
return err
}
diff --git a/internal/host/movesfiles_test.go b/internal/host/movesfiles_test.go
index 30574cb47..1bc735370 100644
--- a/internal/host/movesfiles_test.go
+++ b/internal/host/movesfiles_test.go
@@ -536,8 +536,21 @@ func TestEstablishKeepsOriginsPastAdmissionPruning(t *testing.T) {
if len(o.Claim.Holder.Admissions) != 8 {
t.Fatalf("%d admissions", len(o.Claim.Holder.Admissions))
}
+ // Provenance comes from the claim (platform-1e's ruling, fix round 3):
+ // the carried set is a hint, and every origin must be proven against
+ // admissions the claim has recorded. Today that is the retained chain,
+ // which no longer names g1, so the establishment refuses; it passes
+ // only with a proof that records g1, as claim Plan 15's durable
+ // per-incarnation record will.
+ if err := x.f.Establish(ctx, o, launchInputs("g11"), x); !errors.Is(err, hostfiles.ErrOrigin) {
+ t.Fatalf("past pruning with the retained chain alone: %v", err)
+ }
+ if _, found := x.manifest("g11"); found {
+ t.Fatal("a manifest was written over an unproven ancestor")
+ }
+ x.f.Proof = recordedOrigins{"g1": true, "g2": true, "g3": true, "g4": true, "g5": true, "g6": true, "g7": true, "g8": true, "g9": true, "g10": true}
if err := x.f.Establish(ctx, o, launchInputs("g11"), x); err != nil {
- t.Fatalf("establish past pruning: %v", err)
+ t.Fatalf("establish past pruning with the claim's record: %v", err)
}
m, found := x.manifest("g11")
if !found || m.Entries["a.txt"].Object != movesFilesPrefix(filesLoc, "g1")+"objects/0000000000000001-"+sha([]byte("A")) {
@@ -831,6 +844,25 @@ func TestEstablishRefusesANeverAdmittedOrigin(t *testing.T) {
t.Fatalf("%s: a manifest was written over a never-admitted origin", name)
}
}
+ // Origins vouch for nothing by themselves (platform-1e's ruling): a
+ // manifest that adds the never-granted origin to its own carried set,
+ // with the object named or not, is refused, and so is a carried set
+ // naming a never-granted origin with no entry under it.
+ for name, entries := range map[string]map[string]hostfiles.Entry{
+ "named and carried": {"a.txt": g1.Entries["a.txt"], "n.txt": {Object: never, SHA256: sha([]byte("N")), Size: 1, Seq: 1}},
+ "carried only": {"a.txt": g1.Entries["a.txt"]},
+ } {
+ bad := g1
+ bad.Entries = entries
+ bad.Origins = []string{movesFilesPrefix(filesLoc, "g1"), movesFilesPrefix(filesLoc, "never")}
+ x.bucket.Put("data", hostfiles.ManifestKey(movesFilesPrefix(filesLoc, "g1")), mustJSON(t, bad))
+ if err := x.f.Establish(ctx, o, launchInputs("g2"), x); !errors.Is(err, hostfiles.ErrOrigin) {
+ t.Fatalf("%s: %v", name, err)
+ }
+ if _, found := x.manifest("g2"); found {
+ t.Fatalf("%s: a manifest was written over a self-vouched origin", name)
+ }
+ }
// The genuine manifest establishes, and the new set is exactly the
// chain and the new generation.
x.bucket.Put("data", hostfiles.ManifestKey(movesFilesPrefix(filesLoc, "g1")), mustJSON(t, g1))
@@ -888,6 +920,15 @@ func TestEstablishSeedsALegacyLocationsOriginSet(t *testing.T) {
}
}
+// recordedOrigins is a claim-backed origin proof that records the named
+// generations: what claim Plan 15's durable per-incarnation record will
+// answer.
+type recordedOrigins map[string]bool
+
+func (r recordedOrigins) Proven(_ context.Context, _ hostclaim.Observed, _ hostclaim.Admission, generation string) (bool, error) {
+ return r[generation], nil
+}
+
// The instance key rule (O1'): the manifest Establish leaves carries the
// env file with exactly the bytes on disk when it runs, staged and
// committed before it returns, and no adoption item; the local adoption