Skip to content
EdgeSync Memory
Project Structure
- Go workspace (
go.work) with 5 modules: root, libfossil/, leaf/, bridge/, dst/
sim/ is a package in the root module (not its own module)
- Dual VCS: git + fossil checkout — use
-buildvcs=false for all builds
- Git repo: github.com/danmestas/EdgeSync (private)
fossil/ and libfossil/ are gitignored (local-only reference, purged from history)
.agents/, .claude/skills/, skills-lock.json are gitignored (tool-local)
Entry Points
cmd/edgesync/ — Unified CLI (50 subcommands via kong, including repo clone)
leaf/cmd/leaf/ — Standalone leaf agent daemon
bridge/cmd/bridge/ — Standalone bridge daemon
sim/cmd/soak/ — Continuous soak test runner
Server Handler (2026-03-18, PR#8)
- HandleSync in
sync/handler.go — server-side xfer card handler. Stateless per-round.
- HandleSyncWithOpts — same but accepts
HandleOpts{Buggify} for DST fault injection
- HandleFunc type —
func(ctx, *repo.Repo, *xfer.Message) (*xfer.Message, error)
- Cards handled: pull→igot, push→accept files, igot→gimme, gimme→file, file→store, clone→paginated files, clone_seqno, reqconfig→config
- ServeHTTP in
sync/serve_http.go — HTTP /xfer listener. fossil clone/fossil sync compatible.
- ServeNATS in
leaf/agent/serve_nats.go — NATS request/reply listener
- ServeP2P in
leaf/agent/serve_p2p.go — stub for libp2p
- Agent config:
ServeHTTPAddr string, ServeNATSEnabled bool
- Agent.Start() launches pollLoop + server listeners concurrently
- 4 BUGGIFY sites: handleGimme.skip(5%), handleFile.reject(3%), emitIGots.truncate(10%), emitCloneBatch.smallBatch(10%)
- TigerStyle: preconditions on all handler methods, sql.ErrNoRows distinction, MaxBytesReader(50MB), decompressBounded(50MB)
- xfer.Decode: tries raw zlib → 4-byte prefix+zlib → uncompressed (3-way fallback)
- parsePush/parsePull: accept 1-arg form (Fossil omits project-code on known remotes)
- Spec:
docs/dev/specs/2026-03-18-server-handler-design.md
- Plan:
docs/dev/plans/2026-03-18-server-handler.md
- Obsidian note:
~/notes/Projects/EdgeSync Server Handler.md
DST Updates (2026-03-18)
- MockFossil replaced with HandleSync —
MockFossil.Exchange delegates to HandleSyncWithOpts. All 37 existing DST tests pass.
- PeerNetwork in
dst/peer_network.go — leaf-to-leaf DST without bridge/master. Routes Exchange→HandleSync on peer repo.
- TestScenarioPeerSync — 2 leaves, 10 blobs converge via PeerNetwork
- TestScenarioClone — fresh repo clones 250 blobs through HandleSync pagination
- DST total: 39 tests (37 existing + 2 new)
Two Simulation Layers
dst/ — Deterministic single-threaded sim (NewFromParts, SimNetwork/PeerNetwork, HandleSync-backed MockFossil, event queue)
sim/ — Integration sim (real NATS + TCP fault proxy + real Fossil server + ServeHTTP/ServeNATS tests)
- Both share
simio/ abstractions (Clock, Rand, Env) and sync.BuggifyChecker interface
- sim serve tests: fossil clone, fossil sync, leaf-to-leaf NATS, leaf-to-leaf HTTP — all verified by Fossil 2.28
Clone Implementation (2026-03-18, PR#7 merged)
sync.Clone() — full repo clone via Fossil protocol v3
storeReceivedFile() shared between Sync and Clone
ErrDeltaSourceMissing sentinel: clone creates phantoms, sync returns error
- CLI:
edgesync repo clone <url> <path>
Config Locations
- agent.Config in
leaf/agent/config.go — includes ServeHTTPAddr, ServeNATSEnabled
- bridge.Config in
bridge/bridge/config.go
- SyncOpts in
libfossil/sync/session.go — has BuggifyChecker field
- HandleOpts in
libfossil/sync/handler.go — has BuggifyChecker field
Key Patterns
simio.CryptoRand{} for production callsites of repo.Create and db.SeedConfig
NewFromParts() on agent/bridge bypasses NATS for testing; New() is production path
HandleSync for simple use, HandleSyncWithOpts when BUGGIFY is needed (DST)
CI & Testing
.github/workflows/test.yml — unit tests + sim serve tests
.githooks/pre-commit — ~8s (unit + DST 1 seed + sim unit + sim serve)
- Makefile:
make test (CI), make dst/dst-full/dst-hostile/dst-drivers, make sim/sim-full, make drivers
- DST: 8 seeds normal pass, 16 seeds x hostile ~40s
backup/pre-filter branch on remote — old history before fossil file purge
Follow-ons (documented, not implemented)
- UV cards (uvfile/uvigot/uvgimme) for forum/wiki/chat
- Cookie sessions for igot optimization
- In-memory TTL sessions for libp2p connection identity
- Private branch cards
- Full config sync (tickets, skins, backoffice)
- Login verification beyond accept-all
- libp2p transport (ServeP2P stub exists)
- DST fault schedule for peer-to-peer topology
- Clone under DST event model