Persistent store with Git semantics: lazy reads, delayed writes, content-addressing
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ocaml-merkle-sync: generic Merkle DAG sync; irmin Sync.S module type

ocaml-merkle-sync: reusable sync primitives for any content-addressed DAG.
- Layer 1: anti-entropy gossip (branch head exchange, O(log n))
- Layer 2: merkle descent (DAG diff by hash, dependency-order transfer)
- Bloom filter (FNV-1a, ~1% FP rate)
15 tests passing. Fuzz harness for bloom properties.

irmin/lib/sync.ml: Sync.S module type — discover/locate/fetch/push.
Each backend provides its own implementation.

Also: dune fmt across affected packages.

+39 -42
+11 -9
lib/sync.ml
··· 1 - module type TRANSPORT = sig 1 + module type S = sig 2 2 type t 3 3 type hash 4 4 5 - val fetch : t -> branch:string -> (hash, [ `Msg of string ]) result 6 - val push : t -> branch:string -> hash -> (unit, [ `Msg of string ]) result 7 - end 5 + val discover : t -> (string * hash) list 6 + val locate : t -> local_has:(hash -> bool) -> hash -> hash list 7 + val fetch : t -> hash list -> (hash * string) list 8 8 9 - type resolver = 10 - | Fail 11 - | Ours 12 - | Theirs 13 - | Custom of (string list -> string option -> string option -> string) 9 + val push : 10 + t -> 11 + (hash * string) list -> 12 + branch:string -> 13 + hash -> 14 + (unit, [ `Msg of string ]) result 15 + end
+28 -33
lib/sync.mli
··· 1 - (** Distributed sync: transport + merge composition. 1 + (** Sync backend interface. 2 2 3 - Sync composes a backend-specific {!TRANSPORT} (block transfer) with Schema's 4 - merge (conflict resolution). Three modes: 3 + Each backend provides its own implementation of the four sync operations. 4 + Irmin composes them: [discover] → [locate] → [fetch] → [Schema.merge] → 5 + (optionally) [push]. 5 6 6 - - {b Explicit}: user-triggered pull/push (like Git). 7 - - {b Continuous}: automatic gossip between peers (eventually consistent). 8 - - {b DTN}: delay-tolerant via space-dtn bundle envelopes. 7 + - Git: packfile negotiation, [git fetch] / [git push] 8 + - ATProto: CAR download/upload via XRPC 9 + - Local: direct heap access via [Git.Fetch.fetch_local] 10 + - Generic: [ocaml-merkle-sync] (gossip + merkle descent + bloom) *) 9 11 10 - The transport handles "get blocks from there to here." The merge handles 11 - "reconcile concurrent modifications." They compose in [pull]: 12 - [transport.fetch] + [Schema.merge] + [update_branch]. *) 12 + module type S = sig 13 + type t 14 + (** A connection to a remote store. *) 13 15 14 - (** {1 Transport} 16 + type hash 15 17 16 - Backend-specific block transfer. Each backend implements this: Git uses 17 - fetch_local / git-fetch, ATProto uses CAR import/export, OCI uses registry 18 - pull/push. *) 18 + val discover : t -> (string * hash) list 19 + (** [discover t] returns the remote's branch heads. *) 19 20 20 - module type TRANSPORT = sig 21 - type t 22 - type hash 21 + val locate : t -> local_has:(hash -> bool) -> hash -> hash list 22 + (** [locate t ~local_has remote_head] returns hashes reachable from 23 + [remote_head] that are missing locally. Result is in dependency order: 24 + children before parents. *) 23 25 24 - val fetch : t -> branch:string -> (hash, [ `Msg of string ]) result 25 - (** [fetch t ~branch] ensures all blocks reachable from the remote's [branch] 26 - tip are available locally. Returns the remote's HEAD hash. *) 26 + val fetch : t -> hash list -> (hash * string) list 27 + (** [fetch t hashes] retrieves blocks by hash from the remote. *) 27 28 28 - val push : t -> branch:string -> hash -> (unit, [ `Msg of string ]) result 29 - (** [push t ~branch hash] pushes [hash] and its reachable blocks to the 30 - remote. Fast-forward only — fails if the remote has diverged. *) 29 + val push : 30 + t -> 31 + (hash * string) list -> 32 + branch:string -> 33 + hash -> 34 + (unit, [ `Msg of string ]) result 35 + (** [push t blocks ~branch head] sends [blocks] to the remote and updates 36 + [branch] to [head]. Fast-forward only. *) 31 37 end 32 - 33 - (** {1 Resolver} 34 - 35 - Conflict resolution strategy for pull/merge. *) 36 - 37 - type resolver = 38 - | Fail (** Abort on any conflict. *) 39 - | Ours (** Keep our version for all conflicts. *) 40 - | Theirs (** Keep their version for all conflicts. *) 41 - | Custom of (string list -> string option -> string option -> string) 42 - (** [Custom f] calls [f path ours theirs] for each conflict. *)