Artifact c004db609626ba8e79d6e35125e23884a6b45a35c0b553301fa9482d7ea91cad:
- File
internal/sync/stubs.go
— part of check-in
[54d75fbc78]
at
2026-10-24 00:37:10
on branch main
— fix(sync): resolve horizon names only for authorized requesters
An origin resolved a horizon-name before the requester's login was read, and served a requester that may neither read nor clone at the horizon the name resolved to, so whether a name exists showed in its reply. The login card is now checked first, and only a requester that may read or clone has the name resolved; anyone else is answered as if the request named no horizon, the same reply for any name. A pull naming an unknown horizon is refused with the one error card, not served densely.
The client fails the clone on a second horizon-resolved in a reply, and on one answering a request that sent no horizon-name. A private origin's challenge does not settle the name: the next request resends it with the login, still the clone's first request under §6.1.
API: CloneOpts.Horizon replaces CloneOpts.HorizonName and the stand-in check-in a named horizon's StoreCone had to carry. It reuses the Horizon type, whose Checkin may there be a name, beside the existing HorizonDepth, and keeps the rule and the structural level where they were, so no public concept is added. StoreCone gives the path members alone: a horizon on both, or a depth without a Horizon, is an error. A full hash at depth 0 goes on the store cone as itself. Internally a NamedHorizon carries the name, depth, rule and level, not a cone holding a placeholder hash, and only the resolved hash is ever stored. The CLI passes --horizon through the same field. (user: [email protected], size: 3732) [annotate] [blame] [check-ins using] [more...]
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 // 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 }