Skip to content
edgesync Timeline
Login

Timeline

29 descendants of 7f0fbfa6efc978a4

2026-10-16
18:14
build(deps): bump github.com/danmestas/go-libfossil in /

Bumps [github.com/danmestas/go-libfossil](https://github.com/danmestas/go-libfossil) in `/` from 0.11.1 to 0.12.0.

Updates `github.com/danmestas/go-libfossil` from 0.11.1 to 0.12.0 - [Release notes](https://github.com/danmestas/go-libfossil/releases) - [Changelog](https://github.com/danmestas/go-libfossil/blob/main/CHANGELOG.md) - [Commits](https://github.com/danmestas/go-libfossil/compare/v0.11.1...v0.12.0)

Updates `github.com/danmestas/go-libfossil` from 0.11.1 to 0.12.0 - [Release notes](https://github.com/danmestas/go-libfossil/releases) - [Changelog](https://github.com/danmestas/go-libfossil/blob/main/CHANGELOG.md) - [Commits](https://github.com/danmestas/go-libfossil/compare/v0.11.1...v0.12.0)

Updates `github.com/danmestas/go-libfossil` from 0.11.1 to 0.12.0 - [Release notes](https://github.com/danmestas/go-libfossil/releases) - [Changelog](https://github.com/danmestas/go-libfossil/blob/main/CHANGELOG.md) - [Commits](https://github.com/danmestas/go-libfossil/compare/v0.11.1...v0.12.0)

--- updated-dependencies: - dependency-name: github.com/danmestas/go-libfossil dependency-version: 0.12.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go_modules - dependency-name: github.com/danmestas/go-libfossil dependency-version: 0.12.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go_modules - dependency-name: github.com/danmestas/go-libfossil dependency-version: 0.12.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go_modules ...

Signed-off-by: dependabot[bot] <[email protected]> leaf check-in: ba948a480d user: [email protected] tags: trunk, main, dependabot/go_modules/go_modules-cd07540e7f

2026-10-11
13:52
build(deps): bump github.com/danmestas/go-libfossil to v0.11.1 (#203)

v0.11.1 fixes GHSA-mvx3-6793-8x4r (the sync server sent content to requesters without read permission) and GHSA-3f8x-8fr5-mr98 (the HTTP client followed redirects from HTTPS to HTTP).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> leaf check-in: 0a4c779ae2 user: [email protected] tags: trunk, main

01:50
build(deps): bump the cargo group across 1 directory with 2 updates

Bumps the cargo group with 2 updates in the /iroh-sidecar directory: [hickory-resolver](https://github.com/hickory-dns/hickory-dns) and [rustls](https://github.com/rustls/rustls).

Updates `hickory-resolver` from 0.26.1 to 0.26.3 - [Release notes](https://github.com/hickory-dns/hickory-dns/releases) - [Changelog](https://github.com/hickory-dns/hickory-dns/blob/main/CHANGELOG.md) - [Commits](https://github.com/hickory-dns/hickory-dns/compare/v0.26.1...v0.26.3)

Updates `rustls` from 0.23.43 to 0.23.45 - [Release notes](https://github.com/rustls/rustls/releases) - [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md) - [Commits](https://github.com/rustls/rustls/compare/v/0.23.43...v/0.23.45)

--- updated-dependencies: - dependency-name: hickory-resolver dependency-version: 0.26.3 dependency-type: indirect dependency-group: cargo - dependency-name: rustls dependency-version: 0.23.45 dependency-type: indirect dependency-group: cargo ...

Signed-off-by: dependabot[bot] <[email protected]> leaf check-in: 0f53f1d32b user: [email protected] tags: trunk, dependabot/cargo/iroh-sidecar/cargo-60501f734e

01:48
fix(leaf): grant unauthenticated peers capabilities explicitly; go-libfossil v0.11.0 (#201)

go-libfossil v0.11.0 gives a new repository's `nobody` user fossil's default capabilities, clone and pull (`gjorz`), instead of push and Setup (GHSA-44p2-whvx-9gwq). Leaves that accept pushes from peers syncing without credentials, over HTTP, NATS or iroh, now have to grant that explicitly.

- agent.Config.NobodyCaps, applied by agent.New, sets the leaf repo's nobody capabilities. Empty keeps what the repo stores. - The leaf binary takes it as -nobody-caps / LEAF_NOBODY_CAPS. For example, `gio` lets unauthenticated peers push to this leaf. - The leaf-to-leaf iroh tests grant `gio`, since their peers push without credentials.

The bump also brings v0.11.0's other security fixes: the login nonce is checked against the request body, reqconfig is scoped, payload sizes and table-sync rows are validated, and HTTP replies are capped.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: dbb0c04321 user: [email protected] tags: trunk, main

2026-10-03
19:00
build(deps): bump github.com/danmestas/go-libfossil to v0.10.0 (#200)

Bumps go-libfossil from v0.9.0 to v0.10.0 in the root, bridge and leaf modules. The release fixes three data-loss bugs in the checkout API (Revert deleting never-committed files, Rename overwriting an existing file, Extract overwriting local edits on checkouts made by the fossil binary) and aligns checkout behavior with fossil. EdgeSync calls none of the affected functions except Extract via CreateCheckout, whose tests pass unchanged.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 087d06ceea user: [email protected] tags: trunk, main

19:00
ci: track go-libfossil with Dependabot instead of a release dispatch (#199)

bump-upstream.yml waited for an upstream-bump event from go-libfossil's release workflow. That path has been dead since the rename: go-libfossil sends repo=go-libfossil and module github.com/danmestas/go-libfossil, while this workflow only accepted libfossil and github.com/danmestas/libfossil and ignored anything else. The sending side has also failed with 401 since v0.8.1, because its token expired.

Dependabot now watches github.com/danmestas/go-libfossil and its driver modules in the root, bridge and leaf go.mod files, daily, and opens one pull request per module path across all three. No token is needed, and a broken update shows up in Dependabot's own status rather than as a dispatch nobody sees fail. Security updates for other dependencies are unaffected.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: a8beeb09dc user: [email protected] tags: trunk, main

2026-09-13
19:56
build(deps): bump google.golang.org/grpc (#198) check-in: f5c71d9d03 user: [email protected] tags: trunk, main
2026-08-12
21:15
chore(test): prune stale sim test regexes and stub files (#197)

The v0.2.0 handle-API migration gutted 14 sim test files to 4-line stubs; the Makefile/CI regexes kept matching zero tests and passed vacuously. DST and seed-sweep suites live in go-libfossil now.

- make test / CI sim job run the tests that actually exist - drop dead sim, sim-full, test-interop targets and ghost dst PHONYs - delete the stub files (originals in git history)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: e1882f5021 user: [email protected] tags: trunk, main

20:58
chore: remove agent trace files, gitignore .agent-config (#196)

* chore: remove accidentally committed agent trace files, gitignore .agent-config

* ci: retrigger checks

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: a213a4a665 user: [email protected] tags: trunk, main

20:50
chore(deps): migrate to go-libfossil v0.9.0 (#195)

* chore(deps): migrate to go-libfossil v0.9.0 (renamed module path)

- import path danmestas/libfossil → danmestas/go-libfossil everywhere - CloneOpts.ProjectCode removed upstream; assert via CloneResult instead - Timeline now takes TimelineOpts (cursor pagination, newest-first) - testutil.HasFossil removed; check FossilBinary() != ""

* ci: retrigger checks

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 38efed7a12 user: [email protected] tags: trunk, main

20:35
chore(deps): update Go, Hugo, and cargo dependencies (#194)

* chore(deps): update Go, Hugo, and cargo dependencies

- nats.go 1.49→1.53.1, nats-server 2.12.6→2.14.5, otel 1.43→1.45, modernc sqlite 1.46→1.56, libfossil 0.6.2→0.6.3, hextra 0.12.2→0.12.3 - cargo update for iroh-sidecar; ed25519/pkcs8/signature pinned to the rc series required by iroh's ed25519-dalek 3.0.0-pre.1 - fix traceparent test: nats.go ≥1.50 preserves header case and Get is exact-match; assert via natshdr.Carrier - rustfmt pass on sidecar sources

* chore(deps): bump iroh 0.97→1.0.3, fixes hickory-proto security alerts

SecretKey::generate no longer takes an rng; drop now-unused rand dep. Verified with make test-iroh (peer sync + convergence e2e).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: eb4b6f52d0 user: [email protected] tags: trunk, main

20:07
build(deps): bump quinn-proto (#193)

Bumps the cargo group with 1 update in the /iroh-sidecar directory: [quinn-proto](https://github.com/quinn-rs/quinn).

Updates `quinn-proto` from 0.11.14 to 0.11.16 - [Release notes](https://github.com/quinn-rs/quinn/releases) - [Commits](https://github.com/quinn-rs/quinn/compare/quinn-proto-0.11.14...quinn-proto-0.11.16)

--- updated-dependencies: - dependency-name: quinn-proto dependency-version: 0.11.16 dependency-type: indirect dependency-group: cargo ...

Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> check-in: 4d402f6972 user: [email protected] tags: trunk, main

20:07
build(deps): bump the go_modules group across 1 directory with 2 updates (#192)

Bumps the go_modules group with 2 updates in the /leaf directory: [golang.org/x/net](https://github.com/golang/net) and [google.golang.org/grpc](https://github.com/grpc/grpc-go).

Updates `golang.org/x/net` from 0.54.0 to 0.55.0 - [Commits](https://github.com/golang/net/compare/v0.54.0...v0.55.0)

Updates `google.golang.org/grpc` from 1.80.0 to 1.82.1 - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.80.0...v1.82.1)

--- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.55.0 dependency-type: indirect dependency-group: go_modules - dependency-name: google.golang.org/grpc dependency-version: 1.82.1 dependency-type: indirect dependency-group: go_modules ...

Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> check-in: 51f9b5e893 user: [email protected] tags: trunk, main

2026-07-22
14:08
build(deps): bump the go_modules group across 3 directories with 2 updates (#191)

Bumps the go_modules group with 1 update in the / directory: [golang.org/x/crypto](https://github.com/golang/crypto). Bumps the go_modules group with 1 update in the /bridge directory: [golang.org/x/crypto](https://github.com/golang/crypto). Bumps the go_modules group with 1 update in the /leaf directory: [golang.org/x/crypto](https://github.com/golang/crypto).

Updates `golang.org/x/crypto` from 0.49.0 to 0.52.0 - [Commits](https://github.com/golang/crypto/compare/v0.49.0...v0.52.0)

Updates `golang.org/x/crypto` from 0.49.0 to 0.52.0 - [Commits](https://github.com/golang/crypto/compare/v0.49.0...v0.52.0)

Updates `golang.org/x/crypto` from 0.49.0 to 0.52.0 - [Commits](https://github.com/golang/crypto/compare/v0.49.0...v0.52.0)

Updates `golang.org/x/net` from 0.52.0 to 0.54.0 - [Commits](https://github.com/golang/net/compare/v0.52.0...v0.54.0)

--- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.52.0 dependency-type: indirect dependency-group: go_modules - dependency-name: golang.org/x/crypto dependency-version: 0.52.0 dependency-type: indirect dependency-group: go_modules - dependency-name: golang.org/x/crypto dependency-version: 0.52.0 dependency-type: indirect dependency-group: go_modules - dependency-name: golang.org/x/net dependency-version: 0.54.0 dependency-type: indirect dependency-group: go_modules ...

Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> check-in: eac9836302 user: [email protected] tags: trunk, main

2026-05-19
18:59
fix(hub): single-owner httpServer construction in NewHub (closes 187) (#188)

TigerStyle: eliminate the shared mutable state instead of guarding it.

Move mux + http.Server construction from ServeHTTP into NewHub. After NewHub returns, h.httpServer and h.httpListener are immutable for the hubs lifetime — single writer (NewHub), many readers (ServeHTTP, Stop). No mutex, no atomic, no sync.Once required: the race is gone by construction.

Add a hub package-private assert helper and positive invariants at entry to ServeHTTP and Stop, surfacing programmer-bugs loudly instead of silently UB-ing on a nil httpServer.

Fixes the 3 data races in TestCrossSessionAutosync and the 3 in TestScope_Project_ConcurrentCommits under -race; hub package suite green at -race -count=3.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 3b6306fdc2 user: [email protected] tags: trunk, main

18:54
fix(hub): single-owner httpServer construction in NewHub (closes 187)

TigerStyle: eliminate the shared mutable state instead of guarding it.

Move mux + http.Server construction from ServeHTTP into NewHub. After NewHub returns, h.httpServer and h.httpListener are immutable for the hubs lifetime — single writer (NewHub), many readers (ServeHTTP, Stop). No mutex, no atomic, no sync.Once required: the race is gone by construction.

Add a hub package-private assert helper and positive invariants at entry to ServeHTTP and Stop, surfacing programmer-bugs loudly instead of silently UB-ing on a nil httpServer.

Fixes the 3 data races in TestCrossSessionAutosync and the 3 in TestScope_Project_ConcurrentCommits under -race; hub package suite green at -race -count=3. leaf check-in: 8a18ec9ebe user: 41898282+github-actions[bot]@users.noreply.github.com tags: trunk, fix/hub-stop-servehttp-race-tigerstyle

17:09
fix(hub): resolve TestNotifyCLIPairAndDevices flake (closes #170) (#186)

* fix(hub): resolve TestNotifyCLIPairAndDevices flake (closes #170)

Each of the 4 tests in cmd/edgesync/notify_test.go independently invoked `go build` for the edgesync binary inside its own body. Under cold build cache or parallel test load, this build storm pushed the package's total runtime past `go test -short -timeout=30s ./...` (the pre-push hook gate) and the suite-level timeout fired — surfacing as a hang on os/exec.Wait since the inner `go build` child was still running when the supervisor killed the test process.

Move the build into TestMain so the binary is built once and reused. With -timeout=30s on cold cache, cmd/edgesync drops from ~9s to ~3s and the target test from ~2.8s to ~0.07s — comfortably clear of the timeout.

No production-code change. No sleep band-aids. Verified 200/200 passes (50× per test in the package) and 80/80 under -race.

* fix(test): clean up temp dir before os.Exit (Ousterhout follow-up to PR 186)

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 37780572c3 user: [email protected] tags: trunk, main

16:25
feat(hub): Hub.EnsureUser — idempotent user-table registration (closes #183) (#184)

Add Hub.EnsureUser(login, caps string) error as an idempotent counterpart to Hub.AddUser. Consumers (sesh, others) need "register if absent" semantics; AddUser errors on duplicate, forcing each consumer to write check-then-act + race-recheck wrappers. The user-table layer is the right place to own that idempotency.

Implementation choice (a) — GetUser → CreateUser, serialized by a Repo- level mutex. Picked over (b) "catch CreateUser's already-exists error" because libfossil's CreateUser surfaces a wrapped SQLite UNIQUE-constraint error with no sentinel or typed shape — string-matching would be brittle. A defensive GetUser retry after CreateUser failure absorbs the rare cross- process race where the in-process mutex doesn't apply.

Contract: existing caps are PRESERVED on duplicate (documented). Callers wanting replace-on-conflict use SetUserCaps explicitly.

Tests cover first-time registration, duplicate no-op, caps-change-on- duplicate (preservation), 5-goroutine concurrent race (exactly one row lands), and empty-login rejection. Full hub package + leaf + bridge + CLI suites pass; go vet clean; gofmt clean.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 40c9b9aff3 user: [email protected] tags: trunk, main

2026-05-17
18:47
fix(hub): address pulls to publishing peer to resolve .sync race (#162) (#181)

Three+ hubs sharing a project-code all subscribed to the same broadcast `<prefix>..sync` subject. A `.commit` notification on any one of them caused every other peer to fan out a nats.Request on .sync; every subscribed peer answered, but nats.Request only delivers the first reply. If a stale peer won that race the pull converged against zero blobs and the receiver silently stayed behind.

Two changes close the gap:

1. Each hub now also subscribes to a per-peer addressed sync subject `<prefix>..sync.<peer-id>` (peer-id = the hub's NATS server name). The commit notification carries the publishing hub's peer-id; receivers route their pull-back at that addressed subject so exactly that hub responds. No race regardless of how many other hubs share the project-code.
2. The broadcast `.sync` subscription becomes a NATS queue group (`sync-<project-code>`) so external leaf-agent pushes — which still target the broadcast subject — get exactly one responder per request instead of a fan-out race. Project-code in the queue name scopes the group so unrelated projects sharing a NATS server can't cross-pollinate.

The receiver also drops self-notifications (a publish from this hub round-tripping back via an upstream leaf, which NoEcho doesn't catch across leaf links) and notifications missing a peer-id (older publisher that pre-dates this fix — refusing rather than falling back to a broadcast pull keeps #162 from regressing silently).

`TestCrossLeaf_ThreeHubs_CommitAnnouncePropagatesToAll` is now ungated: it ran red under both -short and full modes before this fix, and passes under both modes (and 10x stability check) after.

Closes #162 Closes #179

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: a96fe486ab user: [email protected] tags: trunk, main

17:44
test(hub): add 3-hub cross-leaf propagation test (#180)

Mirrors the 2-node TestCrossLeaf_SharedProjectCode_PropagatesCommit shape but with a third leaf attached to the same topology root. Asserts that a commit on any peer propagates to BOTH of the other two peers, both directions.

Reproduces the cross-leaf propagation gap reported in #179 and confirms its underlying cause is the multi-responder .sync race filed as #162: with 3+ hubs subscribing to the same .sync subject every peer answers nats.Request, only the first reply wins, and a stale responder silently starves the pull. The first hop (B -> A) is already enough to surface this — A's .sync request can race against C's empty responder even though A and B are the only nodes that need to talk for that direction.

Gated under -short with a skip pointing to #162 so pre-commit and CI stay green. Without -short the test fails at the 10s wait in ~10s, demonstrating the bug. Remove the skip once #162 is fixed; the test then doubles as the regression sentinel for the 3+ hub topology that production sesh relies on.

No production code changes.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 5c36d5498d user: [email protected] tags: trunk, main

2026-05-16
18:01
chore: bump submodule pins (leaf/v0.0.12,bridge/v0.0.4) leaf check-in: 5070f96573 user: 41898282+github-actions[bot]@users.noreply.github.com tags: trunk, v0.0.20
17:55
feat(hub): expose embedded NATS WebSocket endpoint (#177)

Adds Config.EnableWebSocket (opt-in; default off for security since no TLS/WSS is wired up here) plus Config.NATSWebSocketPort (0 = auto-pick, mirroring the sibling NATSClientPort / NATSLeafPort convention). When enabled, the embedded nats-server binds a WebSocket listener on 127.0.0.1:NNNN with NoTLS=true.

Surfaces the resolved listener URL via the new Hub.NATSWebSocketURL() getter, which returns "ws://127.0.0.1:NNNN" when WebSocket is enabled and "" otherwise — same shape as NATSURL() / LeafURL().

Tests cover the disabled-by-default contract, the auto-pick URL shape, and an end-to-end round trip: a nats.go client dials the ws:// URL, subscribes to a subject, and receives a payload published by a TCP client on the same embedded server.

Fixes #176

Co-authored-by: Dan Mestas <[email protected]> check-in: ae52558ba4 user: [email protected] tags: trunk, leaf/v0.0.12, bridge/v0.0.4

2026-05-14
16:08
feat(agent): implement AutosyncCommit on libfossil v0.6.2 Checkout API (#173) (#174)

The v0.2.0 deferral premise — "Checkout type is internal to libfossil" — is stale: v0.6.2 (current dep) exposes Checkout as a public handle with Version, WouldFork, Checkin, HasChanges, plus Repo.OpenCheckout/CreateCheckout.

This wires up the long-deferred autosync workflow on that public API: pull -> fork check -> checkin -> push, with AutosyncOff/On/PullOnly modes. Errors are typed: ErrWouldFork on detected fork (without creating a checkin), and *PushFailedError when the local checkin succeeds but the follow-up push fails — the latter carries the local rid/uuid so callers can decide whether to retry, abandon, or surface the partial state.

Sync.Push and Sync.Pull on the input opts are now rejected (loud failure beats silent override) since AutosyncCommit drives them per-mode.

ErrCkinLockHeld is removed — it was an exported sentinel that no code path ever returned, so it would have invited misleading errors.Is checks downstream. When libfossil exposes a real lock-held signal, the sentinel can come back with a return path.

The four previously-skipped tests are restored as real coverage. The fork-test fixture clones a "remote" repo from local at C0 via httptest (HandleSync doesn't speak the clone protocol — CDG-148), then commits C1 on the remote so the pull genuinely brings it back to local and triggers WouldFork. Three additional tests cover OffMode's branch-tip advance, rejection of caller-set Push/Pull, and PushFailedError's unwrap/format contract.

Closes #173 check-in: ebcb65b235 user: [email protected] tags: trunk

09:32
feat(hub): expose Ready() channel for serve-loop start barrier (#172)

Adds Hub.Ready() returning a channel closed when ServeHTTP has wired up the http.Server and is about to enter the Accept loop. Consumers that advertise hub URLs externally (e.g. sesh writing session state JSON) can wait on Ready() before publishing, closing the window where the TCP listener is bound but the goroutine running ServeHTTP hasn't started processing requests.

Closes #171.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 0f4bfb32df user: [email protected] tags: trunk

2026-05-13
21:03
fix: exclude modernc driver registration from wasm builds (#169)

The init()-based modernc registration added in #166 (v0.0.18) blocked wasm builds because modernc references syscalls unavailable under js/wasm. Adds a //go:build !js && !wasm constraint to hub/driver.go so the registration is skipped on wasm targets — wasm consumers register their own driver (libfossil's ncruces driver supports wasm via its ncruces_js.go file).

Native builds are unchanged: hub still auto-registers modernc. Wasm builds: hub package now compiles successfully under GOOS=js GOARCH=wasm; consumer brings its own driver.

Verified: GOOS=js GOARCH=wasm go vet ./hub/... # clean GOOS=js GOARCH=wasm go build ./hub/... # success go test ./hub/... -timeout 120s # 38/38 pass on native

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 0fa13d595b user: [email protected] tags: trunk, v0.0.19

20:49
chore: bump modernc driver to v0.2.0 (#168)

Picks up the _txlock=immediate DSN fix from libfossil#33/#34 so concurrent writers serialize at BEGIN (where busy_timeout applies) instead of racing the SHARED→RESERVED upgrade.

Required upstream for sesh's --scope=project mode to remain safe after sesh drops its direct libfossil/db/driver/modernc require. Without this bump, EdgeSync's hub init() (added in v0.0.18) would register a pre-fix driver and consumers regress on contention.

Closes #167.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: d5a46f76de user: [email protected] tags: trunk

20:27
refactor(hub): register modernc SQL driver internally (#166)

The hub package promised consumers wouldn't need to import libfossil directly, but they still had to blank-import libfossil's SQL driver to satisfy the underlying db layer's registration requirement. Move the blank import into hub itself (hub/driver.go) so the package fulfills its own dependency.

Consumers like sesh can now reduce their main.go to a plain `import "github.com/danmestas/EdgeSync/hub"` (or whatever wrapper they use) without taking a direct dep on libfossil's internal SQL machinery.

modernc is registered as the production default. If a consumer ever needs ncruces (WASM/WASI), an escape hatch can be added then.

Tests in hub/ drop their now-redundant blank imports.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 754914fd82 user: [email protected] tags: trunk, v0.0.18

00:13
refactor(agent): drop mergeWithParent now that libfossil v0.6.2 ships the fix (#165)

libfossil#30 / EdgeSync#163 shipped consumer-side and upstream respectively. With libfossil v0.6.2 (PR #32, commit e6d34fb), Repo.Commit merges parent F-cards with supplied Files internally when ParentID > 0 — the exact behavior agent.mergeWithParent was emulating. Drop the duplicate.

Repo policy (CLAUDE.md "Upstream-fix policy") is unambiguous: when a fix lands in a repo we own, the workaround in the consuming repo should be removed, not left as defense-in-depth. libfossil is ours; a regression there gets fixed there.

Bumps: - root go.mod: v0.6.1 → v0.6.2 - leaf/go.mod: v0.5.0 → v0.6.2 (was lagging; auto-bump workflow has been failing per #146/#158) - bridge/go.mod: v0.5.0 → v0.6.2 (same)

Keeps the regression tests TestAgent_Commit_PreservesParentFiles and TestAgent_Commit_SuppliedFileOverridesParent — they assert behavior, not implementation, and now serve as integration coverage so any future libfossil regression on full-tree manifest emission surfaces in EdgeSync CI too.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: fabb4e3453 user: [email protected] tags: trunk

2026-05-12
23:50
feat(hub): persist .commit publish watermark across hub restarts (#164)

publishedRID was in-memory only. initPublishWatermark snapshotted the current max ci rid on every start, so any commit that landed in the repo but didn't get its .commit notification out before the hub died (crash mid-publish, HTTP push that completed the SQLite write but crashed before the wrapper ran publishNewCommits, or an offline tool writing to the repo while the hub was down) became silently un-propagated. Peers caught up incidentally on the next commit or not at all. Real gap.

Persist the watermark to the repo's config table under "edgesync:hub-published-rid" after each successful publish. On startup, initPublishWatermark reads the persisted value first and falls back to the max-rid snapshot only when nothing is persisted (cold start). Schedule a one-shot publishNewCommits call publishCatchupDelay (3s) after NewHub so it runs after NATS leaf clients have had time to reconnect on the same leaf port — an immediate catchup fires into a void because nats-core has no per-subscriber queuing.

Stop cancels the pending catchup via catchupStop so the goroutine doesn't outlive the hub.

Regression test TestCrossLeaf_HubRestart_RepublishesUnannouncedCommits: two hubs leaf-linked on a stable leaf port; stop A, open the repo file directly via libfossil and commit (no hub, no NATS, no publish hook), restart A; assert B reads the offline file via the delayed startup catchup. The "open repo file directly" trick is morally equivalent to "hub crashed after libfossil's write but before the wrapper's publish" — both produce a commit that's in the repo but unannounced.

Known limitations (deferred): - If no peer reconnects within publishCatchupDelay, the catchup publish is still lost. JetStream-durable .commit subject would close the gap; tracked as a follow-up. - The .commit subscriber on the pull side doesn't advance the receiving hub's watermark, so a pulled-in commit will be republished on the receiver's next restart. Idempotent on the far side (the subscriber's pull converges to zero blobs) but wasteful chatter; can be tightened in a follow-up. - v1→v2 upgrade with un-announced backlog at the time of upgrade treats those commits as "already known" — same silent-loss behavior as v1; not made worse.

Refs #161 review notes.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 00bc87892b user: [email protected] tags: trunk

23:38
fix(agent): preserve parent's tracked files in agent.Commit (#152) (#163)

agent.Commit passed opts.Files straight through to libfossil.Commit, which emits a partial manifest containing only the supplied subset. Parent files not in opts.Files were silently dropped — bones' multi-slot workflow (each slot commits only its own paths on a linear trunk chain) materialised only the last slot's files into git, every prior slot's work lost. Bug chains to libfossil#30, but EdgeSync can shield consumers regardless of upstream's resolution.

opts.Files now reads with `fossil ci` semantics — "files this commit changes." A new mergeWithParent helper enumerates the parent's F-cards, overlays supplied files (supplied wins by name), and hands the full set to libfossil.Commit. Inherited files preserve the parent's Perm; supplied files get libfossil's default (agent.FileToCommit has no Perm field today). parentID==0 stays an orphan root.

Two regression tests: - TestAgent_Commit_PreservesParentFiles — disjoint paths (alpha, bravo) on a linear chain. Both must surface at trunk's tip. - TestAgent_Commit_SuppliedFileOverridesParent — same-name supplied content must win over parent's.

Tests verify against BranchTip + ReadFile directly. a.Read can't be used because libfossil ResolveVersion("trunk") goes through the sym-trunk singleton tag, which libfossil.Commit doesn't propagate forward; only the first commit on trunk is reachable that way. Orthogonal to #152; flagging separately.

Closes #152

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> check-in: 7f0fbfa6ef user: [email protected] tags: trunk