Pure-Go library and CLI for Fossil repositories. Drop-in compatible with Fossil's .fossil SQLite format, with no CGo and no upstream fossil binary required.
- Pure Go, no CGo. Static binaries, trivial cross-compilation, works under
GOOS=wasip1 GOARCH=wasm. - Drop-in compatible. Repositories produced by upstream Fossil are readable, and vice versa — it's the same SQLite schema.
- Embeddable. Use the full
RepoAPI directly in your Go service. No subprocess calls, no temp files. - Instrumented. Sync and checkout observer interfaces make OpenTelemetry (or your own metrics) a few lines of code.
- Deterministically testable. Deterministic simulation testing (DST) with BUGGIFY fault injection.
go install github.com/danmestas/libfossil/cmd/libfossil@latestThe binary is named libfossil to avoid $PATH collisions with upstream fossil.
go get github.com/danmestas/libfossillibfossil repo new my.fossil
libfossil -R my.fossil repo ci -m "first commit" hello.txt
libfossil -R my.fossil repo timeline
libfossil -R my.fossil repo ls
libfossil -R my.fossil repo annotate hello.txt
libfossil -R my.fossil repo verifyFull command list: libfossil --help. See docs/migration-from-fossil.md for a command-by-command comparison with upstream Fossil.
package main
import (
"log"
libfossil "github.com/danmestas/libfossil"
_ "github.com/danmestas/libfossil/db/driver/modernc"
)
func main() {
r, err := libfossil.Create("my.fossil", libfossil.CreateOpts{User: "admin"})
if err != nil {
log.Fatal(err)
}
defer r.Close()
rid, uuid, err := r.Commit(libfossil.CommitOpts{
Files: []libfossil.FileToCommit{{Name: "hello.txt", Content: []byte("hello")}},
Comment: "initial commit",
User: "admin",
})
if err != nil {
log.Fatal(err)
}
log.Printf("committed rid=%d uuid=%s", rid, uuid[:12])
entries, err := r.Timeline(libfossil.LogOpts{Limit: 10})
if err != nil {
log.Fatal(err)
}
for _, e := range entries {
log.Printf(" %s %s", e.UUID[:12], e.Comment)
}
}Working example: examples/embed-repo-api/.
// Sync with a remote Fossil server.
result, err := r.Sync(ctx,
libfossil.NewHTTPTransport("http://host/repo"),
libfossil.SyncOpts{Push: true, Pull: true},
)
// Serve the sync protocol (e.g. for leaf agents, bridges, mirrors).
http.Handle("/", r.XferHandler())The Transport interface is pluggable — swap HTTP for NATS, WebSocket, libp2p, or anything that delivers bytes round-trip.
cli/ exposes Kong command structs you can mount inside your own CLI:
import (
"github.com/alecthomas/kong"
"github.com/danmestas/libfossil/cli"
_ "github.com/danmestas/libfossil/db/driver/modernc"
)
type MyCLI struct {
cli.Globals
Repo cli.RepoCmd `cmd:"" help:"Fossil repo operations"`
// Add your own commands here.
}
func main() {
var c MyCLI
ctx := kong.Parse(&c)
ctx.FatalIfErrorf(ctx.Run(&c.Globals))
}| Package | Purpose |
|---|---|
root (libfossil) |
Repo handle, Transport, SyncObserver, CheckoutObserver |
cli/ |
Embeddable Kong command structs for the repo subcommands |
cmd/libfossil/ |
Standalone CLI binary |
db/ |
SQLite abstraction with pluggable drivers |
db/driver/modernc |
Pure-Go SQLite driver (default) |
db/driver/ncruces |
wasm-capable SQLite driver |
observer/otel/ |
Optional OpenTelemetry observer (separate submodule) |
dst/ |
Deterministic simulation tests + BUGGIFY harness |
simio/ |
Clock, Rand, Storage interfaces for deterministic testing |
testutil/ |
Shared test helpers |
Deep dive: docs/architecture.md.
| Driver | Import | When to use |
|---|---|---|
modernc (default) |
_ "github.com/danmestas/libfossil/db/driver/modernc" |
Default for any server, CLI, or desktop use |
ncruces |
_ "github.com/danmestas/libfossil/db/driver/ncruces" |
wasm targets (GOOS=wasip1 or browser/OPFS) |
Driver selection happens via blank import at link time. See docs/extension-points.md for the registration contract.
// Built-in, zero dependencies.
libfossil.NopSyncObserver() // silent
libfossil.StdoutSyncObserver() // human-readable stderr
// Optional OpenTelemetry (separate submodule — no OTel deps in core).
import "github.com/danmestas/libfossil/observer/otel"
obs := otel.NewSyncObserver()Both SyncObserver and CheckoutObserver are small interfaces you can implement directly. Details: docs/extension-points.md.
libfossil compiles to a generic WASI Preview 1 module via the ncruces driver:
GOOS=wasip1 GOARCH=wasm go build -o libfossil.wasm ./examples/wasm/See examples/wasm/ for a runnable demo and current runtime caveats (the non-js ncruces driver variant has a known locking-mode quirk under wasmtime).
make test— unit tests with the defaultmoderncdrivermake test-drivers— unit tests against bothmoderncandncrucesmake test-otel— OTel submodule tests (runs withGOWORK=off)make test-all— everything, including the binary buildmake setup-hooks— install the pre-commit hook (~45s/commit)
Test strategy — unit tests, dual-driver matrix, and deterministic simulation (DST) with BUGGIFY fault injection: docs/testing.md.
Full documentation: https://libfossil-docs.daniel-mestas.workers.dev.
Local preview: make docs-serve, then visit http://localhost:1313/.
Markdown sources: docs/site/content/.
| Doc | Audience |
|---|---|
| docs/architecture.md | Contributors learning the codebase |
| docs/testing.md | Contributors writing or debugging tests |
| docs/extension-points.md | Consumers adding observers or custom drivers |
| docs/migration-from-fossil.md | Users coming from upstream fossil |
| CONTRIBUTING.md | Anyone submitting a PR |
| CHANGELOG.md | Release notes |
See CONTRIBUTING.md.
MIT — see LICENSE.