Skip to content
go-libfossil Artifact [53337a53b2]
Login

Artifact [53337a53b2]

Artifact 53337a53b2cc928f27cdbe81081e3b595a696386f4781482b14f247ed12a3d3f:


go-libfossil

go-libfossil

Docs Go Reference Go Report Card Tests License Go

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.

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

Installation

CLI

go install github.com/danmestas/go-libfossil/cmd/libfossil@latest

The binary is named libfossil to avoid $PATH collisions with upstream fossil.

Library

go get github.com/danmestas/go-libfossil

CLI quick start

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 for a command-by-command comparison with upstream Fossil.

Library quick start

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/.

Sync

// 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())

Served repositories enforce the user table the way fossil does. A request without credentials runs as nobody, which a new repository lets clone and pull but not push. Give clients an account, or grant nobody more with r.SetCaps("nobody", ...).

The Transport interface is pluggable — swap HTTP for NATS, WebSocket, libp2p, or anything that delivers bytes round-trip.

Embedding the CLI

cli/ exposes Kong command structs you can mount inside your own CLI:

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.

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 for the registration contract.

Observers

// 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.

WASI / wasm

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).

Testing

Test strategy — unit tests, dual-driver matrix, and deterministic simulation (DST) with BUGGIFY fault injection: 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/.

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

Contributing

See CONTRIBUTING.md.

Acknowledgements

This project stands on the work of others.

License

MIT — see LICENSE.