Artifact cb67fe8ec7c25000b5f65d84fe0dc4236714244aeaaae8d213470d3e8b71a2c7:
- File
docs/site/content/docs/reference/sdk/dst/api.md
— part of check-in
[6273a0750d]
at
2026-07-22 23:03:24
on branch trunk
— fix: correct go.mod module path to go-libfossil (#76) (#117)
* fix: correct go.mod module path to go-libfossil (#76)
The module directive still declared github.com/danmestas/libfossil after the repository was renamed to go-libfossil. pkg.go.dev/godoc scraping matches the declared module path against its hosting path, so the stale path broke SDK doc generation.
Rename the module path across all four in-repo modules (root, the two db/driver submodules, observer/otel, and docs/site) and every internal import, plus the release workflow, the doc-generation script, and the docs.
No tag has been published under the go-libfossil path yet, so the cross-module requires cannot resolve from the proxy. Add replace directives pointing the in-repo requires at their local directories so GOWORK=off builds and the CI matrix resolve without a published version. These replaces are ignored by downstream consumers and can be dropped once new-path tags are cut.
* Guard releases against replace directives; regenerate SDK docs
The go-libfossil rename bridges the in-repo modules with `replace` directives. Go refuses a main module that carries them, so a tag cut while they are present breaks `go install .../cmd/libfossil@<tag>` and every `@version` fetch of the driver submodules. Add a Release workflow step that fails the job before anything is published if any tracked go.mod still contains a replace directive, so the bridge is a CI-enforced TODO rather than a silent landmine. The step comment records what unblocks a release: re-tag the submodules with the corrected module paths, bump the root requires, then drop the replace blocks.
Regenerate the SDK reference docs. The previous pass only substituted URLs over the pre-existing generated files, leaving stale signatures (Repo.Timeline was still documented as taking LogOpts rather than TimelineOpts). Note that gomarkdoc must be run outside a git worktree — inside one it silently strips every source link.
Also reword the README rename note, which read as though the import path had not changed, and re-sort the ncruces require block. (user: [email protected], size: 33213) [annotate] [blame] [check-ins using]
--- title: dst --- <!-- Code generated by gomarkdoc. DO NOT EDIT --> # dst ```go import "github.com/danmestas/go-libfossil/dst" ``` ## Index - [func AllBlobUUIDsSorted\(r \*repo.Repo\) \(\[\]string, error\)](<#AllBlobUUIDsSorted>) - [func CheckBlobIntegrity\(nodeID string, r \*repo.Repo\) error](<#CheckBlobIntegrity>) - [func CheckConvergence\(master \*repo.Repo, leaves map\[NodeID\]\*repo.Repo\) error](<#CheckConvergence>) - [func CheckDeltaChains\(nodeID string, r \*repo.Repo\) error](<#CheckDeltaChains>) - [func CheckNoOrphanPhantoms\(nodeID string, r \*repo.Repo\) error](<#CheckNoOrphanPhantoms>) - [func CheckSubsetOf\(master \*repo.Repo, leaves map\[NodeID\]\*repo.Repo\) error](<#CheckSubsetOf>) - [func CheckTableSyncConvergence\(repos map\[string\]\*repo.Repo\) error](<#CheckTableSyncConvergence>) - [func CheckTableSyncIntegrity\(nodeID string, r \*repo.Repo\) error](<#CheckTableSyncIntegrity>) - [func CheckTagxrefIntegrity\(nodeID string, r \*repo.Repo\) error](<#CheckTagxrefIntegrity>) - [func CheckUVConvergence\(master \*repo.Repo, leaves map\[NodeID\]\*repo.Repo\) error](<#CheckUVConvergence>) - [func CheckUVIntegrity\(nodeID string, r \*repo.Repo\) error](<#CheckUVIntegrity>) - [func CountBlobs\(r \*repo.Repo\) \(int, error\)](<#CountBlobs>) - [func HasBlob\(r \*repo.Repo, uuid string\) bool](<#HasBlob>) - [type Action](<#Action>) - [type ActionType](<#ActionType>) - [type DefaultNode](<#DefaultNode>) - [func NewDefaultNode\(r \*repo.Repo, t sync.Transport, projectCode, serverCode string, opts DefaultNodeOpts\) \*DefaultNode](<#NewDefaultNode>) - [func \(n \*DefaultNode\) Repo\(\) \*repo.Repo](<#DefaultNode.Repo>) - [func \(n \*DefaultNode\) Tick\(ctx context.Context, event EventType\) Action](<#DefaultNode.Tick>) - [type DefaultNodeOpts](<#DefaultNodeOpts>) - [type Event](<#Event>) - [type EventQueue](<#EventQueue>) - [func \(q EventQueue\) Len\(\) int](<#EventQueue.Len>) - [func \(q EventQueue\) Less\(i, j int\) bool](<#EventQueue.Less>) - [func \(q \*EventQueue\) Pop\(\) any](<#EventQueue.Pop>) - [func \(q \*EventQueue\) PopEvent\(\) \*Event](<#EventQueue.PopEvent>) - [func \(q \*EventQueue\) Push\(x any\)](<#EventQueue.Push>) - [func \(q \*EventQueue\) PushEvent\(e \*Event\)](<#EventQueue.PushEvent>) - [func \(q EventQueue\) Swap\(i, j int\)](<#EventQueue.Swap>) - [type EventType](<#EventType>) - [type InvariantError](<#InvariantError>) - [func \(e \*InvariantError\) Error\(\) string](<#InvariantError.Error>) - [type MockFossil](<#MockFossil>) - [func NewMockFossil\(r \*repo.Repo\) \*MockFossil](<#NewMockFossil>) - [func \(f \*MockFossil\) Exchange\(ctx context.Context, req \*xfer.Message\) \(\*xfer.Message, error\)](<#MockFossil.Exchange>) - [func \(f \*MockFossil\) Repo\(\) \*repo.Repo](<#MockFossil.Repo>) - [func \(f \*MockFossil\) SetBuggify\(b libsync.BuggifyChecker\)](<#MockFossil.SetBuggify>) - [func \(f \*MockFossil\) StoreArtifact\(data \[\]byte\) \(string, error\)](<#MockFossil.StoreArtifact>) - [type Node](<#Node>) - [type NodeID](<#NodeID>) - [type PeerNetwork](<#PeerNetwork>) - [func NewPeerNetwork\(rng \*rand.Rand\) \*PeerNetwork](<#NewPeerNetwork>) - [func \(n \*PeerNetwork\) AddPeer\(id NodeID, r \*repo.Repo\)](<#PeerNetwork.AddPeer>) - [func \(n \*PeerNetwork\) Heal\(id NodeID\)](<#PeerNetwork.Heal>) - [func \(n \*PeerNetwork\) HealAll\(\)](<#PeerNetwork.HealAll>) - [func \(n \*PeerNetwork\) Partition\(id NodeID\)](<#PeerNetwork.Partition>) - [func \(n \*PeerNetwork\) SetBuggify\(b libsync.BuggifyChecker\)](<#PeerNetwork.SetBuggify>) - [func \(n \*PeerNetwork\) SetDropRate\(rate float64\)](<#PeerNetwork.SetDropRate>) - [func \(n \*PeerNetwork\) Transport\(source, target NodeID\) \*PeerTransport](<#PeerNetwork.Transport>) - [type PeerTransport](<#PeerTransport>) - [func \(t \*PeerTransport\) Exchange\(ctx context.Context, req \*xfer.Message\) \(\*xfer.Message, error\)](<#PeerTransport.Exchange>) - [type SeededBuggify](<#SeededBuggify>) - [func \(b \*SeededBuggify\) Check\(\_ string, probability float64\) bool](<#SeededBuggify.Check>) - [type SimConfig](<#SimConfig>) - [type SimNetwork](<#SimNetwork>) - [func NewSimNetwork\(rng \*rand.Rand, upstream libsync.Transport\) \*SimNetwork](<#NewSimNetwork>) - [func \(n \*SimNetwork\) Heal\(id NodeID\)](<#SimNetwork.Heal>) - [func \(n \*SimNetwork\) HealAll\(\)](<#SimNetwork.HealAll>) - [func \(n \*SimNetwork\) Partition\(id NodeID\)](<#SimNetwork.Partition>) - [func \(n \*SimNetwork\) SetDropRate\(rate float64\)](<#SimNetwork.SetDropRate>) - [func \(n \*SimNetwork\) SetTruncateRate\(rate float64\)](<#SimNetwork.SetTruncateRate>) - [func \(n \*SimNetwork\) Transport\(nodeID NodeID\) \*SimTransport](<#SimNetwork.Transport>) - [type SimTransport](<#SimTransport>) - [func \(t \*SimTransport\) Exchange\(ctx context.Context, req \*xfer.Message\) \(\*xfer.Message, error\)](<#SimTransport.Exchange>) - [type Simulator](<#Simulator>) - [func New\(cfg SimConfig\) \(\*Simulator, error\)](<#New>) - [func \(s \*Simulator\) CheckAllConverged\(master \*repo.Repo\) error](<#Simulator.CheckAllConverged>) - [func \(s \*Simulator\) CheckAllTableSyncConverged\(\) error](<#Simulator.CheckAllTableSyncConverged>) - [func \(s \*Simulator\) CheckAllUVConverged\(master \*repo.Repo\) error](<#Simulator.CheckAllUVConverged>) - [func \(s \*Simulator\) CheckSafety\(\) error](<#Simulator.CheckSafety>) - [func \(s \*Simulator\) CheckTombstoneConvergence\(t \*testing.T, masterRepo \*repo.Repo, tableName string, def repo.TableDef\)](<#Simulator.CheckTombstoneConvergence>) - [func \(s \*Simulator\) Clock\(\) \*simio.SimClock](<#Simulator.Clock>) - [func \(s \*Simulator\) Close\(\) error](<#Simulator.Close>) - [func \(s \*Simulator\) Leaf\(id NodeID\) Node](<#Simulator.Leaf>) - [func \(s \*Simulator\) LeafIDs\(\) \[\]NodeID](<#Simulator.LeafIDs>) - [func \(s \*Simulator\) Network\(\) \*SimNetwork](<#Simulator.Network>) - [func \(s \*Simulator\) Run\(maxSteps int\) error](<#Simulator.Run>) - [func \(s \*Simulator\) RunUntil\(deadline time.Time\) error](<#Simulator.RunUntil>) - [func \(s \*Simulator\) ScheduleSyncNow\(id NodeID\)](<#Simulator.ScheduleSyncNow>) - [func \(s \*Simulator\) ScheduleUVDelete\(id NodeID, at time.Time, name string, mtime int64\)](<#Simulator.ScheduleUVDelete>) - [func \(s \*Simulator\) ScheduleUVWrite\(id NodeID, at time.Time, name string, data \[\]byte, mtime int64\)](<#Simulator.ScheduleUVWrite>) - [func \(s \*Simulator\) SetMasterRepo\(r \*repo.Repo\)](<#Simulator.SetMasterRepo>) - [func \(s \*Simulator\) Step\(\) \(bool, error\)](<#Simulator.Step>) <a name="AllBlobUUIDsSorted"></a> ## func [AllBlobUUIDsSorted](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L564>) ```go func AllBlobUUIDsSorted(r *repo.Repo) ([]string, error) ``` AllBlobUUIDsSorted returns a sorted slice of UUIDs for deterministic comparison. <a name="CheckBlobIntegrity"></a> ## func [CheckBlobIntegrity](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L33>) ```go func CheckBlobIntegrity(nodeID string, r *repo.Repo) error ``` CheckBlobIntegrity verifies that every blob in the repo has a UUID matching the hash of its expanded content. This catches corruption from buggify \(content.Expand byte\-flip\) or storage bugs. <a name="CheckConvergence"></a> ## func [CheckConvergence](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L102>) ```go func CheckConvergence(master *repo.Repo, leaves map[NodeID]*repo.Repo) error ``` CheckConvergence verifies that every leaf repo contains the same set of artifact UUIDs as the master repo. <a name="CheckDeltaChains"></a> ## func [CheckDeltaChains](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L52>) ```go func CheckDeltaChains(nodeID string, r *repo.Repo) error ``` CheckDeltaChains verifies that every delta's srcid points to an existing blob \(no dangling references\). <a name="CheckNoOrphanPhantoms"></a> ## func [CheckNoOrphanPhantoms](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L79>) ```go func CheckNoOrphanPhantoms(nodeID string, r *repo.Repo) error ``` CheckNoOrphanPhantoms verifies that phantom entries reference blobs that actually exist in the blob table \(they're just missing content\). <a name="CheckSubsetOf"></a> ## func [CheckSubsetOf](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L142>) ```go func CheckSubsetOf(master *repo.Repo, leaves map[NodeID]*repo.Repo) error ``` CheckSubsetOf verifies that all artifacts in the master are present in every leaf. Unlike CheckConvergence, this allows leaves to have extra artifacts \(useful when only pull is being tested\). <a name="CheckTableSyncConvergence"></a> ## func [CheckTableSyncConvergence](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L412>) ```go func CheckTableSyncConvergence(repos map[string]*repo.Repo) error ``` CheckTableSyncConvergence verifies that all repos have identical rows for every shared synced table. Tables present in some repos but not others are skipped \(schema may still be propagating\). <a name="CheckTableSyncIntegrity"></a> ## func [CheckTableSyncIntegrity](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L355>) ```go func CheckTableSyncIntegrity(nodeID string, r *repo.Repo) error ``` CheckTableSyncIntegrity verifies that: 1. Every row in every synced table has a valid PK hash. 2. Every row's mtime is positive. 3. The computed catalog hash is 40 hex chars. <a name="CheckTagxrefIntegrity"></a> ## func [CheckTagxrefIntegrity](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L296>) ```go func CheckTagxrefIntegrity(nodeID string, r *repo.Repo) error ``` CheckTagxrefIntegrity verifies that: 1. Every tagxref.rid references a valid blob 2. Every tagxref.tagid references a valid tag 3. Propagated entries \(srcid=0\) have tagtype=2 4. No tagxref.rid references a phantom blob <a name="CheckUVConvergence"></a> ## func [CheckUVConvergence](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L209>) ```go func CheckUVConvergence(master *repo.Repo, leaves map[NodeID]*repo.Repo) error ``` CheckUVConvergence verifies that all repos have identical UV content hashes. <a name="CheckUVIntegrity"></a> ## func [CheckUVIntegrity](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L171>) ```go func CheckUVIntegrity(nodeID string, r *repo.Repo) error ``` CheckUVIntegrity verifies that every non\-tombstone entry in the unversioned table has a hash matching the SHA1 of its decompressed content, and sz matches the content length. <a name="CountBlobs"></a> ## func [CountBlobs](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L578>) ```go func CountBlobs(r *repo.Repo) (int, error) ``` CountBlobs returns the number of non\-phantom blobs in the repo. <a name="HasBlob"></a> ## func [HasBlob](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L585>) ```go func HasBlob(r *repo.Repo, uuid string) bool ``` HasBlob checks if a specific artifact exists in the repo. <a name="Action"></a> ## type [Action](<https://github.com/danmestas/go-libfossil/blob/main/dst/node.go#L19-L23>) Action is the result of processing a single event via Tick. ```go type Action struct { Type ActionType Result *sync.SyncResult // non-nil when Type == ActionSynced Err error // non-nil on sync failure } ``` <a name="ActionType"></a> ## type [ActionType](<https://github.com/danmestas/go-libfossil/blob/main/dst/node.go#L11>) ActionType classifies the result of processing an event. ```go type ActionType int ``` <a name="ActionNone"></a> ```go const ( ActionNone ActionType = iota ActionSynced // a sync round was executed ) ``` <a name="DefaultNode"></a> ## type [DefaultNode](<https://github.com/danmestas/go-libfossil/blob/main/dst/node.go#L36-L43>) DefaultNode is a simple Node that calls sync.Sync directly. No NATS, no HTTP — just a repo \+ transport. ```go type DefaultNode struct { // contains filtered or unexported fields } ``` <a name="NewDefaultNode"></a> ### func [NewDefaultNode](<https://github.com/danmestas/go-libfossil/blob/main/dst/node.go#L56>) ```go func NewDefaultNode(r *repo.Repo, t sync.Transport, projectCode, serverCode string, opts DefaultNodeOpts) *DefaultNode ``` NewDefaultNode creates a Node backed by sync.Sync. <a name="DefaultNode.Repo"></a> ### func \(\*DefaultNode\) [Repo](<https://github.com/danmestas/go-libfossil/blob/main/dst/node.go#L91>) ```go func (n *DefaultNode) Repo() *repo.Repo ``` Repo returns the node's Fossil repository. <a name="DefaultNode.Tick"></a> ### func \(\*DefaultNode\) [Tick](<https://github.com/danmestas/go-libfossil/blob/main/dst/node.go#L73>) ```go func (n *DefaultNode) Tick(ctx context.Context, event EventType) Action ``` Tick executes a sync cycle for the given event. <a name="DefaultNodeOpts"></a> ## type [DefaultNodeOpts](<https://github.com/danmestas/go-libfossil/blob/main/dst/node.go#L46-L53>) DefaultNodeOpts configures a DefaultNode. ```go type DefaultNodeOpts struct { Push bool Pull bool UV bool XTableSync bool Private bool Buggify sync.BuggifyChecker } ``` <a name="Event"></a> ## type [Event](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L22-L29>) Event is a scheduled occurrence in the simulation. ```go type Event struct { Time time.Time Type EventType NodeID NodeID UVName string // for EvUVWrite/EvUVDelete: file name UVData []byte // for EvUVWrite: file content UVMTime int64 // for EvUVWrite/EvUVDelete: mtime } ``` <a name="EventQueue"></a> ## type [EventQueue](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L32>) EventQueue is a min\-heap of events ordered by time. ```go type EventQueue []*Event ``` <a name="EventQueue.Len"></a> ### func \(EventQueue\) [Len](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L34>) ```go func (q EventQueue) Len() int ``` <a name="EventQueue.Less"></a> ### func \(EventQueue\) [Less](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L35>) ```go func (q EventQueue) Less(i, j int) bool ``` <a name="EventQueue.Pop"></a> ### func \(\*EventQueue\) [Pop](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L38>) ```go func (q *EventQueue) Pop() any ``` <a name="EventQueue.PopEvent"></a> ### func \(\*EventQueue\) [PopEvent](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L53>) ```go func (q *EventQueue) PopEvent() *Event ``` PopEvent removes and returns the earliest event. <a name="EventQueue.Push"></a> ### func \(\*EventQueue\) [Push](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L37>) ```go func (q *EventQueue) Push(x any) ``` <a name="EventQueue.PushEvent"></a> ### func \(\*EventQueue\) [PushEvent](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L48>) ```go func (q *EventQueue) PushEvent(e *Event) ``` PushEvent adds an event to the queue. <a name="EventQueue.Swap"></a> ### func \(EventQueue\) [Swap](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L36>) ```go func (q EventQueue) Swap(i, j int) ``` <a name="EventType"></a> ## type [EventType](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L12>) EventType classifies simulation events. ```go type EventType int ``` <a name="EvTimer"></a> ```go const ( EvTimer EventType = iota // leaf poll timer fired EvSyncNow // manual sync trigger for a leaf EvUVWrite // write a UV file to a node's repo EvUVDelete // delete a UV file from a node's repo ) ``` <a name="InvariantError"></a> ## type [InvariantError](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L18-L22>) InvariantError records which invariant failed, on which node, and why. ```go type InvariantError struct { Invariant string NodeID string // "master" or leaf ID Detail string } ``` <a name="InvariantError.Error"></a> ### func \(\*InvariantError\) [Error](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L24>) ```go func (e *InvariantError) Error() string ``` <a name="MockFossil"></a> ## type [MockFossil](<https://github.com/danmestas/go-libfossil/blob/main/dst/mock_fossil.go#L16-L19>) MockFossil simulates a Fossil master server using the real HandleSync handler. It manages its own repo and implements sync.Transport by dispatching xfer messages to HandleSyncWithOpts. ```go type MockFossil struct { // contains filtered or unexported fields } ``` <a name="NewMockFossil"></a> ### func [NewMockFossil](<https://github.com/danmestas/go-libfossil/blob/main/dst/mock_fossil.go#L25>) ```go func NewMockFossil(r *repo.Repo) *MockFossil ``` NewMockFossil creates a MockFossil backed by the given repo. <a name="MockFossil.Exchange"></a> ### func \(\*MockFossil\) [Exchange](<https://github.com/danmestas/go-libfossil/blob/main/dst/mock_fossil.go#L45>) ```go func (f *MockFossil) Exchange(ctx context.Context, req *xfer.Message) (*xfer.Message, error) ``` Exchange handles one xfer request/response round by delegating to the real HandleSyncWithOpts. This ensures the DST tests exercise the same code path as production servers. <a name="MockFossil.Repo"></a> ### func \(\*MockFossil\) [Repo](<https://github.com/danmestas/go-libfossil/blob/main/dst/mock_fossil.go#L38>) ```go func (f *MockFossil) Repo() *repo.Repo ``` Repo returns the mock fossil's repository \(for seeding and invariants\). <a name="MockFossil.SetBuggify"></a> ### func \(\*MockFossil\) [SetBuggify](<https://github.com/danmestas/go-libfossil/blob/main/dst/mock_fossil.go#L33>) ```go func (f *MockFossil) SetBuggify(b libsync.BuggifyChecker) ``` SetBuggify configures fault injection for the handler. <a name="MockFossil.StoreArtifact"></a> ### func \(\*MockFossil\) [StoreArtifact](<https://github.com/danmestas/go-libfossil/blob/main/dst/mock_fossil.go#L56>) ```go func (f *MockFossil) StoreArtifact(data []byte) (string, error) ``` StoreArtifact adds a raw artifact to the mock fossil's repo. Returns the UUID. Used by tests to seed the master with content. <a name="Node"></a> ## type [Node](<https://github.com/danmestas/go-libfossil/blob/main/dst/node.go#L29-L32>) Node represents a sync participant in the simulation. The simulator drives events by calling Tick; the Node executes a sync cycle and reports the result. EdgeSync can implement this by wrapping its agent.Agent; the DST provides a DefaultNode for in\-module testing. ```go type Node interface { Tick(ctx context.Context, event EventType) Action Repo() *repo.Repo } ``` <a name="NodeID"></a> ## type [NodeID](<https://github.com/danmestas/go-libfossil/blob/main/dst/event.go#L9>) NodeID identifies a node in the simulation. ```go type NodeID string ``` <a name="PeerNetwork"></a> ## type [PeerNetwork](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L16-L22>) PeerNetwork simulates a peer\-to\-peer network where each leaf's Exchange calls HandleSync on the designated peer's repo. No bridge, no central server — pure leaf\-to\-leaf sync under DST control. ```go type PeerNetwork struct { // contains filtered or unexported fields } ``` <a name="NewPeerNetwork"></a> ### func [NewPeerNetwork](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L25>) ```go func NewPeerNetwork(rng *rand.Rand) *PeerNetwork ``` NewPeerNetwork creates a simulated peer network. <a name="PeerNetwork.AddPeer"></a> ### func \(\*PeerNetwork\) [AddPeer](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L37>) ```go func (n *PeerNetwork) AddPeer(id NodeID, r *repo.Repo) ``` AddPeer registers a leaf's repo as a sync target for other leaves. <a name="PeerNetwork.Heal"></a> ### func \(\*PeerNetwork\) [Heal](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L60>) ```go func (n *PeerNetwork) Heal(id NodeID) ``` Heal removes a node from the partition set. <a name="PeerNetwork.HealAll"></a> ### func \(\*PeerNetwork\) [HealAll](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L65>) ```go func (n *PeerNetwork) HealAll() ``` HealAll removes all partitions. <a name="PeerNetwork.Partition"></a> ### func \(\*PeerNetwork\) [Partition](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L55>) ```go func (n *PeerNetwork) Partition(id NodeID) ``` Partition isolates a node — all messages to/from it are dropped. <a name="PeerNetwork.SetBuggify"></a> ### func \(\*PeerNetwork\) [SetBuggify](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L50>) ```go func (n *PeerNetwork) SetBuggify(b libsync.BuggifyChecker) ``` SetBuggify configures fault injection for the handler. <a name="PeerNetwork.SetDropRate"></a> ### func \(\*PeerNetwork\) [SetDropRate](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L45>) ```go func (n *PeerNetwork) SetDropRate(rate float64) ``` SetDropRate sets the probability that any message is dropped. <a name="PeerNetwork.Transport"></a> ### func \(\*PeerNetwork\) [Transport](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L71>) ```go func (n *PeerNetwork) Transport(source, target NodeID) *PeerTransport ``` Transport returns a sync.Transport for the given source node that routes to a specific target peer's HandleSync. <a name="PeerTransport"></a> ## type [PeerTransport](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L103-L107>) PeerTransport implements sync.Transport by routing through the PeerNetwork to a specific target peer. ```go type PeerTransport struct { // contains filtered or unexported fields } ``` <a name="PeerTransport.Exchange"></a> ### func \(\*PeerTransport\) [Exchange](<https://github.com/danmestas/go-libfossil/blob/main/dst/peer_network.go#L110>) ```go func (t *PeerTransport) Exchange(ctx context.Context, req *xfer.Message) (*xfer.Message, error) ``` Exchange sends a request through the peer network to the target's HandleSync. <a name="SeededBuggify"></a> ## type [SeededBuggify](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L18-L20>) SeededBuggify implements sync.BuggifyChecker with a deterministic PRNG. ```go type SeededBuggify struct { // contains filtered or unexported fields } ``` <a name="SeededBuggify.Check"></a> ### func \(\*SeededBuggify\) [Check](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L23>) ```go func (b *SeededBuggify) Check(_ string, probability float64) bool ``` Check returns true with the given probability, using the seeded PRNG. <a name="SimConfig"></a> ## type [SimConfig](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L54-L64>) SimConfig configures a simulation run. ```go type SimConfig struct { Seed int64 NumLeaves int PollInterval time.Duration TmpDir string // directory for repo files Upstream libsync.Transport // mock Fossil master Buggify bool // enable BUGGIFY fault injection UV bool // sync unversioned files Private bool // sync private artifacts SafetyCheckInterval int // run CheckSafety() every N steps; 0 = disabled } ``` <a name="SimNetwork"></a> ## type [SimNetwork](<https://github.com/danmestas/go-libfossil/blob/main/dst/network.go#L20-L26>) SimNetwork simulates the network between leaf agents and the upstream transport \(mock Fossil master\). It supports message dropping, response truncation, and network partitions, controlled by a seeded PRNG for deterministic behavior. In the original EdgeSync DST, messages routed through a Bridge that forwarded to the upstream. Since Bridge.HandleRequest was just upstream.Exchange, SimNetwork now calls the upstream directly. ```go type SimNetwork struct { // contains filtered or unexported fields } ``` <a name="NewSimNetwork"></a> ### func [NewSimNetwork](<https://github.com/danmestas/go-libfossil/blob/main/dst/network.go#L29>) ```go func NewSimNetwork(rng *rand.Rand, upstream libsync.Transport) *SimNetwork ``` NewSimNetwork creates a simulated network connected to the given upstream transport. <a name="SimNetwork.Heal"></a> ### func \(\*SimNetwork\) [Heal](<https://github.com/danmestas/go-libfossil/blob/main/dst/network.go#L54>) ```go func (n *SimNetwork) Heal(id NodeID) ``` Heal removes a node from the partition set. <a name="SimNetwork.HealAll"></a> ### func \(\*SimNetwork\) [HealAll](<https://github.com/danmestas/go-libfossil/blob/main/dst/network.go#L59>) ```go func (n *SimNetwork) HealAll() ``` HealAll removes all partitions. <a name="SimNetwork.Partition"></a> ### func \(\*SimNetwork\) [Partition](<https://github.com/danmestas/go-libfossil/blob/main/dst/network.go#L49>) ```go func (n *SimNetwork) Partition(id NodeID) ``` Partition isolates a node — all messages to/from it are dropped. <a name="SimNetwork.SetDropRate"></a> ### func \(\*SimNetwork\) [SetDropRate](<https://github.com/danmestas/go-libfossil/blob/main/dst/network.go#L38>) ```go func (n *SimNetwork) SetDropRate(rate float64) ``` SetDropRate sets the probability that any message is dropped entirely. <a name="SimNetwork.SetTruncateRate"></a> ### func \(\*SimNetwork\) [SetTruncateRate](<https://github.com/danmestas/go-libfossil/blob/main/dst/network.go#L44>) ```go func (n *SimNetwork) SetTruncateRate(rate float64) ``` SetTruncateRate sets the probability that a response is truncated \(random suffix of cards dropped\). Simulates partial delivery. <a name="SimNetwork.Transport"></a> ### func \(\*SimNetwork\) [Transport](<https://github.com/danmestas/go-libfossil/blob/main/dst/network.go#L65>) ```go func (n *SimNetwork) Transport(nodeID NodeID) *SimTransport ``` Transport returns a sync.Transport for the given node that routes messages through this simulated network to the upstream. <a name="SimTransport"></a> ## type [SimTransport](<https://github.com/danmestas/go-libfossil/blob/main/dst/network.go#L90-L93>) SimTransport implements sync.Transport by routing through the SimNetwork. ```go type SimTransport struct { // contains filtered or unexported fields } ``` <a name="SimTransport.Exchange"></a> ### func \(\*SimTransport\) [Exchange](<https://github.com/danmestas/go-libfossil/blob/main/dst/network.go#L96>) ```go func (t *SimTransport) Exchange(ctx context.Context, req *xfer.Message) (*xfer.Message, error) ``` Exchange sends a request through the simulated network to the upstream. <a name="Simulator"></a> ## type [Simulator](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L29-L51>) Simulator drives a deterministic simulation of multiple leaf nodes syncing through a simulated network to an upstream transport \(mock Fossil master\). ```go type Simulator struct { Seed int64 // Stats Steps int TotalSyncs int TotalErrors int TotalUVSent int TotalUVRecvd int TotalUVGimmes int // contains filtered or unexported fields } ``` <a name="New"></a> ### func [New](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L69>) ```go func New(cfg SimConfig) (*Simulator, error) ``` New creates a Simulator with the given configuration. It creates leaf repos, nodes, and the simulated network. All I/O is local SQLite — no NATS or HTTP connections are made. <a name="Simulator.CheckAllConverged"></a> ### func \(\*Simulator\) [CheckAllConverged](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L260>) ```go func (s *Simulator) CheckAllConverged(master *repo.Repo) error ``` CheckAllConverged checks convergence between master and all leaves. Only meaningful after a fault\-free sync period. <a name="Simulator.CheckAllTableSyncConverged"></a> ### func \(\*Simulator\) [CheckAllTableSyncConverged](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L269>) ```go func (s *Simulator) CheckAllTableSyncConverged() error ``` CheckAllTableSyncConverged checks table sync convergence across all nodes. <a name="Simulator.CheckAllUVConverged"></a> ### func \(\*Simulator\) [CheckAllUVConverged](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L281>) ```go func (s *Simulator) CheckAllUVConverged(master *repo.Repo) error ``` CheckAllUVConverged checks UV convergence between master and all leaves. <a name="Simulator.CheckSafety"></a> ### func \(\*Simulator\) [CheckSafety](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L236>) ```go func (s *Simulator) CheckSafety() error ``` CheckSafety runs all safety invariants on every node in the simulation. <a name="Simulator.CheckTombstoneConvergence"></a> ### func \(\*Simulator\) [CheckTombstoneConvergence](<https://github.com/danmestas/go-libfossil/blob/main/dst/invariants.go#L468>) ```go func (s *Simulator) CheckTombstoneConvergence(t *testing.T, masterRepo *repo.Repo, tableName string, def repo.TableDef) ``` CheckTombstoneConvergence verifies that all nodes agree on which rows are tombstones for each synced table. Two nodes may have different catalog hashes during convergence, but once converged, their tombstone sets must match. <a name="Simulator.Clock"></a> ### func \(\*Simulator\) [Clock](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L148>) ```go func (s *Simulator) Clock() *simio.SimClock ``` Clock returns the simulator's virtual clock. <a name="Simulator.Close"></a> ### func \(\*Simulator\) [Close](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L335>) ```go func (s *Simulator) Close() error ``` Close cleans up all leaf node repos and disables buggify. Iterates leafIDs \(not map\) for deterministic error reporting. <a name="Simulator.Leaf"></a> ### func \(\*Simulator\) [Leaf](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L158>) ```go func (s *Simulator) Leaf(id NodeID) Node ``` Leaf returns the node for the given node ID. <a name="Simulator.LeafIDs"></a> ### func \(\*Simulator\) [LeafIDs](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L163>) ```go func (s *Simulator) LeafIDs() []NodeID ``` LeafIDs returns the ordered list of leaf node IDs. <a name="Simulator.Network"></a> ### func \(\*Simulator\) [Network](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L153>) ```go func (s *Simulator) Network() *SimNetwork ``` Network returns the simulated network for fault injection. <a name="Simulator.Run"></a> ### func \(\*Simulator\) [Run](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L303>) ```go func (s *Simulator) Run(maxSteps int) error ``` Run processes up to maxSteps events. Returns nil on success or the first invariant/error encountered. <a name="Simulator.RunUntil"></a> ### func \(\*Simulator\) [RunUntil](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L317>) ```go func (s *Simulator) RunUntil(deadline time.Time) error ``` RunUntil processes events until the clock reaches the given time. <a name="Simulator.ScheduleSyncNow"></a> ### func \(\*Simulator\) [ScheduleSyncNow](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L173>) ```go func (s *Simulator) ScheduleSyncNow(id NodeID) ``` ScheduleSyncNow injects a SyncNow event for the given leaf at the current time. <a name="Simulator.ScheduleUVDelete"></a> ### func \(\*Simulator\) [ScheduleUVDelete](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L194>) ```go func (s *Simulator) ScheduleUVDelete(id NodeID, at time.Time, name string, mtime int64) ``` ScheduleUVDelete injects a UV delete event for the given node at the specified time. <a name="Simulator.ScheduleUVWrite"></a> ### func \(\*Simulator\) [ScheduleUVWrite](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L182>) ```go func (s *Simulator) ScheduleUVWrite(id NodeID, at time.Time, name string, data []byte, mtime int64) ``` ScheduleUVWrite injects a UV write event for the given node at the specified time. <a name="Simulator.SetMasterRepo"></a> ### func \(\*Simulator\) [SetMasterRepo](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L168>) ```go func (s *Simulator) SetMasterRepo(r *repo.Repo) ``` SetMasterRepo registers the master repo for UV events targeting "master". <a name="Simulator.Step"></a> ### func \(\*Simulator\) [Step](<https://github.com/danmestas/go-libfossil/blob/main/dst/simulator.go#L205>) ```go func (s *Simulator) Step() (bool, error) ``` Step processes the next event in the queue. Returns false if the queue is empty. Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)