Artifact fe2ac7ad5374408eab245f699f4dc1b34147fc4ffaa61f955cd1f66000e6246c:
- File
internal/sync/stubs.go
— part of check-in
[8802c5704f]
at
2026-10-29 02:06:06
on branch feat/horizon-peer-serving
— fix(sync): a horizon store refuses a pull it does not hold and mints no seed
A server over a horizon clone answered a horizon pull or sync at an earlier C from its own partial graph. It now refuses any horizon pull whose held set is not a subset of its own with one error card and no content, rolled back whole, and a go-libfossil client fails the round on that reply (draft-fossil-horizon-00 §6.4). partial-mirror serves it, as it does an uncovered clone. The clone coverage check now judges the horizon the request asked for, not the depth serving cut it to.
A store at a structural horizon no longer mints a seed from its own seed: it answers a structural request at the content level (§6.2.3) and names its horizon-history-url. The requester falls back and reports HorizonRemedy, naming C and the dense origin that holds the manifests below it; the CLI prints it.
The concept doc states when a horizon store may serve a horizon peer. Conformance covers §6.4 on the pull path, a structural store answering as content, and the fallback a structural clone takes from such a peer; zit pulls from a go-libfossil horizon store in both outcomes.
Closes #490 (user: [email protected], size: 3958) [annotate] [blame] [check-ins using]
package sync import ( "github.com/danmestas/go-libfossil/internal/cone" "github.com/danmestas/go-libfossil/simio" ) // CloneOpts configures a clone operation. type CloneOpts struct { StoreCone *cone.Cone // nil is dense; local policy, never synchronized User string // Credentials for clone auth (also default admin user) Password string Version int // Protocol version (default 3) Env *simio.Env // nil defaults to RealEnv Observer Observer // nil defaults to no-op Buggify BuggifyChecker // fault injection for DST (nil = no faults) // PullPhantoms, when set, makes Clone fetch the artifacts the clone // rounds left missing with a follow-up pull session, and fail with a // *PhantomStallError if any are still missing after it. That pull // departs from Section 8.2 of the sync draft, which sends no clone, // pull or push card after clone_seqno 0, so it is off by default. PullPhantoms bool // SeedKey, when set, is the origin's public key: an SSH public key line // or an OpenPGP fingerprint. The clone keeps it as its horizon-seed-key // setting and verifies every structural seed against it // (draft-fossil-horizon-00 §5.2.4); empty accepts an unsigned seed. SeedKey string // Origin names the peer the clone addresses, keying the horizon cursor // a converged horizon clone records (draft-fossil-horizon-00 §6.2.6) as // SyncOpts.Origin keys a sync's. Empty keeps none. Origin string // Named, when set, asks for the store's horizon by name rather than by // hash (draft-fossil-horizon-00 §6.1, §6.2.7); StoreCone then gives the // path members alone and must carry no horizon. The store records only // the check-in the name resolves to. Named *NamedHorizon } // NamedHorizon is a horizon asked for by name: the origin resolves Name, // tip, a symbolic name or a full check-in hash, and steps back Depth // check-ins along primary parents (§6.2.7); a peer that does not resolve it // serves densely and the clone resolves it at the content horizon (§6.3). type NamedHorizon struct { Name string Depth uint32 Rule cone.HorizonRule Level cone.HorizonDepth } // CloneResult reports what happened during a clone. type CloneResult struct { Elided int // artifacts deliberately absent under the store cone ConeServed int // replies acknowledging the store cone (sparse-cone §6.3); the rest were filtered locally HorizonServed int // replies serving the store's horizon at either level (horizon §6.2.5); the rest were classified locally // StructuralServed counts the replies that served the structural level, // and HorizonFallback reports a structural clone that kept the content // horizon instead: its origin did not serve the structural level with a // seed in the first reply, or the seed failed its provenance (§5.1, // §5.2.4, §6.3). StructuralServed int HorizonFallback bool Rounds int BlobsRecvd int ArtifactsLinked int // Manifests crosslinked into event table ProjectCode string ServerCode string Messages []string // Informational messages from server // HorizonHistoryURL is the interface outside the protocol at which the // origin says history below a horizon can be queried (horizon-served // URL, §6.2.5), the last a reply named; "" when none did. HorizonHistoryURL string // HorizonRemedy is set when HorizonFallback is: it names C, the // manifests below it the store now wants, and where they are held, the // history interface a reply named when one did (§6.3, §6.4). HorizonRemedy string // HorizonResolved is the check-in a clone's Named horizon resolved to and // HorizonSteps the steps back it took, fewer than its Depth when // history ran out; HorizonResolvedLocally reports the clone resolved it // itself, its origin having answered no horizon-resolved (§6.3). HorizonResolved string HorizonSteps uint32 HorizonResolvedLocally bool }