<p align="center">
<img src="./assets/logo.png" alt="go-libfossil" width="260">
</p>
<h1 align="center">go-libfossil</h1>
[](https://libfossil-docs.daniel-mestas.workers.dev/)
[](https://pkg.go.dev/github.com/danmestas/go-libfossil)
[](https://goreportcard.com/report/github.com/danmestas/go-libfossil)
[](https://github.com/danmestas/go-libfossil/actions/workflows/test.yml)
[](./LICENSE)
[](https://go.dev)
Pure-Go library and CLI for [Fossil](https://fossil-scm.org) repositories. Drop-in compatible with Fossil's `.fossil` SQLite format, with no CGo and no upstream `fossil` binary required.
> The repository and module path are **go-libfossil** (`go get github.com/danmestas/go-libfossil`), but the Go package is still imported as `libfossil` and the CLI installs as the `libfossil` command. Only the path moved; the package and binary names are unchanged.
## Why libfossil
- **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 `Repo` API 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](./docs/site/content/docs/testing.md#buggify) fault injection.
## Installation
### CLI
```bash
go install github.com/danmestas/go-libfossil/cmd/libfossil@latest
```
The binary is named `libfossil` to avoid `$PATH` collisions with upstream `fossil`.
### Library
```bash
go get github.com/danmestas/go-libfossil
```
## CLI quick start
```bash
libfossil 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 verify
```
Full command list: `libfossil --help`. See [docs/migration-from-fossil.md](./docs/site/content/docs/migration-from-fossil.md) for a command-by-command comparison with upstream Fossil.
## Library quick start
```go
package main
import (
"log"
libfossil "github.com/danmestas/go-libfossil"
_ "github.com/danmestas/go-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.TimelineOpts{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/](./examples/embed-repo-api/).
## Sync
```go
// 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.
## Embedding the CLI
`cli/` exposes [Kong](https://github.com/alecthomas/kong) command structs you can mount inside your own CLI:
```go
import (
"github.com/alecthomas/kong"
"github.com/danmestas/go-libfossil/cli"
_ "github.com/danmestas/go-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))
}
```
## Project layout
| 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](./docs/site/content/docs/architecture.md).
## SQLite drivers
| Driver | Import | When to use |
|--------|--------|-------------|
| `modernc` (default) | `_ "github.com/danmestas/go-libfossil/db/driver/modernc"` | Default for any server, CLI, or desktop use |
| `ncruces` | `_ "github.com/danmestas/go-libfossil/db/driver/ncruces"` | wasm targets (`GOOS=wasip1` or browser/OPFS) |
Library embedders select exactly one driver with a blank import at link time; importing both panics. The shipped `cmd/libfossil` CLI uses `modernc` by default; `go build -tags test_ncruces ./cmd/libfossil` selects `ncruces`. See [docs/extension-points.md](./docs/site/content/docs/extension-points.md#sqlite-driver-interface) for the registration contract.
## Observers
```go
// 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/go-libfossil/observer/otel"
obs := otel.NewSyncObserver()
```
Both `SyncObserver` and `CheckoutObserver` are small interfaces you can implement directly. Details: [docs/extension-points.md](./docs/site/content/docs/extension-points.md#observer-interfaces).
## WASI / wasm
libfossil compiles to a generic WASI Preview 1 module via the `ncruces` driver:
```bash
GOOS=wasip1 GOARCH=wasm go build -o libfossil.wasm ./examples/wasm/
```
See [examples/wasm/](./examples/wasm/) for a runnable demo and current runtime caveats (the non-js ncruces driver variant has a known locking-mode quirk under wasmtime).
## Testing
- `make test` — unit tests with the default `modernc` driver
- `make test-drivers` — unit tests against both `modernc` and `ncruces`
- `make test-otel` — OTel submodule tests (runs with `GOWORK=off`)
- `make test-all` — everything, including the binary build
- `make 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](./docs/site/content/docs/testing.md).
## Documentation
Full documentation: **<https://libfossil-docs.daniel-mestas.workers.dev>**.
Local preview: `make docs-serve`, then visit http://localhost:1313/.
Markdown sources: [`docs/site/content/`](./docs/site/content/).
| Doc | Audience |
|-----|----------|
| [docs/architecture.md](./docs/site/content/docs/architecture.md) | Contributors learning the codebase |
| [docs/testing.md](./docs/site/content/docs/testing.md) | Contributors writing or debugging tests |
| [docs/extension-points.md](./docs/site/content/docs/extension-points.md) | Consumers adding observers or custom drivers |
| [docs/migration-from-fossil.md](./docs/site/content/docs/migration-from-fossil.md) | Users coming from upstream `fossil` |
| [CONTRIBUTING.md](./CONTRIBUTING.md) | Anyone submitting a PR |
| [CHANGELOG.md](./CHANGELOG.md) | Release notes |
## Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md).
## Acknowledgements
This project stands on the work of others.
- **[Stephan Beal](https://wanderinghorse.net)** — author of **[libfossil](https://fossil.wanderinghorse.net/r/libfossil)**, the C library that gives this project its name. This is an *indirect* Go port: it follows the shape and intent of Stephan's libfossil rather than transliterating it, and leans on the original as a reference for how a Fossil library ought to be organized.
- **[Dr. D. Richard Hipp](https://www.hwaci.com/drh/)** and the **[Fossil SCM](https://fossil-scm.org)** project — the [Fossil source code](https://fossil-scm.org) was the implementation guide of record. Where this library and upstream Fossil had to agree on the bytes — SQLite schema, manifest format, delta and sync wire formats — Fossil's source was the authority.
- **[TigerBeetle](https://tigerbeetle.com)** and **[TigerStyle](https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TIGER_STYLE.md)** — the testing philosophy: assertions everywhere, bounded everything, and treating tests as a first-class engineering surface.
- **[John Ousterhout](https://web.stanford.edu/~ouster/)** and **[*A Philosophy of Software Design*](https://web.stanford.edu/~ouster/cgi-bin/aposd.php)** — the engineering principles for keeping modules deep and interfaces narrow as the codebase grew.
- **[Antithesis](https://antithesis.com)** and **[deterministic simulation testing](https://antithesis.com/docs/resources/deterministic_simulation_testing/)** — the model behind this project's own DST harness, where time, randomness, and faults are controlled so that every failure is perfectly reproducible.
## License
MIT — see [LICENSE](./LICENSE).