# go-libfossil Documentation (Full)
> Every documentation page concatenated in sidebar order. Curated overview: https://libfossil-docs.daniel-mestas.workers.dev/llms.txt
---
# Documentation
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/
go-libfossil reads, writes and syncs Fossil repositories in Go, without cgo. It ships as a Go module and as the `libfossil` command.
- **[Get started](/docs/get-started/)**: install, a first repository from the command line, the library from Go, and a first sync.
- **[Concepts](/docs/concepts/)**: artifacts, the derived tables, the sync protocol, sub-manifests, sparse cones, horizons and signing.
- **[Guides](/docs/guides/)**: production use, migrating from fossil, embedding the CLI, extension points and testing.
- **[Reference](/docs/reference/)**: the Go API, the command line, settings, and conformance with the drafts.
- **[Examples](/docs/examples/)**: runnable programs in the repository.
---
# Get started
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/get-started/
Install the `libfossil` command, make a first repository from the command line, embed the library in a Go program, and sync with another repository.
- [Install](/docs/get-started/install/): the command, the Go module, and the SQLite driver.
- [Quickstart: the CLI](/docs/get-started/quickstart/): create a repository, commit, and read it back with stock `fossil`.
- [Embed in Go](/docs/get-started/embed-in-go/): create, commit, open and read history from Go.
- [First sync](/docs/get-started/first-sync/): serve a repository, clone it, pull and push.
---
# Install
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/get-started/install/
go-libfossil is a Go module and a command. Neither needs cgo, a C toolchain or an installed `fossil`.
## The command
```sh
go install github.com/danmestas/go-libfossil/cmd/libfossil@latest
libfossil --help
```
This needs the Go toolchain named in `go.mod` (Go 1.26 or later). The binary is statically linked and runs on Linux, macOS and Windows. Every command is listed in the [CLI reference](/docs/reference/cli/).
## The library
```sh
go get github.com/danmestas/go-libfossil@latest
```
The library talks to SQLite through `database/sql` and does not pick a driver for you. Blank-import exactly one of the two driver modules in your `main` package; importing both panics at start-up.
| Driver | Import | Use it for |
|---|---|---|
| modernc | `_ "github.com/danmestas/go-libfossil/db/driver/modernc"` | servers, command-line tools, desktop: SQLite translated to Go |
| ncruces | `_ "github.com/danmestas/go-libfossil/db/driver/ncruces"` | `GOOS=js` and `GOOS=wasip1` builds: SQLite itself as WebAssembly |
Each driver is its own module, so `go get` it too:
```sh
go get github.com/danmestas/go-libfossil/db/driver/modernc@latest
```
## WebAssembly
With the ncruces driver the module builds for `GOOS=js GOARCH=wasm` (a browser) and `GOOS=wasip1 GOARCH=wasm` (any WASI Preview 1 host). CI builds the module's packages for `js/wasm` on every change. The [wasm example](https://github.com/danmestas/go-libfossil/tree/main/examples/wasm) builds and runs a WASI module.
### TinyGo
The whole public API, the HTTP sync client and signing included, also builds with [TinyGo](https://tinygo.org) for the `wasm` target, which is what makes it small enough to ship with a page or an edge function:
```
tinygo build -target=wasm -no-debug -opt=z -stack-size=1MB -o libfossil.wasm ./examples/tinygo/
wasm-opt -Oz --detect-features --strip-debug --strip-producers libfossil.wasm -o libfossil.wasm
```
- **Size:** for [`examples/tinygo`](https://github.com/danmestas/go-libfossil/tree/main/examples/tinygo), about 1.3 MB gzipped as CI builds it, against about 4.1 MB from the standard toolchain. A program without sync is smaller.
- **Driver:** a TinyGo build imports no SQLite driver. The host registers a `database/sql` driver over its own SQLite; ncruces is for standard Go builds and modernc has no wasm build.
- **Stack:** `-stack-size=1MB`, because TinyGo's default goroutine stack is too small for the deeper paths.
- **Differences:** under TinyGo a sync redirect ends the request with an error instead of being followed, and DNS failures are not classified as transient.
- **Without HTTP:** a program that serves sync through `HandleSyncWithOpts` and syncs, if at all, through a `Transport` its host supplies can build with `-tags libfossil_nohttp`. That leaves out `NewHTTPTransport`, `WithHTTPClient`, `Repo.Pull`, `Repo.XferHandler` and `Repo.ServeHTTP`, and with them `net/http`, whose TLS and X.509 dependencies are about 97 KB of a gzipped TinyGo binary. Everything else is unchanged. The tag is for library embedders: the `cli` package and `examples/tinygo`, which calls `NewHTTPTransport`, do not build with it. A tagged embedder supplies its own transport and serving, for example a `Transport` for `Sync` and its own request handling around `HandleSyncWithOpts`.
CI builds `examples/tinygo` with TinyGo 0.43 on every change.
## Next
- [Quickstart: the CLI](/docs/get-started/quickstart/)
- [Embed in Go](/docs/get-started/embed-in-go/)
---
# Quickstart: the CLI
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/get-started/quickstart/
Create a repository, commit a file, and read the result with stock `fossil`. The `libfossil` command drives the same code as the library, and the file it writes is an ordinary Fossil repository.
## Create a repository and a checkout
```sh
libfossil repo new notes.fossil
mkdir work && cd work
libfossil -R ../notes.fossil repo open .
```
```text
created repository: notes.fossil
opened checkout in . (repo: /home/ana/notes.fossil)
empty repository -- no checkins yet
```
`repo open` writes a `.fslckout` file in the directory. Commands run inside the checkout find the repository through it, so `-R` is only needed to open it.
## Commit
```sh
echo '# notes' > README.md
libfossil repo add README.md
libfossil repo ci -m "first notes" --user ana
```
```text
ADDED README.md
checkin 542a963405 (rid=2)
```
The check-in name is the hash of the manifest, which includes the time, so yours differs. `--user` sets the name on the check-in; without it the OS user name is used.
## Read the history
```sh
libfossil repo timeline
```
```text
542a963405 2026-10-23 21:00 ana first notes
```
The repository is a Fossil repository, so the `fossil` binary reads it unchanged:
```sh
fossil timeline -R ../notes.fossil
```
```text
=== 2026-10-23 ===
21:00:14 [542a963405] first notes (user: ana tags: trunk)
+++ no more data (1) +++
```
## Next
- [Embed in Go](/docs/get-started/embed-in-go/): the same steps from a Go program.
- [First sync](/docs/get-started/first-sync/): serve the repository and clone it.
- [CLI reference](/docs/reference/cli/): every command and flag.
- [Migrating from fossil](/docs/guides/migrating-from-fossil/): how the commands map to fossil's.
---
# Embed in Go
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/get-started/embed-in-go/
The `libfossil` package does what the command does, as typed Go values instead of command output. This program creates a repository, commits a file and reads the history back.
## Set up
```sh
mkdir notes && cd notes
go mod init example.com/notes
go get github.com/danmestas/go-libfossil@latest github.com/danmestas/go-libfossil/db/driver/modernc@latest
```
The blank import of `db/driver/modernc` registers the SQLite driver. A program registers exactly one; see [Install](/docs/get-started/install/) for the choice.
## Create, commit, read
```go
package main
import (
"fmt"
"log"
"github.com/danmestas/go-libfossil"
_ "github.com/danmestas/go-libfossil/db/driver/modernc"
)
func main() {
r, err := libfossil.Create("notes.fossil", libfossil.CreateOpts{User: "ana"})
if err != nil {
log.Fatal(err)
}
defer r.Close()
rid, uuid, err := r.Commit(libfossil.CommitOpts{
Files: []libfossil.FileToCommit{{Name: "README.md", Content: []byte("# notes\n")}},
Comment: "first notes",
User: "ana",
})
if err != nil {
log.Fatal(err)
}
fmt.Println("committed", uuid[:10], "rid", rid)
entries, err := r.Timeline(libfossil.TimelineOpts{Limit: 10})
if err != nil {
log.Fatal(err)
}
for _, e := range entries {
fmt.Println(e.UUID[:10], e.User, e.Comment)
}
files, err := r.ListFiles(rid)
if err != nil {
log.Fatal(err)
}
for _, f := range files {
fmt.Println(f.Name)
}
}
```
```sh
go run .
```
```text
committed ee68ff62c5 rid 2
ee68ff62c5 ana first notes
README.md
```
`Commit` returns the check-in's row id in the `blob` table and its full name. `CommitOpts.Files` lists file contents directly; no checkout or working directory is involved. To commit on top of an existing check-in, set `ParentID`: the files you pass replace the parent's entries of the same name and the rest of the parent's tree is carried forward, as `fossil commit` does.
The file is an ordinary Fossil repository: `fossil timeline -R notes.fossil` shows the same check-in.
## Open an existing repository
```go
r, err := libfossil.Open("notes.fossil")
if err != nil {
log.Fatal(err)
}
defer r.Close()
tip, err := r.BranchTip("trunk")
if err != nil {
log.Fatal(err)
}
files, err := r.ListFiles(tip)
```
`Open` fails if the file does not exist; `Create` fails if it does. Reads through one `*Repo` may run concurrently; writes serialize in SQLite. Close the handle once, when you are done with it. [Production patterns](/docs/guides/production/) covers error handling, contexts and handle lifetime.
## Next
- [First sync](/docs/get-started/first-sync/): clone, pull and push over HTTP.
- [SDK reference](/docs/reference/sdk/libfossil/api/): every exported type and function.
- [Examples](/docs/examples/): runnable programs in the repository.
---
# First sync
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/get-started/first-sync/
Two repositories exchange check-ins with Fossil's sync protocol over HTTP. `libfossil repo serve` is a sync server; `Clone`, `Repo.Sync` and `Repo.Pull` are the client. Either side can be stock `fossil`. [Sync](/docs/concepts/sync/) explains the protocol.
## Serve a repository
```sh
libfossil -R notes.fossil repo serve
```
```text
serving /home/ana/notes.fossil on http://127.0.0.1:8080 (Ctrl-C to stop)
```
`--addr` changes the bind address. The server answers sync requests only; it has no web interface.
An unauthenticated client gets the capabilities of the `nobody` user, which a new repository sets as `fossil new` does: clone and read, no check-in. To let a client push, give a user the check-in capability `i` and a password:
```sh
libfossil -R notes.fossil repo user add --cap=i bo
libfossil -R notes.fossil repo user passwd bo
```
Both commands print the user's generated password.
## Clone
From the command line, with either tool:
```sh
libfossil repo clone http://127.0.0.1:8080 copy.fossil
fossil clone http://127.0.0.1:8080 copy2.fossil
```
```text
Cloned into copy.fossil
Rounds: 2
Blobs: 2
Project-code: ec1581be7066e95dd3b9ee14b91ab3f258221ce4
```
`repo clone` also clones part of a repository: `--cone` by path and `--horizon` by age. See [Sparse cones](/docs/concepts/sparse-cones/) and [Horizons](/docs/concepts/horizons/).
## Clone, commit, push and pull from Go
```go
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/danmestas/go-libfossil"
_ "github.com/danmestas/go-libfossil/db/driver/modernc"
)
func main() {
ctx := context.Background()
url := "http://127.0.0.1:8080"
r, res, err := libfossil.Clone(ctx, "copy.fossil", libfossil.NewHTTPTransport(url), libfossil.CloneOpts{})
if err != nil {
log.Fatal(err)
}
defer r.Close()
fmt.Println("cloned", res.BlobsRecvd, "blobs in", res.Rounds, "rounds")
tip, err := r.BranchTip("trunk")
if err != nil {
log.Fatal(err)
}
if _, _, err := r.Commit(libfossil.CommitOpts{
Files: []libfossil.FileToCommit{{Name: "TODO.md", Content: []byte("- ship\n")}},
Comment: "add a todo",
User: "bo",
ParentID: tip,
}); err != nil {
log.Fatal(err)
}
pushed, err := r.Sync(ctx, libfossil.NewHTTPTransport(url), libfossil.SyncOpts{
Push: true, Pull: true, User: "bo", Password: os.Getenv("NOTES_PASSWORD"),
})
if err != nil {
log.Fatal(err)
}
fmt.Println("sent", pushed.FilesSent, "received", pushed.FilesRecvd)
pulled, err := r.Pull(ctx, url, libfossil.PullOpts{})
if err != nil {
log.Fatal(err)
}
fmt.Println("pull rounds", pulled.Rounds)
}
```
```sh
NOTES_PASSWORD=... go run .
```
```text
cloned 2 blobs in 2 rounds
sent 2 received 0
pull rounds 1
```
`Clone` creates the file and fails if it exists. `Sync` with `Push` and `Pull` runs one session in both directions until neither side has anything the other lacks; `Pull` is a pull-only shorthand that takes a URL. Without `User`, a session runs as `nobody`. Pass a context with a deadline in a service: a stalled peer otherwise holds the call open.
The commit made in the clone went to the origin:
```sh
libfossil -R notes.fossil repo timeline
```
```text
fb5af4c078 2026-10-23 21:02 bo add a todo
ee68ff62c5 2026-10-23 21:02 ana first notes
```
## Next
- [Sync](/docs/concepts/sync/): the cards and rounds behind these calls.
- [Production patterns](/docs/guides/production/): contexts, errors and telemetry for a long-running service.
- [Signing](/docs/concepts/signing/): signed check-ins and signed horizon seeds.
---
# Concepts
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/concepts/
How a Fossil repository is put together, and how go-libfossil implements it. Each page names the Internet-Draft in [`conformance/rfcs/`](https://github.com/danmestas/go-libfossil/tree/main/conformance/rfcs) that specifies the behaviour.
- [Architecture](/docs/concepts/architecture/): packages, drivers, observers and the simulator.
- [Artifacts](/docs/concepts/artifacts/): the text manifests that make up history, and how they are named.
- [Repository state](/docs/concepts/repository-state/): the tables derived from the artifacts.
- [Sync](/docs/concepts/sync/): the card protocol that clones, pulls and pushes.
- [Sub-manifests](/docs/concepts/sub-manifests/): check-ins written as trees instead of flat file lists.
- [Sparse cones](/docs/concepts/sparse-cones/): holding part of a repository by path.
- [Horizons](/docs/concepts/horizons/): holding part of a repository by age.
- [Signing](/docs/concepts/signing/): clear-signed check-ins and signed horizon seeds.
---
# Architecture
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/concepts/architecture/
## Overview
libfossil is a pure-Go library and CLI that reads and writes Fossil SCM's
`.fossil` SQLite repositories. The public surface (package `libfossil`)
exposes `Repo`, `Create`, `Open`, `Clone`, checkout, and check-in
operations; internals live under `internal/`. SQLite access is mediated
by a pluggable driver layer in `db/`, with two shippable drivers
(`modernc`, `ncruces`) selected by blank import. Instrumentation is
pulled in by implementing the `SyncObserver` / `CheckoutObserver`
interfaces; an optional OTel adapter lives in its own submodule. A
deterministic simulator under `dst/` plus a `BUGGIFY` fault-injection
harness in `simio/` exercise concurrent repo operations under a seed.
## Module layout
The library is a Go workspace (`go.work`) of four modules: the root, the
two driver modules, and the OTel observer. (The documentation site under
`docs/site` has its own `go.mod` and is not in the workspace.) Layout:
- **Root package `libfossil`** — thin facade over `internal/repo`,
`internal/sync`, and `internal/checkout`. Main files: `fossil.go`
(Create, Open, Clone), `repo.go` (Repo handle + DB accessor),
`checkout.go`, `checkout_sparse.go` (cones and horizons),
`repo_checkout.go`, `repo_merge.go`, `repo_sync.go`, `repo_pull.go`,
`repo_sparse.go`, `repo_submanifest.go`, `repo_history.go`,
`repo_admin.go`, `repo_extras.go`, `required_content*.go`,
`horizon_*.go`, `signing.go`, `transport.go`, `observer.go`, `types.go`,
`errors.go`, `julian.go`.
- **`cli/`** — Kong command definitions. `repo.go` aggregates 43
subcommands (`new`, `clone`, `ci`, `co`, `timeline`, `diff`, `merge`,
`tag`, `branch`, `uv`, `stash`, `bisect`, `annotate`, `user`, `serve`,
`cone`, `submanifests`, etc.). Intended to be embedded by any `main` that
wants a fossil-shaped CLI.
- **`cmd/libfossil/`** — the shipped binary. `main.go` wires Kong and
dispatches to `cli`; `driver_modernc.go` and `driver_ncruces.go` select
the driver by build tag (`modernc` by default, `ncruces` under
`test_ncruces`).
- **`internal/`** — private implementation, grouped by concern:
- Storage layer: `blob`, `content`, `manifest`, `deck`, `delta`,
`hash`, `fsltype`, `repo` (schema + DB boot), `tag`, `leaf`,
`clearsign`.
- Sparse and horizon stores: `cone`, `horizon` (held set and seed).
- Working-tree operations: `checkout` (checkin, extract, update,
revert, fork, rename, status), `vfile`, `merge` (three-way, ancestor,
detect, resolve, strategies), `diff`, `annotate`, `undo`, `stash`,
`bisect`, `branch`, `path`.
- Sync / transport: `sync` (client, handler, clone, session,
ckin-lock, serve-http, table-sync, UV, sparse and horizon serving),
`xfer` (card codec).
- Housekeeping: `auth`, `shun`, `verify`, `search`, `uv`, `testdriver`.
- **`db/`** — SQLite abstraction (see below).
- **`db/driver/modernc`**, **`db/driver/ncruces`** — separate modules,
each registers exactly one `database/sql` driver.
- **`observer/otel/`** — separate module implementing the observer
interfaces against OpenTelemetry.
- **`dst/`** — deterministic simulator (`simulator.go`, `node.go`,
`network.go`, `peer_network.go`, `mock_fossil.go`, `invariants.go`,
`event.go`) with scenario-style tests.
- **`simio/`** — deterministic IO primitives: `SimClock`, seeded
`Rand`, in-memory storage, and the global `Buggify` switch.
- **`testutil/`** — shared test helpers for the rest of the tree.
## Storage model
A `.fossil` file is a SQLite database. All repository state — blobs,
manifests, tags, branches, unversioned files, tickets, wiki, config,
users — lives in that file. libfossil does not maintain any sidecar
state for the repo itself (a working tree has its own SQLite DB).
Content is stored **content-addressed** in the `blob` table, keyed by
either SHA1 or SHA3-256 names (the repository's hash policy picks which
names new content; see [Artifacts](/docs/concepts/artifacts/)). Every blob is
zlib-compressed with a 4-byte big-endian uncompressed-size prefix;
`internal/blob.Compress` and `Decompress` handle that framing. Blobs may
be stored as full content or as deltas against a parent; reconstruction
walks the chain and applies deltas via `internal/content.Expand`, with
cycle detection. `internal/content.Verify` rehashes expanded content
against the stored UUID.
**Structural artifacts** are themselves blobs whose text describes a
check-in (a manifest), a tag change, a wiki edit, a ticket change, an
attachment, a technote, a forum post, or a cluster. `internal/deck`
parses and marshals them and `internal/manifest` builds check-ins;
"crosslinking" (`internal/manifest/crosslink.go`) is the step that
projects an artifact's effects into derived tables (`mlink`, `plink`,
`event`, `tagxref`, etc.) so queries like timeline and checkout work
without re-parsing blobs. See [Repository state](/docs/concepts/repository-state/).
## Driver layer
`db/` is a thin wrapper over `database/sql`. See
[`db/doc.go`](https://github.com/danmestas/go-libfossil/blob/main/db/doc.go). A driver registers at `init` time via
`db.Register(DriverConfig{Name, BuildDSN})`; exactly one registration is
allowed (a second call panics). Library embedders blank-import exactly one
driver. `db.Open` / `db.OpenWith` look up the registered driver, run
`BuildDSN(path, pragmas)` to produce the DSN, and then call `sql.Open` with
that driver name. Both shipped drivers construct
`file:?_pragma=k(v)&...` DSNs — differing only in the `database/sql`
driver name they register under (`"sqlite"` for modernc, `"sqlite3"` for
ncruces).
Shipped drivers:
- **`db/driver/modernc`** — default for `cmd/libfossil`. Blank-imports
`modernc.org/sqlite`, a pure-Go, CGo-free translation of SQLite with full
filesystem access.
- **`db/driver/ncruces`** — blank-imports `github.com/ncruces/go-sqlite3`,
which runs SQLite as WebAssembly. Has a `ncruces_js.go` variant for
`GOOS=js` / browser targets. Used for `GOOS=wasip1` builds and by
`go build -tags test_ncruces ./cmd/libfossil`.
## Sync protocol
`internal/sync` implements Fossil's sync-over-HTTP protocol as a client
and a stateless server, with the card codec in `internal/xfer`. See
[Sync](/docs/concepts/sync/) for the protocol and where each part lives.
## Observer model
The core keeps instrumentation as a non-goal; hooks are plain Go
interfaces on the public API. `SyncObserver` and `CheckoutObserver` in
[`observer.go`](https://github.com/danmestas/go-libfossil/blob/main/observer.go) define lifecycle events (session
start/end, per-round stats, extract/scan/commit phases). `NopSyncObserver`
/ `NopCheckoutObserver` are zero-cost defaults; `StdoutSyncObserver` /
`StdoutCheckoutObserver` log to stderr for quick debugging.
For OpenTelemetry, [`observer/otel`](https://github.com/danmestas/go-libfossil/tree/main/observer/otel/) is a separate Go
module so the core doesn't pull in the OTel SDK. Users import it
explicitly when they want traces, metrics, or structured logs.
See [Extension Points](/docs/guides/extension-points/) for the full observer
contract and a worked example of a custom observer.
## DST / simulation
`dst/` runs many Fossil nodes deterministically under a seed. A
`Simulator` (`dst/simulator.go`) owns a priority-queue event loop, a
`SimNetwork`, a `simio.SimClock`, and a set of `Node` actors. Scenarios
schedule clones, check-ins, merges, UV pushes, and partitions, and
tests assert invariants (`dst/invariants.go`, `crosslink_completeness_test.go`).
Fault injection has two forms. `simio.Buggify(p)` is a global
probability gate that returns `false` in production but flips `true`
with probability `p` once the simulator enables it; the blob and content
layers use it. The sync code instead asks a `BuggifyChecker` passed in
its options, at named sites such as `clone.emitCloneBatch.truncate` and
`handler.handleGimme.skip`; a nil checker injects nothing. `SeededBuggify`
in `dst` implements `BuggifyChecker` from a seeded random source.
See [Testing](/docs/guides/testing/) for running the DST matrix, seed sweeps,
and the driver test matrix.
---
# Artifacts
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/concepts/artifacts/
Everything a Fossil repository records is an **artifact**: a run of bytes, named by its own hash, that is never changed once written. File contents are artifacts. So are check-ins, tags, wiki pages and tickets, which are written as text **cards**. The format is `draft-fossil-artifact-format-00`.
## Names
An artifact's name is the hash of its exact stored bytes, with nothing added or stripped (§3.1). A 40-digit name is SHA-1; a 64-digit name is SHA3-256. The length alone tells which (§3.2). A signed artifact's name covers its signature wrapper too, so signing a check-in changes its name.
The repository's **hash policy** decides which algorithm names new content (§7.1):
| Policy | Names new content with |
|---|---|
| `sha1`, `auto` | SHA-1 |
| `sha3`, `sha3-only`, `shun-sha1` | SHA3-256 |
A repository with no policy set uses `sha3` unless it holds SHA-1 names and no SHA3 ones, in which case it uses `auto` (§7.2). A new repository is `sha3`. Names already assigned never change (§7.6). `Repo.SetHashPolicy(name)` sets the policy as `fossil hash-policy` does.
## Structural artifacts and cards
A structural artifact is lines of cards: a capital letter, then space-separated arguments, with the cards in sorted order and a `Z` card last (§4). There are eight types, and each allows a fixed set of cards (§5.1, §5.2):
| Type | External name | Allowed cards |
|---|---|---|
| MANIFEST | `checkin` | B C D F N P Q R T U Z |
| CLUSTER | `cluster` | M Z |
| CONTROL | `tag` | D T U Z |
| WIKI | `wiki` | C D L N P U W Z |
| TICKET | `ticket` | D J K U Z |
| ATTACHMENT | `attachment` | A C D N U Z |
| EVENT | `technote` | C D E N P T U W Z |
| FORUM | `forumpost` | D G H I N P U W Z |
Any other byte string is plain content, such as a file's bytes. A parser rejects a structural artifact with a card its type does not allow, or without one its type requires.
[Sub-manifests](/docs/concepts/sub-manifests/) add an `S` card and a ninth type.
## A check-in manifest
This is a check-in made with `libfossil repo ci`:
```
C unsigned
D 2026-10-23T20:45:21.324
F docs/x.md a09fd4d0069e85b78c5234233ffb67179d30e19c2ec94629d224257a920c1234
F src/a.c 7eae391285a140af77b34475a35c4790095acb1f0f5e6ec7e10e962b2405de07
F src/vendor/v.c bedef797cd3184c3c7e620c5c231bbdd523f52ab19ca6301d037c7f2a3f99780
P 2afc4615dadbd7743716f8037223578d7d07a79ebc2cbfd5ba1b6028f133c7f2
R 43fe2394e6714e382e2f6563c13cf020
U dmestas
Z 56b668a3573378eb3e2387902909b385
```
- `C`: the check-in comment.
- `D`: the date, UTC.
- `F`: one per file: path and content hash, then optional permissions and an old name for a rename.
- `P`: the parent check-ins. The first is the primary parent; the rest are merge parents.
- `R`: an MD5 over every file's name, size and content (§6.3). A commit reads its manifest back and checks it. Over a sparse store there is no `R` card.
- `U`: the user.
- `Z`: the MD5 of every byte before it (§6.2).
The first check-in on a branch also carries `T` cards, such as `T *branch * trunk` and `T *sym-trunk *`. A **delta manifest** carries a `B` card naming a baseline manifest, and its `F` cards record only the differences from it (§8).
The name of this check-in is the SHA3-256 of exactly these bytes: `libfossil repo hash --sha3` over the output of `repo cat` prints `7507cc8c3e...`, the name the timeline shows.
## Deltas
Content is stored in the `blob` table, zlib-compressed behind a 4-byte size prefix. A blob can also be stored as a **delta** against another blob, with the pair recorded in the `delta` table (`rid`, `srcid`). Reading it applies the chain of deltas back to a full blob, and the result must hash to the blob's name.
A delta is a header with the target length, a run of copy and insert segments, and a trailer with a checksum of the result (`draft-fossil-delta-format-00` §3). Integers are written in base 64. A delta does not name its source; the storage layer records that (§1).
As in fossil, deltas run backwards: a new version is stored whole and its predecessor is rewritten as a delta against it, so the newest version of a file is always one read away.
## Where the code is
- `internal/deck`: parse and marshal cards, the `Z` card, signature-wrapper stripping.
- `internal/manifest`: build check-ins, compute the `R` card, crosslink.
- `internal/delta`: create and apply deltas (`Create`, `Apply`).
- `internal/hash`: SHA-1 and SHA3-256 names and the hash policy.
- `internal/blob` and `internal/content`: compressed storage, delta chains, verification.
---
# Repository state
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/concepts/repository-state/
[Artifacts](/docs/concepts/artifacts/) are the only facts a repository records. Everything else, such as which check-in is the tip of trunk, which branch a check-in is on, or which leaves are open, follows from them. `draft-fossil-repo-state-00` specifies that derivation, so two implementations reading the same artifacts reach the same answers. The one exception is the current trunk, which also reads the repository's `main-branch` setting (§9).
## The tables
A `.fossil` file stores the artifacts in `blob` and `delta`, and keeps the derived state in tables beside them, so a query does not re-parse every artifact.
| Table | Holds |
|---|---|
| `blob` | Every artifact, compressed, by `rid` and name (`uuid`). A row with no content is a phantom. |
| `delta` | Which blobs are stored as deltas, and against what. |
| `phantom` | Names the repository knows of but does not hold yet. |
| `event` | One row per check-in, tag, wiki edit, ticket change and so on: the timeline. |
| `plink` | Parent links between check-ins, with which parent is primary (§3). |
| `mlink` | Which file changed in which check-in, and from which earlier version. |
| `filename` | The file names `mlink` refers to. |
| `tag`, `tagxref` | Tag names, and each tag's effect on each check-in: cancel, singleton or propagating (§5). |
| `leaf` | Check-ins with no child on their own branch (§7). |
| `orphan` | Delta manifests whose baseline is absent. |
`blob` and `delta` are the source. The others are derived.
## Crosslinking
**Crosslinking** reads one artifact and writes its effects into the derived tables (§4). A check-in adds its `plink` and `mlink` rows and its `event` row. Its `T` cards, or a control artifact's, set `tagxref` rows and propagate along primary children until a cancel or another value blocks them (§5.4). Leaves are rechecked as parents gain children and as tags move (§7.6).
Artifacts can arrive in any order. Sync, clone, a commit and a rebuild all crosslink, and an artifact whose parent has not arrived yet is linked again when it does.
Branches are not stored as such. A check-in's branch is the value of its `branch` tag, inherited from its primary parent unless a tag on it says otherwise (§6). Name resolution (§8) turns a branch name, tag or `trunk` into one check-in: `Repo.ResolveVersion(name)`. `Repo.CurrentTrunk()` returns the tip of the main branch (§9).
## Rebuild gives the same rows
Because the derived tables are a function of the artifacts, they can be thrown away and computed again. go-libfossil's rebuild (`verify.Rebuild`, internal) empties the crosslinked tables and runs the same crosslink sweep a clone runs, so derived state has one owner. The tables it refills are `event`, `plink`, `leaf`, `mlink`, `tagxref`, `forumpost`, `attachment`, `backlink`, `cherrypick` and `orphan`, plus the sync bookkeeping tables `unclustered` and `unsent`.
The conformance suite checks this against fossil itself:
- Each scripted case in `conformance/repostate_*_test.go` loads a sequence of artifacts into go-libfossil (one sweep, one artifact at a time, and through rebuild) and into fossil(1). The rows must equal the expectation written from the draft, and fossil's rows must equal it too: its per-artifact crosslink for a crosslink case, `fossil rebuild` on a copy for a rebuild case. The compared tables are `plink`, `tagxref`, `event`, `phantom`, `leaf`, `orphan` and `mlink`.
- `TestRepoStateCorpus` takes real repositories (named in `CONFORMANCE_REPOS`) and derives their tables three ways: `fossil rebuild` on one copy, as the reference; go-libfossil's rebuild on a second; a crosslink sweep over emptied tables on a third. It compares `event`, `leaf`, `mlink`, `phantom`, `plink` and `tagxref` column by column. It runs only when fossil(1) is present.
Where fossil departs from the draft text, the case records it and names the draft change proposed. The requirements are listed in `conformance/requirements/repostate.tsv`.
## Where the code is
- `internal/manifest`: crosslinking (`crosslink.go`), re-derivation (`rederive.go`), the timeline.
- `internal/tag`: tag application and propagation.
- `internal/leaf`: the leaf table.
- `internal/verify`: integrity checks and rebuild.
- `internal/repo`: the schema and the `main-branch` setting.
---
# Sync
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/concepts/sync/
Two repositories exchange artifacts with Fossil's sync protocol: text cards carried in HTTP POST bodies, one request and one reply per round. go-libfossil implements both ends, and talks to stock fossil, zit and other go-libfossil peers. The governing text is `draft-fossil-sync-protocol-00`.
## Messages and cards
A message is a list of cards, one per line: a card name, then space-separated arguments, and for content cards a byte payload after the line (§5). Unknown pragmas are ignored, so a peer can add new ones without breaking old ones (§3.5).
The client POSTs each request to the repository URL (§4). The body is a 4-byte big-endian length followed by a zlib stream (§4.1). A clone reply may be sent uncompressed, because its `cfile` payloads are already compressed.
The cards that move artifacts:
| Card | Meaning | Draft |
|---|---|---|
| `igot HASH` | I hold this artifact. `igot HASH 1` marks it private. | §8.6 |
| `gimme HASH` | Send me this artifact. | §8.6 |
| `file HASH SIZE` | The artifact's bytes, or a delta against another artifact (`file HASH DELTASRC SIZE`). Used by push and pull. | §7.1 |
| `cfile HASH USIZE CSIZE` | The artifact as the sender stores it, compressed, sent verbatim. Used by clone. | §7.2 |
| `push` / `pull` | The operation, with the server and project codes. | §3.2 |
| `clone 3 SEQNO` / `clone_seqno N` | The clone loop's position. | §8.2 |
| `login USER NONCE SIGNATURE` | Authentication. | §6.1 |
| `pragma NAME ...` | Version negotiation and extensions. | §9 |
| `error` / `message` | Errors and notices. | §10 |
Unversioned files travel as `uvigot`, `uvgimme` and `uvfile` (§7.3, §8.5.1). Configuration travels as `reqconfig` and `config` (§9.3).
## Rounds
A session is a loop of rounds. Each round the client says what it holds (`igot`) and what it wants (`gimme`), and sends the content the server asked for in the last reply. The server answers in kind. A `gimme` is never sent for a shunned artifact or one the peer has said it lacks (§8.8).
The client decides when to stop. It issues another round only while the last round made progress: it stored content and still has phantoms (artifacts it knows by name but does not hold), it queued content to send, or one of the unversioned-file or clone conditions holds (§3.7). Phantoms alone do not keep the loop running. The server sends no completion card.
## Clone, pull, push
- **Clone** starts with `clone 3 1`. The server sends its stored blobs as `cfile` cards in batches and a `clone_seqno` saying where to continue; zero ends the transfer and one cleanup round follows (§8.1 to §8.4). Artifacts the rounds leave missing stay phantoms, as with fossil's clone, unless `CloneOpts.PullPhantoms` is set.
- **Pull** sends `pull` each round and receives what it lacks.
- **Push** sends `push` each round and answers the server's `gimme` cards with `file` cards.
- **Sync** sends both.
After artifacts arrive they are [crosslinked](/docs/concepts/repository-state/), so branches, tags and leaves follow from the artifacts each side holds, not from anything sent on the wire (§8.6.1).
## Login
A named user signs each request (§6). `NONCE` is the SHA-1 of the request body after the login line. The shared secret is `SHA1(project-code "/" login "/" password)`, and `SIGNATURE` is `SHA1(NONCE || shared-secret)`. The first clone round carries no login. A request with no login runs as `nobody`. Once the server announces a version that reads it, the login moves to the `x-f-l-c` cookie (§6.5).
The server checks the user's capabilities from its `user` table (§6.6). In a new repository `nobody` may clone and pull but not push; grant more with `Repo.SetCaps`.
## The server is stateless
Each request is handled on its own (§8.9). The server keeps no session between rounds, so any number of clients can sync against one repository, and a server can sit behind any HTTP stack.
```go
repo, err := libfossil.Open("repo.fossil")
if err != nil {
log.Fatal(err)
}
defer repo.Close()
mux := http.NewServeMux()
mux.HandleFunc("/", repo.XferHandler())
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {})
log.Fatal(http.ListenAndServe(":8080", mux))
```
- `Repo.XferHandler()` returns an `http.HandlerFunc` that decodes a request, handles it and encodes the reply. A non-POST request gets a plain page.
- `Repo.ServeHTTP(ctx, addr)` runs a server on `addr` until `ctx` is cancelled. `libfossil repo serve --addr 127.0.0.1:8080` does the same from the command line.
- `Repo.HandleSync(ctx, payload)` and `HandleSyncWithOpts` take and return raw message bytes, for an embedder with its own transport. Over HTTP, pass the `x-f-l-c` cookie in `HandleOpts.LoginCookie` and the client address in `HandleOpts.RemoteAddr`; `XferHandler` and `ServeHTTP` fill both themselves.
## The client
```go
t := libfossil.NewHTTPTransport("https://example.com/repo")
repo, res, err := libfossil.Clone(ctx, "repo.fossil", t, libfossil.CloneOpts{
User: "alice", Password: "secret",
})
sr, err := repo.Sync(ctx, t, libfossil.SyncOpts{
Push: true, Pull: true, User: "alice", Password: "secret",
})
```
`Transport` is one method, `RoundTrip(ctx, payload) ([]byte, error)`, so any byte pipe works. `NewHTTPTransport` takes `WithHTTPClient` for a custom `http.Client`. A transport that also implements `OriginNamer` (`SyncOrigin() string`) keeps the horizon cursor for that peer; see [Horizons](/docs/concepts/horizons/). `Repo.Pull(ctx, url, PullOpts{})` is a pull-only shortcut over HTTP.
`SyncOpts` also turns on unversioned files (`UV`), private artifacts (`Private`, §7.5), fossil's `--share-links` (`ShareLinks`, §9.5) and go-libfossil's own table sync (`XTableSync`). `SyncResult` reports rounds, files and bytes each way, and errors.
The `libfossil` binary clones (`repo clone`) and serves (`repo serve`). Pull, push and sync are library calls.
## Check-in lock
A client that is about to commit can ask the server to hold the parent with `pragma ci-lock` (§9.4). The server records a `ci-lock-` row in its `config` table and reports a conflicting holder with `ci-lock-fail`. A lock goes stale after the repository's `lock-timeout` setting, 60 seconds when unset (`DefaultCkinLockTimeout`). Fossil uses the same row, so a repository served by both shares one lock table.
## Shunned artifacts
A shunned artifact is never requested, and one that arrives is dropped rather than stored. The shun list itself can travel as the `shun` configuration group (§9.3).
## Extensions
Pragmas carry the extensions. Each is ignored by a peer that does not know it.
- [Sparse cones](/docs/concepts/sparse-cones/): `pragma cone`, answered by `cone-served` (`draft-fossil-sparse-cone-00` §6).
- [Horizons](/docs/concepts/horizons/): `horizon-served`, the seed and the cursor (`draft-fossil-horizon-00` §6).
- [Sub-manifests](/docs/concepts/sub-manifests/): `send-submanifests` and `avoid-submanifests` (`draft-fossil-submanifests-00` §7.2).
## Where the code is
- **Client** (`internal/sync/client.go`, `clone.go`, `session.go`): drives the rounds. Supports clone, pull, push and sync, plus unversioned files and config table sync (`client_tablesync.go`).
- **Server** (`internal/sync/handler.go`, `serve_http.go`, `handler_uv.go`, `handler_tablesync.go`): one request in, one reply out.
- **Cards** (`internal/xfer`): encoding and decoding of messages and cards.
- **Check-in lock** (`internal/sync/ckin_lock.go`).
- **Table sync deletes** (`internal/sync/xdelete.go`): an `xdelete` card removes a synced table row, or leaves a tombstone where the row is absent.
- **Public entry points**: `Clone` in `fossil.go`; `Repo.Sync`, `HandleSync`, `XferHandler` and `ServeHTTP` in `repo_sync.go`; `Repo.Pull` in `repo_pull.go`; `NewHTTPTransport` in `transport.go`.
---
# Sub-manifests
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/concepts/sub-manifests/
A check-in manifest has one `F` card per file in the tree, not per changed file. Its size grows with the width of the tree, and every check-in pays it. Sub-manifests split the file list into a tree of smaller artifacts, so a typical check-in rewrites the root manifest and one leaf. The format is `draft-fossil-submanifests-00`.
Two implementations exist: zit writes, reads and serves sub-manifests, and go-libfossil writes and reads them. The two interoperate. Fossil does not implement the draft (§1.3).
## The tree
A **sub-manifest** carries a contiguous slice of a check-in's sorted file list: `F` cards in a leaf, or `S` cards that point to lower sub-manifests in an interior node. A manifest whose files are given by `S` cards is a **tree manifest**. Walking the tree in order gives exactly the file list a flat manifest would carry (§5.1).
```
S src/a.c 5d0c...
```
An `S` card names the first file of the slice and the sub-manifest that holds it (§3.6). Sub-manifests are a ninth artifact type, `submanifest` (§4.1).
Where the boundaries fall depends only on file names. A name starts a new leaf when the SHA3-256 of the name has at least `k` leading zero bits (§6.1). Editing a file changes one leaf. Adding or removing a name can change a few until the boundaries line up again (§6.5). `k` is never written into an artifact, and readers do not need it.
## Settings
Three repository settings control writing. They travel by configuration exchange: a clone asks for them in its cleanup round and adopts them, and a pull does not (§6.3).
| Setting | Meaning | Default |
|---|---|---|
| `enable-submanifests` | Write new check-ins as trees (§8.1). | off |
| `submanifest-k` | The partition parameter, 1 to 12 (§6.3). | unpinned |
| `submanifest-auto-retune` | Let a commit re-pin `k` when it drifts (§6.4). | off |
When emission is first enabled with no pinned `k`, one is selected by the cost in §6.3 over the file list of the main branch's tip (the newest check-in when no check-in is on the main branch), then refined by replaying the last 200 check-ins. A tree with too few files for any `k` to split has no selection, and enabling fails; give `--k` explicitly. A pinned `k` outside 1 to 12 makes commits fall back to flat manifests with a warning.
A re-tune is forward only. The next commit shares only the sub-manifests whose bytes are the same under both values. With `submanifest-auto-retune` on, a commit re-pins `k` when the best candidate costs more than 15% less than the pinned one, zit's margin. With it off, the drift is only logged.
## Command line
```
$ libfossil -R repo.fossil repo submanifests status
enable-submanifests: off
submanifest-k: (unpinned)
submanifest-auto-retune: off
trees in repository: no
$ libfossil -R repo.fossil repo submanifests enable --k 4
$ libfossil -R repo.fossil repo submanifests retune --k 5
$ libfossil -R repo.fossil repo submanifests disable
```
- `enable [--k N]`: write new check-ins as trees, pinning `k` (selected when `--k` is absent). It prints a warning that fossil peers will be refused the trees.
- `retune --k N`: re-pin `k` for later commits.
- `disable`: write flat manifests again. Existing trees stay.
- `status`: show the three settings and whether the repository holds any tree.
The library calls are `Repo.EnableSubmanifests(k)` (0 selects; it returns the pinned `k` and the warning), `Repo.RetuneSubmanifests(k)` and `Repo.DisableSubmanifests()`.
## Sync
A peer that reads sub-manifests says so with `pragma send-submanifests` in every request and reply (§7.2). go-libfossil always sends it, whatever its settings. A peer that has not sent it is refused every sub-manifest and every tree manifest, with an explicit error. Nothing is converted or downgraded. Every fossil release is such a peer, so a repository with trees serves fossil only the history before the first tree.
A server whose `enable-submanifests` is off also sends `pragma avoid-submanifests`. It binds only commits made during that sync session. go-libfossil commits outside a session and pushes afterwards, so the pragma never binds its commits: whether a commit writes a tree is decided by the local `enable-submanifests` alone. The pragma does not stop a server storing a tree pushed to it.
## Things to know before enabling
- **It is a flag day.** Every reader of the repository must understand sub-manifests before any writer emits one (§8). A repository that a fossil peer serves must not enable them.
- **Trees cannot be retired.** Disabling stops new trees; artifact names are immutable, so the ones written stay, and fossil peers are still refused them.
- **Sparse stores hold every tree node.** A [sparse store](/docs/concepts/sparse-cones/) keeps all sub-manifest nodes, so history stays readable without the file content.
## Where the code is
- `internal/manifest`: tree construction (`treebuild.go`), writing and settings (`submanifest_write.go`), reading (`tree.go`).
- `internal/sync/submanifest.go`: the capability gate, both pragmas, and configuration exchange of the settings.
- `repo_submanifest.go` and `cli/repo_submanifests.go`: the public API and command.
---
# Sparse cones
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/concepts/sparse-cones/
A repository does not have to hold everything. A **cone** limits it by path. A [horizon](/docs/concepts/horizons/) limits it by age, and is carried as a member of a cone. Both leave the history intact, and the blobs they skip can be fetched later.
Cones follow `draft-fossil-sparse-cone-00`. They are implemented in step with zit.
## Cones
A cone is a set of repository paths. Each member is a directory subtree or a single file, and each is either an include or an exclude. Members are literal prefixes, not patterns (§3.4).
The longest member that covers a path decides it. An exclude carves a part out of an include, and a deeper include re-includes part of an exclude (§3.3):
```
include src
exclude src/vendor
include src/vendor/keep
```
`src/a.c` is covered. `src/vendor/v.c` is not. `src/vendor/keep/k.c` is covered again.
A cone must have at least one include, at most 255 members, and no exclude that no include covers. Order does not matter; members are sorted and reduced to the smallest set with the same coverage (§3.2).
### Checkout cones and store cones
A **checkout cone** limits the working directory (§4). The repository still holds every file. A sparse checkout writes, scans and tracks only the covered files. A commit carries the uncovered files from its parent unchanged, so it is the same check-in a dense checkout would make. Adding, removing, renaming or reverting an uncovered path fails with `ErrOutsideCone`.
A **store cone** limits the repository itself (§5). Blobs outside the cone are not fetched, and can be dropped by compaction. A checkout opens over a sparse store only if its own cone stays inside the store's.
Manifests, [sub-manifest](/docs/concepts/sub-manifests/) nodes and control artifacts are always held, so the whole history stays readable. Only file content is elided.
### Elided is not damaged
An artifact the store lacks is classified one of three ways (§5.2):
- **transit**: not here yet, and sync will ask for it.
- **shunned**: deliberately refused.
- **elided**: the cone says the store need not hold it.
Only elided is by design. Sync never requests an elided artifact, whether the store is the client or the server, and `verify` reports a store that is complete within its cone as OK. Elision is computed from the cone and never stored, so changing the cone reclassifies. Losing the cone, or one this version cannot read, turns every elided artifact back into a transit one and the store heals toward dense.
## Library usage
### Build a cone
```go
cone, err := libfossil.NewCone(
[]string{"src", "docs"}, // include
[]string{"src/vendor"}, // exclude
)
if err != nil {
log.Fatal(err)
}
fmt.Println(cone.Covers("src/main.go")) // true
covered, member := cone.Decide("src/vendor/x.c") // false, "src/vendor"
```
A `Cone` is immutable. `With`, `Without` and `WithHorizon` return new ones. `Include`, `Exclude`, `Digest`, `String` and `Horizon` read it.
### Sparse checkout
```go
repo, err := libfossil.Open("repo.fossil")
if err != nil {
log.Fatal(err)
}
defer repo.Close()
co, err := repo.CreateCheckout("work", libfossil.CheckoutCreateOpts{Cone: cone})
if err != nil {
log.Fatal(err)
}
defer co.Close()
// Widen, narrow, or pass nil to make the checkout dense.
change, err := co.SetCone(wider)
if err != nil {
log.Fatal(err)
}
fmt.Println(change.Materialized, change.Dematerialized)
```
`SetCone` writes the files the new cone adds and removes the ones it drops from the working directory. The repository keeps them. It refuses, changing nothing, when a file leaving the cone has local changes or a file entering it would overwrite an untracked one. A cone that carries a horizon fails with `ErrHorizonCheckout`.
Over a sparse store, widening needs content the store may not hold. `SetConeContext` takes a `RequiredContentOpts{Peer: transport}` and fetches it. With no peer the widening is refused and nothing changes.
### Clone into a sparse store
```go
t := libfossil.NewHTTPTransport("https://example.com/repo")
repo, res, err := libfossil.Clone(ctx, "repo.fossil", t, libfossil.CloneOpts{
StoreCone: cone,
})
if err != nil {
log.Fatal(err)
}
defer repo.Close()
fmt.Println(res.Elided, res.ConeServed)
```
The result counts:
- `Elided`: artifacts left absent under the cone.
- `ConeServed`: replies where the server acknowledged applying the cone (`pragma cone-served`, §6.3). The other rounds were served densely and filtered locally.
Both are zero for a dense clone. `SyncResult` has the same fields. A server that will not serve a sparse clone refuses it. A cone with a horizon clones a horizon store; see [Horizons](/docs/concepts/horizons/).
### Set, widen and drop on an existing store
```go
// Set or replace, without a peer. Narrowing deletes nothing.
err = repo.SetStoreCone(next)
// Read it.
cur, err := repo.StoreCone()
// Widen, and fetch what that newly covers.
sr, err := repo.SetStoreConeContext(ctx, wider, libfossil.RequiredContentOpts{
Peer: t,
})
// Drop the cone. Fetch everything it elided.
sr, err = repo.SetStoreConeContext(ctx, nil, libfossil.RequiredContentOpts{Peer: t})
// Drop held blobs the cone elides. See Limits.
n, err := repo.CompactStore(ctx)
```
`SetStoreCone` moves no content. Widening makes the newly covered elided artifacts wanted again, and the next pull fetches them. `SetStoreConeContext` does the pull itself through an ordinary cone-scoped sync. With artifacts to fetch and no `Peer`, it refuses, names the count, and leaves the cone unchanged. If the transfer fails after the cone is replaced, the wider policy stays and the next pull heals it. While a store cone is set, a commit carries no R card. `CompactStore` on a dense store fails with `ErrNoStoreCone`.
### List a check-in's files in a cone
```go
files, err := repo.ListFilesIn(rid, cone)
```
`ListFilesIn` returns what `ListFiles` returns, narrowed to the files the cone covers. A nil cone lists everything. On a tree manifest it skips every sub-manifest the cone cannot reach, so it costs the cone plus the path down to it, not the whole tree (§4.5). Over a sparse store it is the listing to use: `ListFiles` walks the whole tree and fails on the first elided node, while `ListFilesIn` never asks for one outside its cone. A cone that reaches a node the store does not hold still fails.
## Command line
### Open a sparse checkout
```
libfossil -R repo.fossil repo open [DIR] --cone src --cone docs --cone-not src/vendor
```
- `--cone=CONE,...`: make a sparse checkout; materialize and track only this path's subtree (repeatable).
- `--cone-not=CONE-NOT,...`: exclude this path's subtree from an enclosing `--cone` (repeatable).
### Edit the checkout cone
All of these take `-d, --dir` for the checkout directory (default `.`).
```
libfossil repo cone show
libfossil repo cone add src/extra
libfossil repo cone add --not src/gen
libfossil repo cone remove src/extra
libfossil repo cone set src docs --not src/vendor
libfossil repo cone dense
libfossil repo cone test src/a.c src/vendor/v.c
```
- `show`: list the cone's members ("dense" when there is none).
- `add `: widen the cone, or with `--not` carve paths out of it.
- `remove `: remove members of either polarity.
- `set `: replace the cone. `--not` takes the exclusions.
- `dense`: drop the cone and materialize every file.
- `test `: classify paths against the cone and name the deciding member. It exits non-zero when any path is elided, so it works as a predicate. `-q` prints nothing and reports through the status only.
`add`, `remove`, `set` and `dense` can need content the store lacks. They take `--remote` (default: the saved last-sync-url), `--sync-user`, `--sync-pass` (never saved), `--sync-private` and `--timeout` (default 10m; 0 disables it).
### Clone a sparse store
```
libfossil repo clone URL repo.fossil --cone src --cone-not src/vendor
```
- `--cone=CONE,...`: clone a sparse store: fetch only this path's subtree (repeatable).
- `--cone-not=CONE-NOT,...`: carve this path out of a `--cone` (repeatable).
`--horizon`, `--leaves`, `--structural` and `--seed-key` combine with these; see [Horizons](/docs/concepts/horizons/).
### Change the store cone
```
libfossil -R repo.fossil repo cone store # show it
libfossil -R repo.fossil repo cone store src docs --not src/vendor
libfossil -R repo.fossil repo cone store --dense
libfossil -R repo.fossil repo cone compact
```
- With no paths, `store` shows the store cone. With paths, it replaces it and fetches what the widening covers. `--not` takes the exclusions.
- `--dense`: drop the store cone, fetching everything it elided.
- `compact`: drop held blobs the store cone elides (irreversible locally).
`store` takes the same `--remote`, `--sync-user`, `--sync-pass`, `--sync-private` and `--timeout` flags as above. Its `--horizon`, `--leaves`, `--structural` and `--no-horizon` flags are described under [Horizons](/docs/concepts/horizons/).
## Interoperability
**zit.** The cone format matches zit's byte for byte, so a cone's digest is the same in both. Sparse sync works in both directions against zit's `horizon-rfc` revision. An implementation that predates horizons refuses a cone that carries one.
**Stock fossil 2.28.** A sparse store must not be served as if it were dense, because the clone would be silently incomplete. A store held sparsely is served only when its `partial-mirror` setting is on (`repo config set partial-mirror on`, `SettingPartialMirror`); otherwise it refuses. The refusal puts the push card ahead of the error so fossil 2.28 aborts instead of carrying on. Fossil cannot read [sub-manifest](/docs/concepts/sub-manifests/) check-ins either, so a repository that uses them is for libfossil and zit peers only.
## Limits
- **Compaction is irreversible locally.** `CompactStore` and `repo cone compact` delete held blobs (§5.4). Widening later fetches them again, which needs a peer that still has them. Structural artifacts, sub-manifest nodes, unsent or private content and the delta bases of retained artifacts are kept.
- **A sparse store lacks file content.** Commands that read an elided blob need a peer to fetch it, or fail.
- **A cone is not an authorization boundary** (§6.5). It limits what a store holds and asks for, not what a peer may read.
---
# Horizons
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/concepts/horizons/
A horizon cuts a store by time. It names a check-in **C**, a rule, and a depth. The check-ins the rule keeps are the **held set**. A blob is elided when no check-in in the held set names it. Structure (manifests, tags, control artifacts) is never elided by a content horizon.
A horizon is a member of a store cone, so everything on [Sparse cones](/docs/concepts/sparse-cones/) about store cones, elided artifacts and compaction applies to it too. The behaviour follows `draft-fossil-horizon-00`, implemented in step with zit.
The held set is computed from the check-in graph the repository holds. It is stored nowhere (§3.1).
## Rules
- **descendants** (the default): C and every check-in descended from it, through any parent, merges included.
- **leaves**: the descendants, plus, for every childless check-in that does not descend from C, every descendant of each check-in where its ancestry meets C's.
A check-in is childless when no other check-in names it as a parent, open or closed. A branch tip merged into another branch has a child, so it is not childless. Under the leaves rule, any two childless check-ins have their merge base in the held set, so merging branches that are still being extended never crosses the horizon. Both rules give a set that includes every child of every check-in in it.
The leaves rule has a cost you should check before using it. An old branch that was never merged forks from C's ancestry near the root, and the rule then holds every descendant of that fork. On fossil-scm.org's repository it held 20,907 of 21,008 check-ins at every depth tried. The descendants rule, with C 10, 100, 426 and 1000 check-ins back from the tip, saves 97, 86, 56 and 41 percent of that history (§3.4). Use the leaves rule when the repository keeps few old unmerged branches.
`repo cone cost ` (`Repo.HorizonCost`) reports what each rule would hold at a check-in, read from a repository that has the history:
```
$ libfossil -R repo.fossil repo cone cost trunk
horizon at trunk
descendants 1 check-ins, 3 blobs, 6 B expanded, 57 B stored
leaves 1 check-ins, 3 blobs, 6 B expanded, 57 B stored
```
## Depth
- **content** (the default): which blobs are held. Every check-in's structure is still held (§4).
- **structural** (`Horizon.Structural`, `--structural`): also cuts the manifests below C (§5). It needs a seed, a control artifact the origin mints so a clone can derive tag and branch state without C's ancestry.
A content horizon keeps all structure and elides only blobs; a structural horizon also leaves out the manifests below C.
## Seeds are never pushed or kept
A seed is private to the clone that holds it, so a go-libfossil origin refuses any pushed control artifact that looks like one (§5.2.3):
- its U card is `go-libfossil`, the label go-libfossil writes on every seed it mints; or
- it carries a `horizon-seed` T card, whatever its U card.
The test runs when the artifact first becomes readable, on its content after any delta is applied. A push that carries a seed the origin can read within that push is refused whole: nothing it carried is stored, and the reply is one error card, `seed refused: a seed is private to the clone that holds it; it is never pushed`, which a go-libfossil pusher reports among its sync errors. A delta whose base has not arrived is stored as any dangling delta is, and withheld, never announced or sent, until something supplies the base: a later push, a pull, or a check-in or tag that writes the same bytes. That supplies only the base, so it is stored as usual, with no error, and the seed is refused then.
A store keeps a seed only when it arrives through a `horizon-seed` announcement, as a horizon clone or pull receives its origin's seed (§5.2.3). One received by pull, clone or sync outside an announcement is dropped as soon as it can be read: not stored, not counted as received, and not asked for again. A delta completed in a later round is refused by the link step instead: kept, but never linked, announced or sent. A control artifact with neither the store's seed label nor a `horizon-seed` card stays ordinary. Any delta a store keeps without its base, pushed or received, is not announced or sent until it can be expanded; a clone records the ones still unreadable when it ends.
A refused seed's hash is recorded in a table of the store's own that no configuration group carries, so no peer pulls it. A refused seed that is stored stays stored, so a delta built on it stays readable, but it is never linked, announced or sent. The store never asks for a refused hash again, and an origin refuses a re-push of it at once.
Two consequences follow:
- Another implementation's label alone is ordinary. A control artifact under zit's `U zit-seed` with no `horizon-seed` T card is stored when pushed or pulled; one that carries a `horizon-seed` card is refused.
- A user whose login is `go-libfossil` cannot push control artifacts (tags, branch moves, closes) to a go-libfossil store, and a store drops ones it receives or relinks, since they carry that login as their U card. go-libfossil does not forbid the login; do not give it to a user.
## The binding card
Every seed names the horizon it was minted for in one card (§5.2.1):
```
T +horizon-seed
```
The card is part of the seed's bytes, so a signature covers it. It is not a tag in force: a clone skips it when it applies the seed, and an origin never derives a `horizon-seed` tag it holds into a seed. It does not count as the one T card a control artifact needs, so with no other tag in force there is still no seed.
A seed's D card is the latest tag event anywhere in the repository, one that applied, propagated or cancelled a tag, rounded up to a whole second (§5.2.2). A tag event takes effect only over an older one and its own row keeps its time, so a cancel, or a closed branch reopened, moves D later, never earlier, and D changes whenever any tag in the repository changes: the next pull re-sends the seed. Dated from the tags in force instead, as go-libfossil and zit did before, cancelling the newest tag moved D back and the clone below refused the honest re-seed as a replay.
Before it holds a seed, signed or not, a clone checks it (§5.2.4):
- it carries exactly one binding card, and that card names the clone's own C and rule;
- its D card is not earlier than that of the seed the clone already holds. A newer tag state never gives an earlier D, so an earlier one is a replay. An equal D is accepted.
A seed that fails either check is refused as a wrongly signed one: the clone falls back to the content horizon, completes, and reports `HorizonFallback`; later pulls keep the content horizon. A deepen discards the held seed first, so the floor starts again at the new C. A held seed that is no longer bound to the store's horizon is discarded when it is next applied, as one that fails its signature is. A seed held by v0.15.0, which carried no binding card, is replaced on the first pull from an origin that binds its seeds; a rebuild before that discards it, and the next pull fetches a bound one.
zit 0.7.1 writes and checks the same card. zit 0.7.2 dates its seeds from the repository's latest tag event too, and zit 0.7.3 keeps the same minted floor, so the two implementations' seeds for one repository are byte-identical up to the U card; interop is tested against zit 0.7.3. Against zit 0.7.2 they still are unless a shun has removed the latest tag events, and against zit 0.7.1 wherever no tag has been cancelled.
## Signed seeds
An origin signs each seed it mints with its `horizon-seed-command`, configured apart from the `clearsign` and `pgp-command` settings that sign check-ins (§5.2.4). An origin with no seed command sends its seeds unsigned, even when it signs check-ins. It keeps one signature per seed, so a seed keeps its name while the tag state it records is unchanged.
A clone given the origin's public key verifies every seed against it: at clone time, on every pull, and when it rebuilds. A seed that is unsigned, or signed by another key, is discarded and the clone falls back to the content horizon. A clone with no key accepts an unsigned seed. The key is the clone's own `horizon-seed-key` setting, never something the origin sends. `CloneOpts.SeedKey` and `repo clone --seed-key` set it when the clone is made; `Repo.SetConfig` refuses a value that is neither an SSH public key nor an OpenPGP fingerprint.
- **SSH:** the origin signs with `ssh-keygen -q -Y sign -n fossil-horizon-seed -f KEY`; the clone's key is the public key line, verified in process. The namespace is `fossil-horizon-seed`, never the check-in namespace `fossilscm`, so a signature on a check-in or tag cannot stand in for a seed's. An origin whose seed command signs in another namespace serves no seed: the request fails.
- **PGP:** the origin signs with a `gpg --clearsign -o` command; the clone's key is the key's 40 or 64 digit fingerprint, verified by `gpg` against the user's keyring. PGP has no namespace and relies on the binding card alone.
```
libfossil -R repo.fossil repo config set horizon-seed-command \
"ssh-keygen -q -Y sign -n fossil-horizon-seed -f ~/.ssh/id_ed25519"
```
An origin signs only for a requester that may read, after the request is authorized, and keeps the signature in its own table; a store that cannot write, such as a read-only mirror, signs again for every request. Setting the origin's own `horizon-seed-key` to its public key makes it check a kept signature before serving it, so replacing the key behind an unchanged seed command takes effect at once. A clone that cannot run its verifier (gpg missing, or timed out) keeps the seed it holds and reports the error rather than falling back. Settings are read from the repository only: go-libfossil reads no global fossil configuration.
[Signing](/docs/concepts/signing/) covers the signing settings and the verifier in more detail.
## Moving the horizon
Moving C earlier and pulling fetches what the deeper horizon keeps. Moving it later keeps what you hold until you compact. A horizon store cannot be a checkout's cone: a horizon governs what the repository holds, not which paths a checkout writes.
## Library usage
### Build a horizon cone
```go
h := libfossil.Horizon{
Checkin: "9f2c...", // full 40 or 64 digit hash
Rule: libfossil.HorizonDescendants, // or HorizonLeaves
Structural: false, // true for the structural cut
}
// Horizon only: every path, cut by time.
hc, err := libfossil.NewHorizonCone(nil, nil, h)
// Horizon plus paths.
hc, err = libfossil.NewHorizonCone([]string{"src"}, nil, h)
got, ok := hc.Horizon() // read it back
noHorizon, err := hc.WithHorizon(nil) // drop it, keep the paths
```
`Checkin` must be a full hash, and it is lower-cased. Dropping the horizon of a horizon-only cone is an error, since nothing would be left; use `SetStoreCone(nil)` to make the store dense.
### Clone into a horizon store
```go
t := libfossil.NewHTTPTransport("https://example.com/repo")
repo, res, err := libfossil.Clone(ctx, "repo.fossil", t, libfossil.CloneOpts{
StoreCone: hc,
SeedKey: "ssh-ed25519 AAAA...", // optional; structural only
})
if err != nil {
log.Fatal(err)
}
defer repo.Close()
fmt.Println(res.Elided, res.HorizonServed, res.StructuralServed, res.HorizonFallback)
```
- `HorizonServed`: replies where the server cut at the horizon (`pragma horizon-served`, §6.2.5). The other rounds were classified locally.
- `StructuralServed`: those served at the structural level.
- `HorizonFallback`: a structural clone kept the content horizon instead (§6.3).
`SyncResult` has the same fields.
### Clone by name or depth
A clone has no history yet, so it cannot turn a branch name or a count into C. It asks the origin, which resolves the name once, in its first reply (§6.2.7):
```go
repo, res, err := libfossil.Clone(ctx, "repo.fossil", t, libfossil.CloneOpts{
Horizon: &libfossil.Horizon{
Checkin: "trunk", // "tip", a branch or tag, or a full hash
Rule: libfossil.HorizonLeaves, // and Structural, as for any horizon
},
HorizonDepth: 100, // step back 100 check-ins along primary parents
})
fmt.Println(res.HorizonResolved, res.HorizonSteps, res.HorizonResolvedLocally)
```
- `tip` is the most recent check-in. A full hash names that check-in. Any other name is the most recent check-in carrying the tag `sym-NAME`, so branches and tags both work. Two check-ins with the same time resolve to the one with the lower full hash, so every origin resolves a name alike. Hash prefixes are not resolved.
- `HorizonDepth` steps back along primary parents. If history runs out first, C is the root it stopped at and `HorizonSteps` is less than the depth asked for.
- The store records only the resolved hash. Every later request of the clone, and every later pull, sync or deepen, sends that hash, never the name.
- A name the origin cannot resolve fails the clone with `horizon name not found: NAME`, and nothing is left on disk.
`CloneOpts.Horizon` takes the same `Horizon` as a store cone, except that its `Checkin` may be a name. `StoreCone` then gives only the path members: giving a horizon on both is an error, as is `HorizonDepth` without `Horizon`. A full hash is sent as a name too: the origin tries it as a check-in hash first and then as a branch or tag, so a branch spelled as 40 hex digits is found. A structural clone by name gets the seed the origin mints for the resolved C.
An origin resolves a name only for a requester that may read or clone. To anyone else it answers as if the request named no horizon, so whether a name exists cannot be learned from its replies.
An origin that does not answer the name (stock fossil, zit 0.7.0 and earlier) serves the clone densely. The clone then resolves the name itself over what it received, classifies locally at the content horizon, and reports `HorizonResolvedLocally`; a structural request falls back to the content horizon and reports `HorizonFallback` (§6.3).
`HorizonHistoryURL`, on both results, is the address the origin gives for querying history below the horizon outside the sync protocol, when it gives one. An origin sets it with `repo config set horizon-history-url https://...` (`SettingHorizonHistoryURL`); only one http or https URL with no spaces or credentials is accepted. `repo clone` prints it as `History:`.
### Deepen, merge across, and verify the seed
Setting, replacing and dropping a horizon on an existing store use `SetStoreCone` and `SetStoreConeContext`, as for any [store cone](/docs/concepts/sparse-cones/).
```go
// Move the horizon down far enough to reach a check-in, which may lie
// below it. C moves to that check-in when it is an ancestor of C, else to
// a common ancestor of the two, asked of the origin when the store cannot
// see one. The rule, the depth and the path members stay.
sr, err := repo.DeepenHorizon(ctx, target, libfossil.RequiredContentOpts{Peer: t})
// Merge. With a Peer, a merge where either side's ancestry crosses a
// structural horizon asks the origin for the base, deepening to it when it
// lies below. Without one it fails with an error matching
// libfossil.ErrBelowHorizon, even when the store finds a base it holds.
mr, err := co.MergeContext(ctx, libfossil.CheckoutMergeOpts{Version: rid},
libfossil.RequiredContentOpts{Peer: t})
// Check the seed against the history it summarizes. This fetches every
// manifest below C and leaves a content-horizon store.
v, err := repo.VerifySeed(ctx, libfossil.RequiredContentOpts{Peer: t})
```
Deepening a structural store discards its seed before it fetches the new one, so the old seed cannot shadow the new one. If the transfer fails, the old horizon and seed are put back (§5.8).
A merge never runs against a base the store does not hold. A bisection whose good end lies below the horizon is refused the same way, naming the horizon and the deepen.
An origin checks each pushed check-in that creates a branch: if the branch it cancels is not the one this origin derives for the parent, the clone's seed was stale, and the whole push is refused with `stale seed: ...` (§5.7). Pull, push a control artifact that cancels the parent's real branch tag on the check-in, and push again.
## Command line
### Clone a horizon store
```
libfossil repo clone URL repo.fossil --horizon
libfossil repo clone URL repo.fossil --horizon trunk --depth 100
libfossil repo clone URL repo.fossil --horizon tip --leaves --cone src
libfossil repo clone URL repo.fossil --horizon release --structural --seed-key "ssh-ed25519 AAAA..."
```
- `--horizon=STRING`: clone a horizon store: elide blobs below this check-in, given as `tip`, a branch or tag, or its full 40 or 64 digit hash.
- `--depth=N`: with `--horizon`, step the check-in back N check-ins along primary parents, stopping at a root.
- `--leaves`: with `--horizon`, also hold every open leaf's ancestry back to the check-in.
- `--structural`: with `--horizon`, also elide the manifests below the check-in, deriving their branch and tag state from the origin's seed.
- `--seed-key=STRING`: the origin's public key, an SSH public key line or an OpenPGP fingerprint. With `--structural`, a seed not signed by it is discarded and the clone falls back to the content horizon. It is kept as the `horizon-seed-key` setting.
`--cone` and `--cone-not` combine with these. The origin resolves a name, since nothing is local yet to resolve it against, and the clone prints the check-in it resolved to as `Horizon at:`. A full hash is sent as a name too, so the origin can also find a branch or tag spelled as one. Later pulls, syncs and deepens send the resolved hash.
### Change the horizon
```
libfossil -R repo.fossil repo cone store --horizon
libfossil -R repo.fossil repo cone store --horizon --leaves
libfossil -R repo.fossil repo cone store --horizon --structural
libfossil -R repo.fossil repo cone store --no-horizon
libfossil -R repo.fossil repo cone deepen
libfossil -R repo.fossil repo cone cost
```
- `--horizon=STRING`: elide blobs below this check-in (name, branch or hash prefix), fetching what a deeper horizon keeps. Unlike `clone`, a local name is fine.
- `--leaves`: with `--horizon`, also hold every open leaf's ancestry back to the check-in.
- `--structural`: with `--horizon`, also elide the manifests below the check-in; the next pull fetches the origin's seed.
- `--no-horizon`: drop the horizon, keeping the path members, and fetch what it elided.
- `cone deepen`: move the horizon down to reach the check-in, fetching what the deeper horizon keeps.
- `cone cost`: report what each rule would hold at the check-in, from this repository.
### Merge and verify
```
libfossil -R repo.fossil repo merge -d DIR
libfossil -R repo.fossil repo verify --seed
```
- `merge`: a merge where either side's ancestry crosses a structural horizon asks the remote for the base, and deepens to it when it lies below. A merge whose ancestry stays above the horizon contacts no peer.
- `verify --seed`: verify the structural seed by fetching the history below the horizon. The store stays a content-horizon store.
`store`, `deepen`, `merge` and `verify` take `--remote` (default: the saved last-sync-url), `--sync-user`, `--sync-pass` (never saved), `--sync-private` and `--timeout` (default 10m; 0 disables it).
## Interoperability
**zit.** The horizon format matches zit's byte for byte. Horizon sync works in both directions against zit's `horizon-rfc` revision.
**Stock fossil 2.28.** A horizon store refuses a dense clone, because the clone would be silently incomplete.
**A horizon store serving horizon peers.** Horizon clones can pull from one another, as in a mesh where each peer pulls from whichever one announced a commit. A store at a horizon serves another horizon store by three rules (§6.2.3, §6.4):
- A horizon store serves a horizon request only when the held set the request names is a subset of its own. A pull or sync at an earlier C, or under a rule that holds more, gets one error card beginning `horizon-not-held:` and nothing else, and the client fails. A push in the same sync is still taken: the store asks for what the peer announced, the client sends it in later rounds, and only then fails. §6.4's "no content" is what the store sends, not what it accepts. A peer at a later C, including one the store does not hold yet, is served. A clone it does not cover is refused the same way, unless `partial-mirror` is on.
- A content-horizon store holds every manifest, so it serves any content-horizon request at or above its own C.
- A structural store does not mint seeds. A structural request is answered at the content level. The requester falls back to the content horizon and reports a `horizon-fallback:` remedy in `SyncResult.Errors` or `CloneResult.Messages`. That names C and the dense origin to pull the manifests below C from, by the store's `horizon-history-url` when one is set. A structural store still refuses a content-horizon clone, since it lacks the manifests below C that the clone would keep.
**zit 0.7.4** fails a pull that a go-libfossil horizon store refuses. As a server, zit 0.7.4 serves a horizon pull at an earlier C than its own store's instead of refusing it.
**zit 0.7.1** resolves horizon names and depths as go-libfossil's origin does. Against zit 0.7.0 and earlier a clone by name falls back to resolving locally.
**A peer that knows nothing of horizons.** A horizon clone from stock Fossil works, by hash or by name. The client asks for nothing below the horizon, records the below-horizon blobs it is sent as elided instead of storing them, and classifies locally. `HorizonServed` stays 0. Stock Fossil can announce a new check-in only inside a cluster. A horizon store holds no clusters, so it requests the cluster members as transit and classifies what arrives (§4.5). Without that a new check-in would not be learned until a later one named it.
## Limits
- **The structural horizon needs an origin that serves it.** Against a peer that does not answer `horizon-served structural` (stock fossil, or a server without a current seed) the clone falls back to the content horizon and reports `HorizonFallback` (§6.3).
- **PGP verification needs gpg.** A PGP-signed seed is verified by running `gpg`, so a clone keyed with a fingerprint needs gpg installed and the key in its keyring; on js/wasm only SSH keys verify. gpg-agent hangs at start under a very large open-file limit, which a macOS shell can set to 2^31 (`ulimit -n`): lower it, to 4096 say, in the environment that signs or verifies with gpg.
- **The horizon cursor needs a transport that names its peer.** The cursor (`pragma horizon-cursor`, §6.2.6) lets an exchange skip re-announcing what both sides hold. The HTTP transport keys it by URL with any password removed. A custom `Transport` keeps one when it also implements `OriginNamer`, whose `SyncOrigin() string` must name the same peer across sessions and carry no credential, since it is written into the repository's config; any other keeps none and announces in full.
- **A horizon is not a checkout cone.** Give a checkout a horizon and you get `ErrHorizonCheckout`.
- **Compaction is irreversible locally.** `repo cone compact` drops the held blobs below the horizon; deepening later needs a peer that still has them.
---
# Signing
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/concepts/signing/
go-libfossil signs two kinds of artifact: check-ins, as fossil and zit sign them, and the seeds an origin mints for [structural horizons](/docs/concepts/horizons/). Each has its own signing command, and both use the same two envelopes, a PGP clear-signed message or the SSH envelope fossil builds around a detached `ssh-keygen` signature (`draft-fossil-artifact-format-00` §4.1.1).
## Settings
| Setting | Constant | Meaning |
|---|---|---|
| `clearsign` | `SettingClearsign` | Sign check-ins. Off by default. |
| `omitsign` | | Fossil's legacy setting. On, it turns check-in signing off again. |
| `pgp-command` | `SettingPGPCommand` | The check-in signing command. Unset, it is fossil's default, `gpg --clearsign -o `. |
| `horizon-seed-command` | `SettingHorizonSeedCommand` | The command an origin signs its horizon seeds with. Unset, seeds go out unsigned. |
| `horizon-seed-key` | `SettingHorizonSeedKey` | The public key a clone verifies its origin's seeds with. |
Settings are read from the repository only. go-libfossil reads no global fossil configuration, so values set with `fossil settings --global` do not apply.
## Signing check-ins
With `clearsign` on, every check-in a commit or a branch creation stores is signed. The command runs as fossil runs it:
- **PGP:** `COMMAND OUTFILE INFILE`, which writes a clear-signed copy of the manifest.
- **SSH:** a command whose name starts with `ssh` runs as `COMMAND INFILE` and writes a detached `INFILE.sig`, which is wrapped in fossil's SSH envelope. The namespace must be `fossilscm`.
```
libfossil -R repo.fossil repo config set clearsign on
libfossil -R repo.fossil repo config set pgp-command \
"ssh-keygen -q -Y sign -n fossilscm -f ~/.ssh/id_ed25519"
```
The command runs through the shell, so `~` and environment variables expand. A signed check-in looks like this:
```
-----BEGIN SSH SIGNED MESSAGE-----
C signed
D 2026-10-23T20:45:21.089
F docs/x.md a09fd4d0...
...
Z 0e57ef44fd0eec16824d0a54ab247953
-----BEGIN SSH SIGNATURE-----
...
-----END SSH SIGNATURE-----
```
The stored bytes are the whole envelope, so the check-in's name is the hash of the signed bytes (§3.1). Readers strip the envelope before parsing, and the `Z` card covers the manifest inside it (§6.2).
To store one check-in unsigned, as fossil's `--nosign` does:
- `libfossil repo ci --nosign` and `libfossil repo branch new --nosign`.
- `CommitOpts.NoSign` (`Repo.Commit`) and `CheckoutCommitOpts.NoSign` (`Checkout.Checkin`).
### What fails
- **The signer fails or times out** (after two minutes). The commit is abandoned and nothing is stored.
- **The signer changes what it signs.** The envelope must strip back to exactly the manifest that was given, as zit checks it. A dash-escaped or rewritten manifest is refused rather than stored under a name its cards do not match.
- **On js/wasm** no external command can run, so signing fails there.
go-libfossil does not verify check-in signatures when it reads them. To check one, take the output of `libfossil repo cat` and run the signer's own verifier: `gpg --verify` on a PGP envelope, or, for SSH, `ssh-keygen -Y verify -n fossilscm -s SIG` with the manifest between the header and the signature block on standard input. The conformance test `TestSignedCheckin` checks that fossil and zit read the signed check-ins, and that a check-in zit signs with the same SSH key verifies here.
## Signing horizon seeds
An origin signs each structural horizon seed it mints with its `horizon-seed-command` (`draft-fossil-horizon-00` §5.2.4). The command is configured apart from check-in signing: `clearsign` and `pgp-command` never sign a seed, and an origin with no seed command sends its seeds unsigned even when it signs check-ins. It follows `pgp-command`'s contract. An SSH signer signs in the namespace `fossil-horizon-seed`, never `fossilscm`, so that a signature on a check-in or tag cannot stand in for a seed's; an origin whose seed command signs in another namespace serves no seed, and the request fails. PGP has no namespace and relies on the seed's [binding card](/docs/concepts/horizons/#the-binding-card) alone.
```
libfossil -R repo.fossil repo config set horizon-seed-command \
"ssh-keygen -q -Y sign -n fossil-horizon-seed -f ~/.ssh/id_ed25519"
```
The origin keeps one signature per seed in its own table, so a seed keeps its name while the tag state it records is unchanged. It signs only for a requester allowed to read, after the request is authorized. A store that cannot write, such as a read-only mirror, signs again for every request.
A clone verifies seeds against its `horizon-seed-key` setting:
```go
repo, res, err := libfossil.Clone(ctx, "repo.fossil", t, libfossil.CloneOpts{
StoreCone: hc, // a cone with a structural horizon
SeedKey: "ssh-ed25519 AAAA... origin",
})
```
```
libfossil repo clone URL repo.fossil --horizon --structural \
--seed-key "ssh-ed25519 AAAA... origin"
```
The key is an SSH public key line, verified in process, or a 40 or 64 digit OpenPGP fingerprint, verified by `gpg` against the user's keyring. `Repo.SetConfig` refuses a `horizon-seed-key` that is neither. The key is the clone's own; the origin never sends it.
The clone verifies every seed it holds: at clone time, on every pull, and when it rebuilds.
| Seed | Key set | Result |
|---|---|---|
| Signed by the key, in namespace `fossil-horizon-seed` for SSH | yes | Held. |
| Unsigned, signed by another key, or SSH-signed in another namespace | yes | Discarded; the store falls back to the content horizon. |
| Any | no | Held. A clone with no key accepts an unsigned seed. |
| Verifier could not run (gpg missing or timed out, or an SSH key in a program that does not link `sshverify`) | yes | The seed already held is kept and the error is reported. A new seed is refused. Nothing falls back. |
Whatever its signature, a seed is also refused when its binding card does not name the clone's own horizon or it is dated before the seed the clone holds; see [the binding card](/docs/concepts/horizons/#the-binding-card).
Setting the origin's own `horizon-seed-key` to its public key makes it check a kept signature before serving it, so replacing the key behind an unchanged seed command takes effect at once.
A signature proves where a seed came from, not that its derivation is right (§5.2.4). `repo verify --seed` (`Repo.VerifySeed`) checks the seed against the history it summarizes.
## Verifying SSH signatures from Go
SSH signatures are verified in process, and the library links that cryptography only into a program that asks for it. A program that verifies seeds against an SSH key imports the `sshverify` package for its side effect:
```go
import (
libfossil "github.com/danmestas/go-libfossil"
_ "github.com/danmestas/go-libfossil/sshverify"
)
```
The `libfossil` command imports it. A program that does not, and never verifies an SSH signature, leaves out `golang.org/x/crypto/ssh` and the ECDSA, Ed25519 and RSA code it pulls in, which matters to a wasm build. Signing needs no import: it runs `ssh-keygen`. PGP keys are verified by `gpg` either way.
Without the import, nothing signed is accepted unchecked. A clone whose `horizon-seed-key` is an SSH key refuses a new seed, and a sync or rebuild fails with an error wrapping `libfossil.ErrSSHVerifierUnavailable`, which names the package to import; the seed already held is kept, and nothing falls back. A seed whose signature is malformed or in another namespace is still discarded, since that needs no cryptography to tell. A clone with no key is unaffected. An origin whose own `horizon-seed-key` is an SSH key cannot check its kept signatures, so it signs each seed afresh for every request.
## Limits
- **PGP verification needs gpg** installed, with the origin's key in the keyring. On js/wasm only SSH keys verify.
- **gpg-agent hangs under a very large open-file limit.** A macOS shell can set `ulimit -n` to 2^31. Lower it, to 4096 say, in the environment that signs or verifies with gpg.
## Where the code is
- `signing.go`: the setting constants.
- `internal/clearsign`: running the signer, the SSH envelope, gpg verification, and the registry the SSH verifier joins.
- `sshverify`: SSH signature verification in process, linked by importing it.
- `internal/manifest/manifest.go`: signing a check-in before it is stored.
- `internal/sync/horizon_seedsign.go` and `internal/horizon/seed`: signing seeds at the origin and verifying them at the clone.
---
# Guides
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/guides/
Task-oriented pages for running go-libfossil in a service, moving from fossil, and testing changes.
- [Production patterns](/docs/guides/production/): errors, contexts, telemetry, handle lifetime, driver choice.
- [Migrating from fossil](/docs/guides/migrating-from-fossil/): what carries over from the fossil binary and what differs.
- [Embedding the CLI](/docs/guides/cli-embedding/): driving `libfossil` as a child process.
- [Extension points](/docs/guides/extension-points/): observers, SQLite drivers and the `db.DB` abstraction.
- [Testing](/docs/guides/testing/): the test suite, deterministic simulation, fuzzing and interop.
---
# Production patterns
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/guides/production/
The [Quickstart](/docs/get-started/quickstart/) uses `log.Fatal` to keep the snippets short. This page shows the patterns you'll actually want when libfossil is embedded in a service.
## Error handling
Don't `log.Fatal` from a long-running process — return errors up to a caller that can log, retry, or fail the request.
```go
func openRepo(path string) (*libfossil.Repo, error) {
repo, err := libfossil.Open(path)
if err != nil {
return nil, fmt.Errorf("open %s: %w", path, err)
}
return repo, nil
}
```
The errors you get back are wrapped Go errors; use `errors.Is` and `errors.As` against the sentinel errors exported from the [`libfossil` package](/docs/reference/sdk/libfossil/api/) when you need to branch on a specific failure (for example `ErrVersionNotFound`, `ErrAmbiguousVersion` or `ErrArtifactNotFound`).
## Context cancellation
`Sync`, `Pull`, and `Clone` accept a `context.Context`. Pass a request-scoped context with a deadline so a stalled remote doesn't tie up your goroutine forever.
```go
ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second)
defer cancel()
if _, err := repo.Sync(ctx, transport, libfossil.SyncOpts{Pull: true}); err != nil {
return fmt.Errorf("sync: %w", err)
}
```
The HTTP transport honors the deadline at the network boundary; the rest of the sync state machine checks `ctx.Done()` between rounds.
## Telemetry
Wire an observer to your Repo's operations to surface sync rounds, checkout events, and per-error events to your existing logging or metrics stack. See [Extension points](/docs/guides/extension-points/) for the full interface, and [`observer/otel`](/docs/reference/sdk/observer/otel/api/) for an OpenTelemetry-ready implementation that emits spans + counters.
```go
import (
libfossil "github.com/danmestas/go-libfossil"
otelobs "github.com/danmestas/go-libfossil/observer/otel"
)
obs := otelobs.NewSyncObserver()
_, err := repo.Sync(ctx, transport, libfossil.SyncOpts{
Pull: true,
Observer: obs,
})
```
`observer/otel` is a separate Go module so the OpenTelemetry SDK doesn't leak into consumers that don't want it.
## Repo handle lifetime
A `*Repo` wraps a SQLite database connection pool. Open it once at service startup, keep it for the lifetime of the process, and `Close()` it during shutdown — don't open and close per request.
```go
type service struct {
repo *libfossil.Repo
}
func newService(path string) (*service, error) {
repo, err := libfossil.Open(path)
if err != nil {
return nil, err
}
return &service{repo: repo}, nil
}
func (s *service) Shutdown() error {
return s.repo.Close()
}
```
Concurrent reads through one Repo are safe — SQLite handles the locking. Writes serialize at the database level (the drivers open write transactions with `BEGIN IMMEDIATE`, and a writer waits up to the 5-second busy timeout for the lock); if your service has heavy write traffic, queue write operations through a single goroutine rather than relying on lock contention to back-pressure.
## Driver selection
The shipped `cmd/libfossil` CLI uses `modernc` by default; `go build -tags test_ncruces ./cmd/libfossil` selects `ncruces`.
For library embedders targeting Linux/macOS/Windows, the default `modernc` driver is the right choice — pure Go, static binaries, no surprises. Switch to `ncruces` only when you're shipping a WASI/browser binary or when you need ncruces-specific behavior. Blank-import exactly one driver:
```go
import _ "github.com/danmestas/go-libfossil/db/driver/ncruces"
// Do not also import:
// _ "github.com/danmestas/go-libfossil/db/driver/modernc"
```
Drivers are mutually exclusive — a process can register exactly one. See [`db`](/docs/reference/sdk/db/api/) for the registry contract.
## Signing check-ins
With the `clearsign` setting on, every check-in that `Repo.Commit`, `Checkout.Checkin` or `repo branch new` stores is signed, as fossil and zit sign them. Fossil's legacy `omitsign` setting, when on, turns signing off again. The signer is the `pgp-command` setting, fossil's contract:
- **PGP:** unset, it is `gpg --clearsign -o `, run as `COMMAND OUTFILE INFILE`.
- **SSH:** a command whose name starts with `ssh`, such as `ssh-keygen -q -Y sign -n fossilscm -f ~/.ssh/id_ed25519`, is run as `COMMAND INFILE` and its detached signature is wrapped in fossil's SSH envelope. The namespace must be `fossilscm`.
```
libfossil -R repo.fossil repo config set clearsign on
libfossil -R repo.fossil repo config set pgp-command "ssh-keygen -q -Y sign -n fossilscm -f ~/.ssh/id_ed25519"
```
The signed text is the artifact, so a signed check-in is named by the hash of the signed bytes. Stock fossil and zit clone and read signed check-ins. A signer that fails abandons the commit. `CommitOpts.NoSign`, `CheckoutCommitOpts.NoSign` and `--nosign` store one check-in unsigned. The command runs through the shell, so `~` and environment variables expand. It runs inside the commit's transaction, so a signer that waits on a passphrase prompt holds the repository's write lock until it returns or two minutes pass. On js/wasm there is no external signer.
These settings sign no horizon seed. An origin signs the seeds it mints with its own `horizon-seed-command`, whose SSH signer must use the namespace `fossil-horizon-seed`; with none set, seeds go out unsigned. A clone verifies them against the `horizon-seed-key` setting, which `repo clone --seed-key` (or `CloneOpts.SeedKey`) sets. See [Signing](/docs/concepts/signing/) for the envelope, verification and seed details.
---
# Migrating from fossil
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/guides/migrating-from-fossil/
This guide is for users of upstream [Fossil SCM](https://fossil-scm.org) (the C
binary distributed by `fossil-scm.org`) who are evaluating `libfossil` — a
pure-Go implementation of the Fossil repository format and sync protocol.
It is intentionally honest about gaps. If a capability is not listed under
"Command coverage" below, assume it is not implemented yet.
For a structural overview of how libfossil is organised — the package layout, storage model, and pluggable axes — see [Architecture](/docs/concepts/architecture/).
## Repo format compatibility
`libfossil` reads and writes the same on-disk `.fossil` SQLite repository
format as upstream Fossil. A repository created by either tool can be opened
by the other at the file level — there is no import/export step and no data
migration is needed. Both implementations share the same blob table layout,
manifest/control artifact encoding, and delta chain representation.
One caveat: features introduced in very recent upstream Fossil releases
(bleeding-edge artifact card types, new Fossil-specific SQL functions used in
triggers) may not yet be parsed by `libfossil`. The tests that compare
against stock fossil run against the pinned release, 2.28. See
[Conformance](/docs/reference/conformance/) and [Testing](/docs/guides/testing/).
## Binary name and invocation
The compiled binary is named `libfossil` (not `fossil`) to avoid `$PATH`
collisions when both tools are installed on the same machine:
```
go install github.com/danmestas/go-libfossil/cmd/libfossil@latest
```
`libfossil` uses a subcommand-grouped syntax: every command lives under the `repo` group,
and individual operations live beneath it. Upstream Fossil uses a flat
command style. Examples:
| Upstream Fossil | libfossil |
|------------------------------|----------------------------------------|
| `fossil clone URL repo.fossil` | `libfossil repo clone URL repo.fossil` |
| `fossil ci -m "msg" file.txt` | `libfossil -R repo.fossil repo ci -m "msg" file.txt` |
| `fossil timeline` | `libfossil -R repo.fossil repo timeline` |
The `-R ` flag selects the repository and is a global flag (before the
`repo` subcommand group).
## Command coverage
The following `repo` subcommands are implemented today. Every entry below
corresponds to a registered Kong command in `cli/repo.go` in the libfossil
source tree. `libfossil --help` lists them all.
### Repository creation
- `repo new` — create a new, empty repository file.
- `repo clone` — clone a remote Fossil server over HTTP(S). With `--cone`
it clones a sparse store; with `--structural` it derives branch and tag
state from the origin's horizon seed; `--seed-key` pins the key the seed
must be signed by. See [Sparse cones](/docs/concepts/sparse-cones/) and
[Horizons](/docs/concepts/horizons/).
### Working tree and checkout
- `repo open` — materialize a checkout in a directory (creates `.fslckout`).
- `repo co` — check out a named version.
- `repo ls` — list files in a version.
- `repo status` — show pending changes in the working directory.
- `repo add` — stage files for addition.
- `repo rm` — stage files for removal.
- `repo rename` — rename a tracked file.
- `repo revert` — undo staging changes.
- `repo ci` — commit the checkout's changes, or only the named files,
with any pending merge as a merge parent. `--nosign` stores one check-in
unsigned; see [Signing](/docs/concepts/signing/).
- `repo cone` — show or edit the checkout's sparse cone (`show`, `add`,
`remove`, `set`, `dense`, `test`) and the repository's store cone
(`store`, `deepen`, `compact`, `cost`).
### History and inspection
- `repo timeline` — repository history (`-n` sets the count, default 20).
- `repo log --path` — the check-ins that changed a path (`fossil finfo`).
- `repo snapshot` — commit one file's bytes onto trunk without a checkout;
see [Embedding the CLI](/docs/guides/cli-embedding/).
- `repo diff` — show changes vs. a version.
- `repo annotate` / `repo blame` — annotate file lines with commit history.
- `repo cat` — output artifact content.
- `repo hash` — hash files (SHA1 or SHA3-256).
- `repo info` — repository statistics.
- `repo resolve` — resolve a symbolic name to a UUID.
- `repo schema` — operate on the synced table schema.
- `repo query` — run ad-hoc SQL against the repository DB.
### Branching and merging
- `repo branch` — `ls`, `new` and `close`.
- `repo tag` — `ls` and `add`.
- `repo merge` — merge a divergent version into the current checkout.
- `repo conflicts` — `ls` lists files holding merge conflict markers and
conflict-fork entries; `show`, `pick`, `merge` and `extract` inspect and
resolve one conflicted file.
- `repo mark-resolved` — resolve a conflict-fork entry. A merge conflict
needs no marking: as in fossil, editing the markers out resolves it.
- `repo bisect` — binary-search commits for bug introductions (`good`,
`bad`, `next`, `skip`, `reset`, `ls`, `status`).
- `repo undo` / `repo redo` — undo/redo the last operation.
- `repo stash` — `save`, `pop`, `apply`, `ls`, `drop` and `clear`.
### Collaboration and admin
- `repo user` — user management.
- `repo invite` — generate an invite token for a user.
- `repo config` — get/set/list repository configuration.
- `repo wiki` — wiki page operations.
- `repo uv` — unversioned file operations.
- `repo extract` — extract files from a version.
- `repo delta` — create/apply delta operations.
- `repo verify` — verify repository integrity.
- `repo serve` — serve the repository over HTTP for clone and sync
(`-P host:port`, default `127.0.0.1:8080`). Access follows the user
table. It is a sync endpoint, not a web UI.
- `repo submanifests` — `enable`, `retune`, `disable` and `status` for
sub-manifest trees; see [Sub-manifests](/docs/concepts/sub-manifests/).
### Known gaps vs. upstream Fossil CLI
The following upstream commands have no `libfossil` equivalent today:
- `fossil ui` / `fossil http` — no web UI or HTTP admin server is shipped.
`repo serve` answers sync and clone requests only; it is the counterpart
of `fossil server` for the sync protocol and nothing else.
- `fossil sync` / `fossil push` / `fossil pull` — there is no CLI verb; sync
is exposed via the Go API (`Repo.Sync`, `Repo.Pull`, `Repo.XferHandler`)
but not as a subcommand.
- `fossil update` — merging a newer trunk into a checkout has no dedicated
CLI command; use `repo co` plus `repo merge` as needed.
- `fossil search` — no CLI front-end for full-text search.
- `fossil fts-config` / FTS index management — no CLI command.
## What's not supported yet
Beyond the gaps listed above, several broader features of upstream Fossil are
not yet exposed by `libfossil`:
- **Web UI / HTTP admin** — no equivalent of `fossil ui`. The sync protocol
handler exists as `Repo.XferHandler()` and `repo serve`, but the HTML UI,
wiki viewer, ticket tracker, and forum are not implemented.
- **Interactive conflict resolution** — there is no interactive merge
tool. `repo conflicts` lists, shows and resolves conflicts by picking a
version or re-merging; otherwise edit out fossil's conflict markers in
your editor.
- **Full-text search CLI** — `internal/search` contains indexing and query
primitives, but no CLI command surfaces them. Use the Go API directly.
- **Ticket and forum subsystems** — no commands or pages. Ticket and forum
artifacts are synced and stored like any other artifact. Tickets have a Go
API: `Repo.TicketAdd` and `Repo.TicketSet` write the change artifact `fossil
ticket add` and `fossil ticket set` write, and `Repo.TicketList` and
`Repo.TicketChanges` read tickets back. Report formats, TH1 ticket scripts
and `private_` fields are not supported. Attachments to tickets, wiki pages
and tech notes have a Go API too: `Repo.AttachmentAdd`,
`Repo.AttachmentDelete`, `Repo.Attachments` and `Repo.AttachmentRead` write
and read the artifacts `fossil attachment add` and the attachment pages do.
- **Mirror / export bridges** to Git, svn, and other systems — not implemented.
If any of these are load-bearing for your workflow, stay on upstream Fossil
for now.
## Interop patterns
Where `libfossil` shines is in environments where the upstream C binary is
inconvenient to run:
- **CGo-free CI pipelines** — `libfossil` links statically with a pure-Go
SQLite driver (`db/driver/modernc`), so clone/sync steps work on stripped
container images and cross-compiled targets with no system dependencies.
- **Embedding in Go services** — the `Repo` handle can be used directly from
a Go program to read history, extract artifacts, or accept pushes over a
custom transport (WebSocket, NATS, libp2p).
- **WASI / browser deployments** — the `db/driver/ncruces` SQLite driver
compiles to WebAssembly, enabling repo access from edge runtimes.
See [Examples](/docs/examples/) for runnable starting points. The
`cmd/libfossil` package is also a short working example: `main.go` wires the
CLI, while `driver_modernc.go` registers the default driver and
`driver_ncruces.go` shows the tagged WebAssembly-capable alternative.
---
# Embedding the CLI
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/guides/cli-embedding/
An application that keeps version history in a Fossil repository can run the `libfossil` binary as a child process: no checkout, no shadow working tree. `repo new` and four more commands make up the interface; their output and exit statuses are stable.
## Commands
```
libfossil -R repo.fossil repo new [--user U]
libfossil -R repo.fossil repo snapshot -m MSG [--user U] --path notes/x.md [--stdin] [--json]
libfossil -R repo.fossil repo log --path notes/x.md [--json] [-n N]
libfossil -R repo.fossil repo cat --at VERSION notes/x.md
libfossil -R repo.fossil repo diff [--json] A B [PATH]
```
**new** creates the repository file, empty and with no check-ins. The path is the `-R` path, or PATH as an argument (`repo new PATH`); when both are given PATH is the file created. With neither it exits 1. `--user U` names the admin user, which gets the setup capability; the default is the process's login name, as `fossil new` uses. The hash policy is `sha3`, fossil's default for a new repository, and the project code is generated. There is no flag for either. On success it prints `created repository: PATH` and a newline to stdout, exits 0, and leaves no file but the repository. Stock fossil 2.28 opens the result, and `fossil info`, `fossil sql` and `fossil hash-policy` read it.
`new` never overwrites. A file, an empty file or a directory already at PATH is refused with exit 1 and `repository already exists: PATH` on stderr, and what was there is untouched; `fossil new` also refuses an existing file, with exit 1. A PATH whose parent directory does not exist is refused with exit 1 and `parent directory does not exist: DIR` on stderr, and nothing is created; `fossil new` fails there with exit 1 as well, with a SQLite error. Create the parent first.
**snapshot** commits one file's bytes onto trunk and carries every other file forward. The bytes come from stdin with `--stdin`, otherwise from `--path` read on disk relative to the working directory. `--path` must be a name fossil accepts for a new file: a clean relative path (`notes/x.md`; not `/x`, `../x`, `./x`, `a//b`, `a/`, `a\b`) in valid UTF-8, with no control character (a byte below 0x20), and not one of the names fossil reserves for its own files (`_FOSSIL_` or `.fslckout`, in any case, with or without a `-wal`, `-shm` or `-journal` suffix, at the top or as the last element of the path). A rejected path exits 1 and says why. When the bytes equal the file at the trunk tip, nothing is committed and the existing tip is reported. The first snapshot into an empty repository becomes the root check-in. A repository that has check-ins but none on its main branch (`trunk`, or the branch the `main-branch` setting names) is refused with exit 1, rather than given a second, unrelated root. The check-in time is the current time.
**log** lists the check-ins that changed the path, newest first, as `fossil finfo` does. `-n` caps the count; the default is all.
**Paths.** `log --path`, `cat --at` and `diff` match a PATH as fossil does inside a check-out. They normalize it: a leading `./`, a trailing `/`, doubled `/` and `/./` or `/x/../` components are dropped, so `./Notes//A.md` and `Notes/A.md/` name `Notes/A.md`. They compare it with the repository's `case-sensitive` setting: when it is off, names that differ only in ASCII letter case are the same file, so `notes/a.md` finds `Notes/A.md`; `log` then merges the history of every name that matches, and `cat` reads the first matching file in the version. Without that setting the default is fossil's platform default: off on macOS and Windows, on elsewhere. Fossil also consults a global setting in `~/.fossil`; go-libfossil does not, so a `case-sensitive` set with `fossil settings --global` is ignored. A repository's names are never folded or rewritten, only matched. What is stored and printed is the name as the check-in recorded it.
A PATH is relative to the repository root, so there is no check-out for it to be outside of. `../x` and an absolute path such as `/x` are not special: they match no file, and exit 2 as not found. Fossil refuses them as outside the check-out and exits 1; go-libfossil keeps one status for every path that names nothing.
The exit status for a PATH or version that is not found is 2 in go-libfossil's own contract. `fossil` exits 1 for the same failures, so a caller that checks for 2 should not expect fossil's status.
A rename ends a name's history, as it does in `fossil finfo`. After `a.md` is renamed to `b.md`, `log --path b.md` lists the check-ins that changed `b.md`, then the rename itself (`action` `renamed`, `prev_name` `a.md`), and stops: it does not continue into `a.md`'s history. To follow a file across renames, run `log` again with `prev_name`. `log --path a.md` lists the rename first, as the check-in that removed `a.md` (`action` `deleted`), then `a.md`'s own changes. That first row is the one difference from `fossil finfo`, which lists only check-ins that hold a version of the file.
**cat** writes the file's bytes as VERSION holds them, as `fossil cat -r` does. VERSION is anything `repo resolve` accepts: `tip`, `trunk`, a branch, a uuid or a unique prefix of one. Without `--at`, `repo cat` still outputs an artifact by uuid.
**diff** with two versions prints the unified diff between them for PATH, or for every file that differs when PATH is omitted. PATH is matched as `fossil diff --from A --to B PATH` matches it: a file of that name, or every file under a directory of that name, in either version. A PATH that matches nothing in either version is not found (exit 2; fossil exits 1 with `not found`); a PATH that matches only unchanged files is an empty diff. With one or no version it compares the working directory, as before.
## Output
Every JSON document is a single line on stdout. Fields are never renamed or removed; new fields may be added, so parse tolerantly. Times are RFC 3339 in UTC with milliseconds, such as `2026-10-19T08:30:00.123Z`. A uuid is the repository's hash of the artifact (40 hex characters for SHA1, 64 for SHA3-256).
`snapshot` prints the check-in uuid and a newline. With `--json`:
```json
{"uuid": "9f2c...", "changed": true}
```
`changed` is false when the bytes already matched the tip and `uuid` is that tip.
`log --json` prints an array, newest first:
```json
[{"uuid": "9f2c...", "time": "2026-10-19T08:30:00.123Z", "user": "dan",
"comment": "edit", "action": "modified", "file_uuid": "ab12...", "prev_name": ""}]
```
`action` is `added`, `modified`, `deleted` or `renamed`. `file_uuid` is absent for `deleted`; `prev_name` is present only for `renamed`. Without `--json`, each line is `UUID10 TIME USER ACTION COMMENT`.
`diff --json A B [PATH]` prints an array, empty when nothing differs:
```json
[{"name": "notes/x.md", "unified": "--- a/notes/x.md\n+++ b/notes/x.md\n@@ ..."}]
```
## Exit statuses
| Status | Meaning |
| --- | --- |
| 0 | Success, including a `snapshot` that changed nothing and a `diff` with no differences. |
| 1 | Any other failure: a bad `--path`, an unreadable repository, an ambiguous version prefix, an I/O error. The message is on stderr. |
| 2 | Not found: the version does not resolve, the file is not in that version (`cat`), the path is in neither version (`diff`), or the path has no history (`log`). Nothing is written to stdout. |
| 80 | Usage error: an unknown command or flag, or a missing required flag or argument. The command-line parser sets this status and writes the error and the command's usage to stderr; stdout stays empty. |
These are the statuses of the `libfossil` binary itself. `go run ./cmd/libfossil` reports any non-zero status as 1, so measure them on a built binary. A combination the parser accepts but the command refuses, such as `diff --json` without two versions, exits 1.
## Concurrency
Processes may snapshot into one repository at the same time. A snapshot takes SQLite's write lock before it reads the trunk tip and keeps it until its check-in commits, so concurrent snapshots run one after another, form one linear trunk, and none loses another's files. A snapshot waits for the lock up to the busy timeout (5 seconds) and then fails with exit 1. Keep the repository outside any synced folder, and open it from one machine at a time.
---
# Extension points
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/guides/extension-points/
libfossil exposes four surfaces for consumers that want to instrument, extend, or replace pieces of the stack:
1. **Observer interfaces** for sync and checkout lifecycle events (`SyncObserver`, `CheckoutObserver`).
2. **SQLite driver registration** (`db.Register`) — pick the SQLite backend at link time.
3. **The `db.DB` abstraction** — an opaque wrapper around `database/sql` with Fossil-specific helpers. Treat it as internal; it is less stable than the observer and driver surfaces.
4. **The `Transport` interface** — one method, `RoundTrip(ctx, payload) ([]byte, error)`, carries a sync round. `Repo.Sync` and `Clone` take any implementation, so sync can run over HTTP, WebSocket, NATS or an in-process pipe. `TransportFunc` adapts a plain function. A transport that also implements `OriginNamer` names its peer, which keeps the horizon cursor between pulls.
## Observer interfaces
Observers are the supported way to attach telemetry, logging, or metrics to a `Repo` without forking or wrapping the public API. Both interfaces are defined in the root `github.com/danmestas/go-libfossil` package and are context-free by design so that they can be reused across synchronous and asynchronous callers.
### `SyncObserver`
Source: `observer.go`.
```go
type SyncObserver interface {
Started(info SessionStart)
RoundStarted(round int)
RoundCompleted(round int, stats RoundStats)
Completed(info SessionEnd)
Error(err error)
HandleStarted(info HandleStart)
HandleCompleted(info HandleEnd)
TableSyncStarted(info TableSyncStart)
TableSyncCompleted(info TableSyncEnd)
}
```
| Method | Fires when |
|---|---|
| `Started` | A client-side sync or clone session begins, before any rounds run. |
| `RoundStarted` | A new sync round starts (there are typically multiple rounds per session). |
| `RoundCompleted` | A sync round finishes — `RoundStats` carries per-round totals. |
| `Completed` | The sync/clone session ends successfully. |
| `Error` | The sync machinery reports a per-error event (protocol or transport). |
| `HandleStarted` | Server-side: an inbound sync/clone HTTP request is accepted. |
| `HandleCompleted` | Server-side: an inbound sync/clone request finishes. |
| `TableSyncStarted` | An extension table sync (for example `peer_registry`) is about to run. |
| `TableSyncCompleted` | An extension table sync finishes. |
libfossil ships `NopSyncObserver()` and `StdoutSyncObserver()` (logs to stderr) as drop-in implementations.
### `CheckoutObserver`
Source: `observer.go`.
```go
type CheckoutObserver interface {
ExtractStarted(info ExtractStart)
ExtractFileCompleted(name string, change UpdateChange)
ExtractCompleted(info ExtractEnd)
ScanStarted(dir string)
ScanCompleted(info ScanEnd)
CommitStarted(info CommitStart)
CommitCompleted(info CommitEnd)
Error(err error)
}
```
| Method | Fires when |
|---|---|
| `ExtractStarted` | An extract or update begins writing files from a checkin into the working tree. |
| `ExtractFileCompleted` | One file has been written — `change` is `ChangeAdded`, `ChangeModified`, or `ChangeDeleted`. |
| `ExtractCompleted` | The extract/update finishes. |
| `ScanStarted` | A working-tree scan (status/change detection) begins for `dir`. |
| `ScanCompleted` | The working-tree scan finishes. |
| `CommitStarted` | A checkin commit begins after staging. |
| `CommitCompleted` | The commit lands — `info.UUID` and `info.RID` identify the new checkin. |
| `Error` | A per-error event fires from the checkout pipeline. |
### Minimal logging observer
```go
package main
import (
"log"
libfossil "github.com/danmestas/go-libfossil"
)
type logSync struct{ libfossil.SyncObserver } // embed nop for defaults
func (logSync) Started(info libfossil.SessionStart) {
log.Printf("sync start project=%s push=%v pull=%v", info.ProjectCode, info.Push, info.Pull)
}
func (logSync) RoundCompleted(round int, s libfossil.RoundStats) {
log.Printf("round %d sent=%d recv=%d", round, s.FilesSent, s.FilesRecvd)
}
func newLogSyncObserver() libfossil.SyncObserver {
return logSync{SyncObserver: libfossil.NopSyncObserver()}
}
```
The embedded `NopSyncObserver()` provides no-op implementations of the methods you do not override, so you only need to define the events you care about. The same pattern applies to `CheckoutObserver` via `NopCheckoutObserver()`; `StdoutCheckoutObserver()` is its stderr logger.
For a full real-world observer that emits spans and counters, see `observer/otel/otel.go`. It implements both interfaces and is the reference you should copy from when wiring up your own telemetry backend.
## Adding an OpenTelemetry (or other) observer
`observer/otel` lives in its own Go module (separate `go.mod`) so that the OpenTelemetry SDK dependency does not leak into consumers that do not want it. To use it, import the subpackage explicitly and hand the observer to each `Repo` operation that accepts one (`SyncOpts.Observer`, `CloneOpts.Observer`, `HandleOpts.Observer`, `CheckoutCreateOpts.Observer`, `CheckoutOpenOpts.Observer`).
```go
import (
libfossil "github.com/danmestas/go-libfossil"
"github.com/danmestas/go-libfossil/observer/otel"
)
obs := otel.NewSyncObserver() // implements libfossil.SyncObserver
_, err := repo.Sync(ctx, transport, libfossil.SyncOpts{
Pull: true,
Observer: obs,
})
```
`otel.NewCheckoutObserver()` returns the matching `CheckoutObserver`. Any third-party telemetry library (Prometheus, Datadog, zap, etc.) should be vendored the same way: a sibling module that imports `libfossil` and implements the observer interfaces.
## SQLite driver interface
Source: `db/register.go`, `db/db.go`, `db/doc.go`.
libfossil never imports a SQLite driver directly. Instead, the `db` package exposes a single registration hook that a driver package calls from its `init()`:
```go
// db/register.go
type DriverConfig struct {
Name string
BuildDSN func(path string, pragmas map[string]string) string
}
func Register(cfg DriverConfig)
```
Each `db.Open(path)` / `db.OpenWith(path, cfg)` call routes through the currently registered driver. Exactly one driver may be registered per process — `Register` panics if called twice, if `Name` is empty, or if `BuildDSN` is nil. Likewise, calling `Open` with no driver registered panics with a message pointing at the driver import paths.
The driver contract is intentionally small:
- **`Name`**: the name passed to `sql.Open`. The driver package is responsible for registering this name with `database/sql` (typically via a blank import of the underlying SQLite library).
- **`BuildDSN(path, pragmas)`**: return a DSN string that opens `path` and applies the given pragmas. libfossil calls `db.DefaultPragmas()` and merges any caller overrides before handing them to `BuildDSN`.
### Built-in drivers
| Driver | Import | When to use |
|---|---|---|
| `modernc` | `_ "github.com/danmestas/go-libfossil/db/driver/modernc"` | Default. Pure Go, no cgo, works on every `GOOS`/`GOARCH` libfossil supports. |
| `ncruces` | `_ "github.com/danmestas/go-libfossil/db/driver/ncruces"` | Uses `github.com/ncruces/go-sqlite3` (WASM-based SQLite). Pick this for WASM builds or environments where you need the ncruces feature set. |
### Registering a custom driver
Use this pattern when you want to wrap an instrumented SQLite build, redirect to a network-attached SQLite, or inject additional pragmas. Model the package after `db/driver/modernc/modernc.go`:
```go
package mydriver
import (
"fmt"
"strings"
"github.com/danmestas/go-libfossil/db"
_ "example.com/my/sqlite" // registers "mysqlite" with database/sql
)
func init() {
db.Register(db.DriverConfig{
Name: "mysqlite",
BuildDSN: buildDSN,
})
}
func buildDSN(path string, pragmas map[string]string) string {
var parts []string
for k, v := range pragmas {
parts = append(parts, fmt.Sprintf("_pragma=%s(%s)", k, v))
}
if len(parts) == 0 {
return path
}
return fmt.Sprintf("file:%s?%s", path, strings.Join(parts, "&"))
}
```
Consumers then blank-import your package (`_ "example.com/mydriver"`) exactly like they would for `modernc` or `ncruces`.
## The `DB` abstraction
`github.com/danmestas/go-libfossil/db` wraps `*sql.DB` with Fossil-aware helpers: DSN construction, default-pragma setup, WAL/nolock handling for WASM, transaction scoping via `DB.WithTx`, and the `Querier` interface (satisfied by both `DB` and `Tx`) so repository code can be written transaction-agnostic. The exported surface is stable for reading — `SqlDB()`, `Path()`, `Driver()`, `Exec`/`Query*` — but the struct itself is treated as internal: the public `Repo` API is the intended extension point. Prefer writing a custom driver (above) over implementing `DB` yourself. See `db/db.go`, `db/config.go`, and `db/scan.go` for the source of truth.
---
# Testing
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/guides/testing/
This guide describes libfossil's test strategy for contributors adding tests or
investigating failures. For the module layout referenced throughout, see
[Architecture](/docs/concepts/architecture/).
## Overview
libfossil's test suite has these layers:
1. **Unit tests** — standard `go test ./...` coverage for individual packages
under the root module and `internal/...`.
2. **Conformance tests** — `./conformance`, which checks the library against
the Fossil Internet-Drafts in `conformance/rfcs/`, requirement by
requirement, and against real `fossil` and `zit` binaries where they are
present. See [Conformance](/docs/reference/conformance/).
3. **DST simulation tests** — deterministic simulation scenarios under
`./dst/...` that drive multiple repos through a virtual clock with fault
injection and invariant checks.
4. **Fuzz targets** — Go native fuzz tests for the parsers and codecs.
5. **Dual-driver matrix** — the whole suite runs against two SQLite drivers
(`modernc` and `ncruces`) to catch driver-specific behavior.
## Running tests
The `Makefile` wraps the common targets:
- `make test` — runs `go test ./... -count=1 -timeout=120s` with the default
`modernc` driver.
- `make test-drivers` — runs the full suite twice:
`go test ./... -count=1 -timeout=120s` followed by
`go test -tags test_ncruces ./... -count=1 -timeout=120s`.
- `make test-otel` — runs the out-of-workspace OTel submodule with
`cd observer/otel && GOWORK=off go test ./... -count=1`.
- `make test-all` — chains `test-drivers`, `test-otel`, and `go build
./cmd/libfossil/`.
- `make ci` — mirrors `.github/workflows/test.yml`. It uses `GOWORK=off`,
runs the packages other than `dst` with a 120-second timeout, runs `./dst/...`
with a 300-second timeout, runs the driver submodule tests, `go vet` and
the CLI build, then repeats the tests under `-tags test_ncruces` and runs
the OTel submodule. Run it before opening a PR. `make release VERSION=vX.Y.Z`
runs it before it tags.
`make setup-hooks` points `core.hooksPath` at `.githooks/`. The installed
pre-commit hook runs both drivers with `-short`, `go vet`, the OTel submodule,
the CLI build, and a check that the generated SDK reference pages are in sync,
in roughly 45 seconds. Skip with `git commit --no-verify` only in emergencies.
Tests that compare against real binaries skip when the binary is missing.
`REQUIRE_FOSSIL_BIN=1` turns a missing `fossil` into a failure, and CI sets
it. `fossil` keeps a global config in `$HOME`, so run those tests with a
scratch `HOME` if you do not want your own touched.
## Fuzzing
There are seven fuzz targets. Each is also a regular test over its seed
corpus, so `go test` runs the seeds without `-fuzz`.
| Target | Package | Exercises |
|---|---|---|
| `FuzzUnified` | `internal/diff` | Unified diff output. |
| `FuzzTableSyncPayload` | `internal/sync` | Extension-table sync payload parsing. |
| `FuzzParseXDelete` | `internal/xfer` | The `xdelete` card parser. |
| `FuzzDecode` | `internal/xfer` | The xfer message decoder and its size limits. |
| `FuzzArtifactRoundTrip` | `conformance` | Artifact parse and re-encode. |
| `FuzzDeltaApply` | `conformance` | Applying arbitrary deltas. |
| `FuzzDeltaRoundTrip` | `conformance` | Delta create then apply. |
Run one with, for example:
```
go test ./conformance -run '^$' -fuzz FuzzDeltaApply -fuzztime 60s
```
CI does not run `-fuzz`, and no Makefile target or script runs the fuzz
targets or a seed sweep. They are run by hand.
## Corpus parity gate
`TestCloneCorpusParity` (`apitest/` package, `apitest/clone_corpus_parity_test.go`) clones one
repository two ways — with the canonical `fossil` binary and with
`libfossil.Clone` — and compares what each derived. It is opt-in:
```
LIBFOSSIL_CORPUS_PARITY=1 go test -run TestCloneCorpusParity -v .
```
Without the variable it skips, so `make test` is unchanged in both runtime and
outcome. It also needs a fossil binary and skips without one (or fails, under
`REQUIRE_FOSSIL_BIN=1`). Budget roughly a second and under a megabyte of temp
space; everything lands in `t.TempDir()` and is removed on exit.
It exists because the per-package parity tests compare our derivation against
fossil's for fixtures built in-test, which only covers the shapes someone
thought to build. Issue #184 — a silent 100% loss of the ticket-change artifact
shape — passed that suite and was found by an external benchmark instead.
The gate asserts two things:
- **Per-`event`-type counts match**, not just the total. This distinction is the
point: on the real Fossil repository the totals read 23,396 vs 27,099, a 14%
shortfall easy to wave off, while the per-type breakdown put ticket events at
96 vs 3,808.
- **Row totals match for `blob`, `event`, `mlink`, `plink`, `tagxref` and
`leaf`**, reported in both directions. A surplus matters as much as a
shortfall — our `tagxref` ran +17,119 over fossil's on the real repository,
which is what writing a tag without finishing the work it marks looks like.
Failures name the table or type and the delta, e.g.
`event type "t": got 0, want 10 (-10)`.
The corpus is built by the canonical binary at test time rather than
downloaded: seconds and kilobytes instead of the ~10 minutes and ~1 GB the real
Fossil repository costs, no network dependency, and contents that are a
property of the test rather than of whatever a remote server holds today. It
covers check-ins (including a branch and a merge), ticket creations and
updates, a wiki page, a technote, an attachment, and a tag control artifact.
`requireCorpusShapes` classifies every blob by its card set and fails if any
shape falls below its expected floor. Without that check a corpus that quietly
stopped producing ticket artifacts would let the gate pass while covering
nothing — the exact blind spot it exists to remove.
Not covered: forum posts (`f` events), because fossil exposes no CLI to create
them; the scale-dependent behaviour of a large repository; and per-row content
comparison — this gate counts rows, and `internal/manifest` diffs them.
## Driver matrix
libfossil abstracts SQLite behind `db/` and two driver submodules. Both must
pass identical tests:
- **`modernc`** (default) — pure-Go port of SQLite. Used by every build
that doesn't set a tag. Registered via `internal/testdriver/modernc.go`
under `//go:build !test_ncruces && !test_mattn`.
- **`ncruces`** (wasm-capable) — selected with `-tags test_ncruces`, suitable
for WASM targets. Registered via `internal/testdriver/ncruces.go` under
`//go:build test_ncruces`.
GitHub Actions (`.github/workflows/test.yml`) runs `test`, `test-ncruces`,
`build-wasm`, `build-tinygo`, `test-otel` and `test-docs` as parallel jobs. The first two
install the pinned fossil 2.28, build every package, then run the unit tests,
DST and the driver submodule tests under modernc and ncruces respectively.
`build-wasm` compiles the packages for `js/wasm` with the ncruces tag,
`build-tinygo` builds `examples/tinygo` with TinyGo 0.43, and
`test-docs` builds this site with Hugo. For the canonical local invocation,
use `make test-drivers` or `make ci` rather than hand-rolling tag flags.
## DST (Deterministic Simulation Testing)
DST is inspired by FoundationDB and TigerBeetle: run many repos in a single
process under a simulated clock, inject faults, and replay a failing seed
deterministically. The harness lives in `dst/simulator.go` and
`dst/invariants.go`.
### Seeds and reproducibility
`dst.SimConfig.Seed` drives every random source used by the simulator: the
event queue, network fault decisions, and a `SeededBuggify` PRNG that gates
fault-injection sites. Any failure on seed N reproduces exactly on seed N.
`dst/scenario_test.go` exposes three flags driving `TestDST`:
```
go test ./dst -run 'TestDST$' -seed=42 -level=hostile -steps=10000
```
`TestDST` skips unless at least one flag is set. It builds a master repo
with 50 artifacts, 5 unversioned files and 3 private blobs, starts a
simulator with 3 leaves, and checks safety invariants as it runs.
- `-seed=` — seed passed to `SimConfig.Seed`. In `TestDST`, 0 means
seed 1; other scenarios use their own fixed seeds.
- `-level={normal,adversarial,hostile}` — picks a `severity` struct setting
`DropRate` and `Buggify`: `normal` is no drops and no BUGGIFY,
`adversarial` is 10% drops with BUGGIFY, `hostile` is 20% drops with
BUGGIFY. An empty or unknown level is `normal`.
- `-steps=` — caps the number of events processed (default 10000 in
`TestDST`).
Seed-parameterized scenarios (e.g. `TestCloneDSTSeedSweep` in
`dst/clone_test.go`, which runs seeds 0 to 19) loop over seeds via `t.Run(fmt.Sprintf("seed_%d", seed),
...)`. To reproduce a single sub-seed: `go test ./dst -run
TestCloneDSTSeedSweep/seed_7 -v`.
### Invariants
`dst/invariants.go` defines per-node and cross-node checks. `Simulator.CheckSafety`
runs the per-node set after every `SafetyCheckInterval` steps; convergence
checks run once the sim has quiesced:
- **Safety (anytime):** `CheckBlobIntegrity` (UUID matches hash of expanded
content), `CheckDeltaChains` (`delta.srcid` resolves to a blob),
`CheckNoOrphanPhantoms`, `CheckUVIntegrity`, `CheckTableSyncIntegrity`.
`CheckTagxrefIntegrity` is a separate check that scenarios call
directly.
- **Convergence (after fault-free sync):** `CheckConvergence` (leaves match
master blob-for-blob), `CheckSubsetOf`, `CheckUVConvergence`,
`CheckTableSyncConvergence`, `CheckTombstoneConvergence`.
Failures return a `*InvariantError{Invariant, NodeID, Detail}` naming the
failing invariant and the offending node. The scenarios add the seed and
level to the failure message, so a log line is enough to replay the run.
The checks run with BUGGIFY suspended, so they see what is persisted and
not faults injected into their own reads.
## BUGGIFY
BUGGIFY marks code points where faults can be injected — shrunk send
budgets, dropped or rejected cards, bad nonces, byte-flipped content. About
twenty sites exist in `internal/sync`, `internal/blob` and
`internal/content`. In production
the check is a single boolean read; under DST the seeded PRNG decides whether
to fire.
Two styles appear in the tree:
1. **Process-global, used outside the sync path.** `simio.Buggify(probability)`
consults a process-global state toggled by `simio.EnableBuggify(seed)` /
`DisableBuggify()` (see `simio/buggify.go`). Example from
`internal/content/content.go`:
```go
// BUGGIFY: flip a byte in expanded content to exercise the hash check
// below under DST.
if check && simio.Buggify(0.01) && len(content) > 0 {
corrupted := make([]byte, len(content))
copy(corrupted, content)
corrupted[0] ^= 0xFF
content = corrupted
verified = false
}
```
2. **Scoped, passed through session options.** `BuggifyChecker` is an interface
(`Check(site string, probability float64) bool`) defined in
`internal/sync/session.go` and re-exported from the root as
`libfossil.BuggifyChecker`. Sync code threads a checker through so each site
has a stable name:
```go
// BUGGIFY: drop the last file card to simulate partial send.
if s.opts.Buggify != nil && s.opts.Buggify.Check("sync.buildFileCards.skip", 0.05) && len(cards) > 0 {
cards = cards[:len(cards)-1]
}
```
`dst.SeededBuggify` in `dst/simulator.go` implements `BuggifyChecker` with a
seeded `*rand.Rand` (it ignores the site name). `Simulator.New` enables both styles when `SimConfig.Buggify`
is true, wiring independent PRNG streams into the client, the server-side
`MockFossil`, and the global `simio` state.
## Interop with fossil 2.28 and zit
Two other implementations check libfossil's output.
**Fossil.** CI downloads the official fossil 2.28 release tarball
(`FOSSIL_VERSION` in `.github/workflows/test.yml`) and verifies its SHA-256
and its reported version, so every run compares against the same build.
Tests in `conformance/`, `cli/`, `apitest/` and the root package create repositories with
stock `fossil`, have it open and verify what libfossil wrote, and compare
the rows libfossil derives with the rows fossil derives. Locally they use
the `fossil` on `PATH`; `fossil version` should report 2.28.
**zit.** zit is a Zig reimplementation of Fossil. The `TestZit*` tests in
`conformance/*zit*_test.go` (20 of them) cover the newer drafts: sub-manifest
trees (`zit_interop_test.go`, `zit_writer_test.go`), sparse cones and sparse
stores, and horizons (serve, structural seeds, fallback, push check, cursor,
nodes, history URL and merge base). Each runs both ways where it can:
go-libfossil clones from and pushes to `zit serve`, and zit clones from and
pushes to go-libfossil's handler. They need a `zit` on `PATH` (or `ZIT_BIN`)
at a recent enough commit and skip otherwise. `REQUIRE_ZIT_BIN=1` makes a
missing or older zit a failure. CI has no zit, so there the bundle tests
(`TestZitTreeBundle`, `TestZitWriterBundle`) read recorded copies of what
zit wrote from `conformance/testdata/`. `ZIT_REGENERATE=1` rewrites those
copies from a live zit.
Signed check-ins are covered the same way: `TestSignedCheckin` has fossil
and zit clone and read libfossil's signed check-ins, and checks that a
check-in zit signs verifies in libfossil.
## Writing new tests
- Prefer table-driven tests in unit code; include success and failure rows.
- If you touch sync, merge, clone, or checkin, add a DST scenario under
`dst/` and run it across a seed range (see `TestCloneDSTSeedSweep` for the
pattern).
- Mark any new fault-sensitive code point with BUGGIFY. Use the scoped
`BuggifyChecker` style when the site lives inside the sync session; reserve
`simio.Buggify` for code that runs outside an explicit session scope.
- Assert invariants. If the new behavior warrants one, add it to
`dst/invariants.go` and wire it into `CheckSafety` or a convergence helper.
- Confirm tests pass under both drivers (`make test-drivers`) before
requesting review.
---
# Reference
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/
Reference material, most of it generated from the source.
- [SDK](/docs/reference/sdk/): the Go API, one page per package, generated by gomarkdoc from doc comments.
- [CLI](/docs/reference/cli/): every `libfossil` command, generated from the binary's `--help` output.
- [Settings](/docs/reference/settings/): the repository settings go-libfossil reads.
- [Conformance](/docs/reference/conformance/): every requirement of every draft and its status, generated from `TestCoverage`.
---
# SDK
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/sdk/
Auto-generated Go API reference. One page per package, generated by [gomarkdoc](https://github.com/princjef/gomarkdoc) from doc comments.
Run `make docs-gen-sdk` to regenerate. The pre-commit hook fails if the working tree drifts.
---
# cli
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/sdk/cli/api/
# cli
```go
import "github.com/danmestas/go-libfossil/cli"
```
## Index
- [Constants](<#constants>)
- [Variables](<#variables>)
- [func Version\(\) string](<#Version>)
- [type ExitError](<#ExitError>)
- [func \(e \*ExitError\) Error\(\) string](<#ExitError.Error>)
- [func \(e \*ExitError\) Unwrap\(\) error](<#ExitError.Unwrap>)
- [type Globals](<#Globals>)
- [func \(g \*Globals\) OpenRepo\(\) \(\*libfossil.Repo, error\)](<#Globals.OpenRepo>)
- [type RecoveryFlags](<#RecoveryFlags>)
- [type RepoAddCmd](<#RepoAddCmd>)
- [func \(c \*RepoAddCmd\) Run\(g \*Globals\) \(err error\)](<#RepoAddCmd.Run>)
- [type RepoAnnotateCmd](<#RepoAnnotateCmd>)
- [func \(c \*RepoAnnotateCmd\) Run\(g \*Globals\) error](<#RepoAnnotateCmd.Run>)
- [type RepoBisectBadCmd](<#RepoBisectBadCmd>)
- [func \(c \*RepoBisectBadCmd\) Run\(g \*Globals\) error](<#RepoBisectBadCmd.Run>)
- [type RepoBisectCmd](<#RepoBisectCmd>)
- [type RepoBisectGoodCmd](<#RepoBisectGoodCmd>)
- [func \(c \*RepoBisectGoodCmd\) Run\(g \*Globals\) error](<#RepoBisectGoodCmd.Run>)
- [type RepoBisectLsCmd](<#RepoBisectLsCmd>)
- [func \(c \*RepoBisectLsCmd\) Run\(g \*Globals\) error](<#RepoBisectLsCmd.Run>)
- [type RepoBisectNextCmd](<#RepoBisectNextCmd>)
- [func \(c \*RepoBisectNextCmd\) Run\(g \*Globals\) error](<#RepoBisectNextCmd.Run>)
- [type RepoBisectResetCmd](<#RepoBisectResetCmd>)
- [func \(c \*RepoBisectResetCmd\) Run\(g \*Globals\) error](<#RepoBisectResetCmd.Run>)
- [type RepoBisectSkipCmd](<#RepoBisectSkipCmd>)
- [func \(c \*RepoBisectSkipCmd\) Run\(g \*Globals\) error](<#RepoBisectSkipCmd.Run>)
- [type RepoBisectStatusCmd](<#RepoBisectStatusCmd>)
- [func \(c \*RepoBisectStatusCmd\) Run\(g \*Globals\) error](<#RepoBisectStatusCmd.Run>)
- [type RepoBlameCmd](<#RepoBlameCmd>)
- [type RepoBranchCloseCmd](<#RepoBranchCloseCmd>)
- [func \(c \*RepoBranchCloseCmd\) Run\(g \*Globals\) error](<#RepoBranchCloseCmd.Run>)
- [type RepoBranchCmd](<#RepoBranchCmd>)
- [type RepoBranchLsCmd](<#RepoBranchLsCmd>)
- [func \(c \*RepoBranchLsCmd\) Run\(g \*Globals\) error](<#RepoBranchLsCmd.Run>)
- [type RepoBranchNewCmd](<#RepoBranchNewCmd>)
- [func \(c \*RepoBranchNewCmd\) Run\(g \*Globals\) error](<#RepoBranchNewCmd.Run>)
- [type RepoCatCmd](<#RepoCatCmd>)
- [func \(c \*RepoCatCmd\) Run\(g \*Globals\) error](<#RepoCatCmd.Run>)
- [type RepoCiCmd](<#RepoCiCmd>)
- [func \(c \*RepoCiCmd\) Run\(g \*Globals\) \(err error\)](<#RepoCiCmd.Run>)
- [type RepoCloneCmd](<#RepoCloneCmd>)
- [func \(c \*RepoCloneCmd\) Run\(g \*Globals\) error](<#RepoCloneCmd.Run>)
- [type RepoCmd](<#RepoCmd>)
- [type RepoCoCmd](<#RepoCoCmd>)
- [func \(c \*RepoCoCmd\) Run\(g \*Globals\) error](<#RepoCoCmd.Run>)
- [type RepoConeAddCmd](<#RepoConeAddCmd>)
- [func \(c \*RepoConeAddCmd\) Run\(g \*Globals\) error](<#RepoConeAddCmd.Run>)
- [type RepoConeCmd](<#RepoConeCmd>)
- [type RepoConeCompactCmd](<#RepoConeCompactCmd>)
- [func \(c \*RepoConeCompactCmd\) Run\(g \*Globals\) \(err error\)](<#RepoConeCompactCmd.Run>)
- [type RepoConeCostCmd](<#RepoConeCostCmd>)
- [func \(c \*RepoConeCostCmd\) Run\(g \*Globals\) \(err error\)](<#RepoConeCostCmd.Run>)
- [type RepoConeDeepenCmd](<#RepoConeDeepenCmd>)
- [func \(c \*RepoConeDeepenCmd\) Run\(g \*Globals\) \(err error\)](<#RepoConeDeepenCmd.Run>)
- [type RepoConeDenseCmd](<#RepoConeDenseCmd>)
- [func \(c \*RepoConeDenseCmd\) Run\(g \*Globals\) error](<#RepoConeDenseCmd.Run>)
- [type RepoConeRemoveCmd](<#RepoConeRemoveCmd>)
- [func \(c \*RepoConeRemoveCmd\) Run\(g \*Globals\) error](<#RepoConeRemoveCmd.Run>)
- [type RepoConeSetCmd](<#RepoConeSetCmd>)
- [func \(c \*RepoConeSetCmd\) Run\(g \*Globals\) error](<#RepoConeSetCmd.Run>)
- [type RepoConeShowCmd](<#RepoConeShowCmd>)
- [func \(c \*RepoConeShowCmd\) Run\(g \*Globals\) \(err error\)](<#RepoConeShowCmd.Run>)
- [type RepoConeStoreCmd](<#RepoConeStoreCmd>)
- [func \(c \*RepoConeStoreCmd\) Run\(g \*Globals\) \(err error\)](<#RepoConeStoreCmd.Run>)
- [type RepoConeTestCmd](<#RepoConeTestCmd>)
- [func \(c \*RepoConeTestCmd\) Run\(g \*Globals\) \(err error\)](<#RepoConeTestCmd.Run>)
- [type RepoConfigCmd](<#RepoConfigCmd>)
- [type RepoConfigGetCmd](<#RepoConfigGetCmd>)
- [func \(c \*RepoConfigGetCmd\) Run\(g \*Globals\) error](<#RepoConfigGetCmd.Run>)
- [type RepoConfigLsCmd](<#RepoConfigLsCmd>)
- [func \(c \*RepoConfigLsCmd\) Run\(g \*Globals\) error](<#RepoConfigLsCmd.Run>)
- [type RepoConfigSetCmd](<#RepoConfigSetCmd>)
- [func \(c \*RepoConfigSetCmd\) Run\(g \*Globals\) error](<#RepoConfigSetCmd.Run>)
- [type RepoConflictsCmd](<#RepoConflictsCmd>)
- [type RepoConflictsExtractCmd](<#RepoConflictsExtractCmd>)
- [func \(c \*RepoConflictsExtractCmd\) Run\(g \*Globals\) error](<#RepoConflictsExtractCmd.Run>)
- [type RepoConflictsLsCmd](<#RepoConflictsLsCmd>)
- [func \(c \*RepoConflictsLsCmd\) Run\(g \*Globals\) error](<#RepoConflictsLsCmd.Run>)
- [type RepoConflictsMergeCmd](<#RepoConflictsMergeCmd>)
- [func \(c \*RepoConflictsMergeCmd\) Run\(g \*Globals\) error](<#RepoConflictsMergeCmd.Run>)
- [type RepoConflictsPickCmd](<#RepoConflictsPickCmd>)
- [func \(c \*RepoConflictsPickCmd\) Run\(g \*Globals\) error](<#RepoConflictsPickCmd.Run>)
- [type RepoConflictsShowCmd](<#RepoConflictsShowCmd>)
- [func \(c \*RepoConflictsShowCmd\) Run\(g \*Globals\) error](<#RepoConflictsShowCmd.Run>)
- [type RepoDeltaApplyCmd](<#RepoDeltaApplyCmd>)
- [func \(c \*RepoDeltaApplyCmd\) Run\(g \*Globals\) error](<#RepoDeltaApplyCmd.Run>)
- [type RepoDeltaCmd](<#RepoDeltaCmd>)
- [type RepoDeltaCreateCmd](<#RepoDeltaCreateCmd>)
- [func \(c \*RepoDeltaCreateCmd\) Run\(g \*Globals\) error](<#RepoDeltaCreateCmd.Run>)
- [type RepoDiffCmd](<#RepoDiffCmd>)
- [func \(c \*RepoDiffCmd\) Run\(g \*Globals\) error](<#RepoDiffCmd.Run>)
- [type RepoExtractCmd](<#RepoExtractCmd>)
- [func \(c \*RepoExtractCmd\) Run\(g \*Globals\) error](<#RepoExtractCmd.Run>)
- [type RepoHashCmd](<#RepoHashCmd>)
- [func \(c \*RepoHashCmd\) Run\(g \*Globals\) error](<#RepoHashCmd.Run>)
- [type RepoInfoCmd](<#RepoInfoCmd>)
- [func \(c \*RepoInfoCmd\) Run\(g \*Globals\) error](<#RepoInfoCmd.Run>)
- [type RepoInviteCmd](<#RepoInviteCmd>)
- [func \(c \*RepoInviteCmd\) Run\(g \*Globals\) error](<#RepoInviteCmd.Run>)
- [type RepoLogCmd](<#RepoLogCmd>)
- [func \(c \*RepoLogCmd\) Run\(g \*Globals\) \(err error\)](<#RepoLogCmd.Run>)
- [type RepoLsCmd](<#RepoLsCmd>)
- [func \(c \*RepoLsCmd\) Run\(g \*Globals\) error](<#RepoLsCmd.Run>)
- [type RepoMergeCmd](<#RepoMergeCmd>)
- [func \(c \*RepoMergeCmd\) Run\(g \*Globals\) \(err error\)](<#RepoMergeCmd.Run>)
- [type RepoMergeResolveCmd](<#RepoMergeResolveCmd>)
- [func \(c \*RepoMergeResolveCmd\) Run\(g \*Globals\) \(err error\)](<#RepoMergeResolveCmd.Run>)
- [type RepoNewCmd](<#RepoNewCmd>)
- [func \(c \*RepoNewCmd\) Run\(g \*Globals\) error](<#RepoNewCmd.Run>)
- [type RepoOpenCmd](<#RepoOpenCmd>)
- [func \(c \*RepoOpenCmd\) Run\(g \*Globals\) error](<#RepoOpenCmd.Run>)
- [type RepoQueryCmd](<#RepoQueryCmd>)
- [func \(c \*RepoQueryCmd\) Run\(g \*Globals\) error](<#RepoQueryCmd.Run>)
- [type RepoRedoCmd](<#RepoRedoCmd>)
- [func \(c \*RepoRedoCmd\) Run\(g \*Globals\) error](<#RepoRedoCmd.Run>)
- [type RepoRenameCmd](<#RepoRenameCmd>)
- [func \(c \*RepoRenameCmd\) Run\(g \*Globals\) \(err error\)](<#RepoRenameCmd.Run>)
- [type RepoResolveCmd](<#RepoResolveCmd>)
- [func \(c \*RepoResolveCmd\) Run\(g \*Globals\) error](<#RepoResolveCmd.Run>)
- [type RepoRevertCmd](<#RepoRevertCmd>)
- [func \(c \*RepoRevertCmd\) Run\(g \*Globals\) \(err error\)](<#RepoRevertCmd.Run>)
- [type RepoRmCmd](<#RepoRmCmd>)
- [func \(c \*RepoRmCmd\) Run\(g \*Globals\) \(err error\)](<#RepoRmCmd.Run>)
- [type RepoSchemaAddCmd](<#RepoSchemaAddCmd>)
- [func \(c \*RepoSchemaAddCmd\) Run\(g \*Globals\) error](<#RepoSchemaAddCmd.Run>)
- [type RepoSchemaCmd](<#RepoSchemaCmd>)
- [type RepoSchemaListCmd](<#RepoSchemaListCmd>)
- [func \(c \*RepoSchemaListCmd\) Run\(g \*Globals\) error](<#RepoSchemaListCmd.Run>)
- [type RepoSchemaRemoveCmd](<#RepoSchemaRemoveCmd>)
- [func \(c \*RepoSchemaRemoveCmd\) Run\(g \*Globals\) error](<#RepoSchemaRemoveCmd.Run>)
- [type RepoSchemaShowCmd](<#RepoSchemaShowCmd>)
- [func \(c \*RepoSchemaShowCmd\) Run\(g \*Globals\) error](<#RepoSchemaShowCmd.Run>)
- [type RepoServeCmd](<#RepoServeCmd>)
- [func \(c \*RepoServeCmd\) Run\(g \*Globals\) error](<#RepoServeCmd.Run>)
- [type RepoSnapshotCmd](<#RepoSnapshotCmd>)
- [func \(c \*RepoSnapshotCmd\) Run\(g \*Globals\) \(err error\)](<#RepoSnapshotCmd.Run>)
- [type RepoStashApplyCmd](<#RepoStashApplyCmd>)
- [func \(c \*RepoStashApplyCmd\) Run\(g \*Globals\) error](<#RepoStashApplyCmd.Run>)
- [type RepoStashClearCmd](<#RepoStashClearCmd>)
- [func \(c \*RepoStashClearCmd\) Run\(g \*Globals\) error](<#RepoStashClearCmd.Run>)
- [type RepoStashCmd](<#RepoStashCmd>)
- [type RepoStashDropCmd](<#RepoStashDropCmd>)
- [func \(c \*RepoStashDropCmd\) Run\(g \*Globals\) error](<#RepoStashDropCmd.Run>)
- [type RepoStashLsCmd](<#RepoStashLsCmd>)
- [func \(c \*RepoStashLsCmd\) Run\(g \*Globals\) error](<#RepoStashLsCmd.Run>)
- [type RepoStashPopCmd](<#RepoStashPopCmd>)
- [func \(c \*RepoStashPopCmd\) Run\(g \*Globals\) error](<#RepoStashPopCmd.Run>)
- [type RepoStashSaveCmd](<#RepoStashSaveCmd>)
- [func \(c \*RepoStashSaveCmd\) Run\(g \*Globals\) error](<#RepoStashSaveCmd.Run>)
- [type RepoStatusCmd](<#RepoStatusCmd>)
- [func \(c \*RepoStatusCmd\) Run\(g \*Globals\) error](<#RepoStatusCmd.Run>)
- [type RepoSubmanifestsCmd](<#RepoSubmanifestsCmd>)
- [type RepoSubmanifestsDisableCmd](<#RepoSubmanifestsDisableCmd>)
- [func \(c \*RepoSubmanifestsDisableCmd\) Run\(g \*Globals\) error](<#RepoSubmanifestsDisableCmd.Run>)
- [type RepoSubmanifestsEnableCmd](<#RepoSubmanifestsEnableCmd>)
- [func \(c \*RepoSubmanifestsEnableCmd\) Run\(g \*Globals\) error](<#RepoSubmanifestsEnableCmd.Run>)
- [type RepoSubmanifestsRetuneCmd](<#RepoSubmanifestsRetuneCmd>)
- [func \(c \*RepoSubmanifestsRetuneCmd\) Run\(g \*Globals\) error](<#RepoSubmanifestsRetuneCmd.Run>)
- [type RepoSubmanifestsStatusCmd](<#RepoSubmanifestsStatusCmd>)
- [func \(c \*RepoSubmanifestsStatusCmd\) Run\(g \*Globals\) error](<#RepoSubmanifestsStatusCmd.Run>)
- [type RepoTagAddCmd](<#RepoTagAddCmd>)
- [func \(c \*RepoTagAddCmd\) Run\(g \*Globals\) error](<#RepoTagAddCmd.Run>)
- [type RepoTagCmd](<#RepoTagCmd>)
- [type RepoTagLsCmd](<#RepoTagLsCmd>)
- [func \(c \*RepoTagLsCmd\) Run\(g \*Globals\) error](<#RepoTagLsCmd.Run>)
- [type RepoTimelineCmd](<#RepoTimelineCmd>)
- [func \(c \*RepoTimelineCmd\) Run\(g \*Globals\) error](<#RepoTimelineCmd.Run>)
- [type RepoUVCmd](<#RepoUVCmd>)
- [type RepoUVDeleteCmd](<#RepoUVDeleteCmd>)
- [func \(c \*RepoUVDeleteCmd\) Run\(g \*Globals\) error](<#RepoUVDeleteCmd.Run>)
- [type RepoUVGetCmd](<#RepoUVGetCmd>)
- [func \(c \*RepoUVGetCmd\) Run\(g \*Globals\) error](<#RepoUVGetCmd.Run>)
- [type RepoUVLsCmd](<#RepoUVLsCmd>)
- [func \(c \*RepoUVLsCmd\) Run\(g \*Globals\) error](<#RepoUVLsCmd.Run>)
- [type RepoUVPutCmd](<#RepoUVPutCmd>)
- [func \(c \*RepoUVPutCmd\) Run\(g \*Globals\) error](<#RepoUVPutCmd.Run>)
- [type RepoUndoCmd](<#RepoUndoCmd>)
- [func \(c \*RepoUndoCmd\) Run\(g \*Globals\) error](<#RepoUndoCmd.Run>)
- [type RepoUserCmd](<#RepoUserCmd>)
- [type RepoVerifyCmd](<#RepoVerifyCmd>)
- [func \(c \*RepoVerifyCmd\) Run\(g \*Globals\) error](<#RepoVerifyCmd.Run>)
- [type RepoWikiCmd](<#RepoWikiCmd>)
- [type RepoWikiExportCmd](<#RepoWikiExportCmd>)
- [func \(c \*RepoWikiExportCmd\) Run\(g \*Globals\) error](<#RepoWikiExportCmd.Run>)
- [type RepoWikiLsCmd](<#RepoWikiLsCmd>)
- [func \(c \*RepoWikiLsCmd\) Run\(g \*Globals\) error](<#RepoWikiLsCmd.Run>)
- [type UserAddCmd](<#UserAddCmd>)
- [func \(c \*UserAddCmd\) Run\(g \*Globals\) error](<#UserAddCmd.Run>)
- [type UserListCmd](<#UserListCmd>)
- [func \(c \*UserListCmd\) Run\(g \*Globals\) error](<#UserListCmd.Run>)
- [type UserPasswdCmd](<#UserPasswdCmd>)
- [func \(c \*UserPasswdCmd\) Run\(g \*Globals\) error](<#UserPasswdCmd.Run>)
- [type UserRmCmd](<#UserRmCmd>)
- [func \(c \*UserRmCmd\) Run\(g \*Globals\) error](<#UserRmCmd.Run>)
- [type UserUpdateCmd](<#UserUpdateCmd>)
- [func \(c \*UserUpdateCmd\) Run\(g \*Globals\) error](<#UserUpdateCmd.Run>)
- [type VersionCmd](<#VersionCmd>)
- [func \(c \*VersionCmd\) Run\(\) error](<#VersionCmd.Run>)
## Constants
Exit statuses the machine\-facing commands \(snapshot, log, cat \-\-at, diff\) document and keep stable. Every other failure, including a usage error, is ExitFailure.
```go
const (
ExitOK = 0
ExitFailure = 1
ExitNotFound = 2 // the version, path or history asked for does not exist
)
```
## Variables
ErrConeTestElided is what \`repo cone test\` returns when any path it was given is outside the cone, so the command works as a predicate.
```go
var ErrConeTestElided = errors.New("a path is elided by the sparse cone")
```
## func Version
```go
func Version() string
```
Version returns a single\-line build identifier for this binary: the module version \(or a build\-time override\), the Go toolchain version, and the platform. The shape is fixed at four whitespace\-separated fields \-\- "libfossil \ \ \/\" \-\- regardless of which fields carried real data, so a caller can rely on strings.Fields\(Version\(\)\) unconditionally. This is the same string both \`libfossil version\` and \`libfossil \-\-version\` print.
## type ExitError
ExitError is a command failure that carries the process exit status it maps to. The binary's main exits with Code after printing Err.
```go
type ExitError struct {
Code int
Err error
}
```
### func \(\*ExitError\) Error
```go
func (e *ExitError) Error() string
```
### func \(\*ExitError\) Unwrap
```go
func (e *ExitError) Unwrap() error
```
## type Globals
Globals holds flags shared by all CLI commands.
```go
type Globals struct {
Repo string `short:"R" help:"Path to repository file" type:"path"`
Verbose bool `short:"v" help:"Verbose output"`
}
```
### func \(\*Globals\) OpenRepo
```go
func (g *Globals) OpenRepo() (*libfossil.Repo, error)
```
OpenRepo opens a Fossil repository using the handle API. If Repo is empty, it searches for a .fossil file or checkout database.
## type RecoveryFlags
RecoveryFlags selects only the peer and credentials used to obtain content a local operation requires. It does not enable ordinary pull, push or autosync.
```go
type RecoveryFlags struct {
Remote string `help:"Required-content peer URL (default: saved last-sync-url)"`
SyncUser string `help:"Required-content authentication user"`
SyncPass string `help:"Required-content password (never saved)"`
SyncPrivate bool `help:"Allow recovery of private required artifacts"`
Timeout time.Duration `default:"10m" help:"Required-content deadline; 0 disables it"`
}
```
## type RepoAddCmd
RepoAddCmd stages files for addition in the checkout database.
```go
type RepoAddCmd struct {
Files []string `arg:"" required:"" help:"Files to stage for addition"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoAddCmd\) Run
```go
func (c *RepoAddCmd) Run(g *Globals) (err error)
```
## type RepoAnnotateCmd
RepoAnnotateCmd annotates file lines with version history.
```go
type RepoAnnotateCmd struct {
File string `arg:"" required:"" help:"File to annotate"`
Version string `help:"Starting version (default: tip)"`
}
```
### func \(\*RepoAnnotateCmd\) Run
```go
func (c *RepoAnnotateCmd) Run(g *Globals) error
```
## type RepoBisectBadCmd
RepoBisectBadCmd marks a version as bad.
```go
type RepoBisectBadCmd struct {
Version string `arg:"" optional:"" help:"Version to mark (default: current checkout)"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoBisectBadCmd\) Run
```go
func (c *RepoBisectBadCmd) Run(g *Globals) error
```
## type RepoBisectCmd
RepoBisectCmd groups binary search operations.
```go
type RepoBisectCmd struct {
Good RepoBisectGoodCmd `cmd:"" help:"Mark version as good"`
Bad RepoBisectBadCmd `cmd:"" help:"Mark version as bad"`
Next RepoBisectNextCmd `cmd:"" help:"Check out midpoint version"`
Skip RepoBisectSkipCmd `cmd:"" help:"Skip current version"`
Reset RepoBisectResetCmd `cmd:"" help:"Clear bisect state"`
Ls RepoBisectLsCmd `cmd:"" help:"Show bisect path"`
Status RepoBisectStatusCmd `cmd:"" help:"Show bisect state"`
}
```
## type RepoBisectGoodCmd
RepoBisectGoodCmd marks a version as good.
```go
type RepoBisectGoodCmd struct {
Version string `arg:"" optional:"" help:"Version to mark (default: current checkout)"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoBisectGoodCmd\) Run
```go
func (c *RepoBisectGoodCmd) Run(g *Globals) error
```
## type RepoBisectLsCmd
RepoBisectLsCmd shows the bisect path.
```go
type RepoBisectLsCmd struct {
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoBisectLsCmd\) Run
```go
func (c *RepoBisectLsCmd) Run(g *Globals) error
```
## type RepoBisectNextCmd
RepoBisectNextCmd checks out the bisect midpoint version.
```go
type RepoBisectNextCmd struct {
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoBisectNextCmd\) Run
```go
func (c *RepoBisectNextCmd) Run(g *Globals) error
```
## type RepoBisectResetCmd
RepoBisectResetCmd clears bisect state.
```go
type RepoBisectResetCmd struct {
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoBisectResetCmd\) Run
```go
func (c *RepoBisectResetCmd) Run(g *Globals) error
```
## type RepoBisectSkipCmd
RepoBisectSkipCmd skips the current version.
```go
type RepoBisectSkipCmd struct {
Version string `arg:"" optional:"" help:"Version to skip (default: current checkout)"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoBisectSkipCmd\) Run
```go
func (c *RepoBisectSkipCmd) Run(g *Globals) error
```
## type RepoBisectStatusCmd
RepoBisectStatusCmd shows bisect state.
```go
type RepoBisectStatusCmd struct {
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoBisectStatusCmd\) Run
```go
func (c *RepoBisectStatusCmd) Run(g *Globals) error
```
## type RepoBlameCmd
RepoBlameCmd is an alias for annotate.
```go
type RepoBlameCmd struct {
RepoAnnotateCmd
}
```
## type RepoBranchCloseCmd
RepoBranchCloseCmd closes a branch.
```go
type RepoBranchCloseCmd struct {
Name string `arg:"" help:"Branch name to close"`
User string `help:"User for control artifacts (default: OS username)"`
}
```
### func \(\*RepoBranchCloseCmd\) Run
```go
func (c *RepoBranchCloseCmd) Run(g *Globals) error
```
## type RepoBranchCmd
RepoBranchCmd groups branch operations.
```go
type RepoBranchCmd struct {
Ls RepoBranchLsCmd `cmd:"" help:"List branches"`
New RepoBranchNewCmd `cmd:"" help:"Create new branch"`
Close RepoBranchCloseCmd `cmd:"" help:"Close a branch"`
}
```
## type RepoBranchLsCmd
RepoBranchLsCmd lists branches.
```go
type RepoBranchLsCmd struct {
Closed bool `help:"Show only closed branches"`
All bool `help:"Show all branches (open and closed)"`
}
```
### func \(\*RepoBranchLsCmd\) Run
```go
func (c *RepoBranchLsCmd) Run(g *Globals) error
```
## type RepoBranchNewCmd
RepoBranchNewCmd creates a new branch.
```go
type RepoBranchNewCmd struct {
Name string `arg:"" help:"Branch name"`
From string `help:"Parent version (default: tip)"`
Message string `short:"m" help:"Checkin comment"`
User string `help:"Checkin user (default: OS username)"`
NoSign bool `name:"nosign" help:"Do not sign the branch's check-in, even with the clearsign setting on"`
}
```
### func \(\*RepoBranchNewCmd\) Run
```go
func (c *RepoBranchNewCmd) Run(g *Globals) error
```
## type RepoCatCmd
RepoCatCmd outputs the content of an artifact, or with \-\-at the content of a file as a version holds it, as fossil cat \-r does.
```go
type RepoCatCmd struct {
Artifact string `arg:"" help:"Artifact UUID or prefix; with --at, the file name"`
At string `name:"at" short:"r" help:"Version to read the file from (tip, trunk, a branch, a uuid or prefix)"`
Raw bool `help:"Output raw blob (no delta expansion)"`
}
```
### func \(\*RepoCatCmd\) Run
```go
func (c *RepoCatCmd) Run(g *Globals) error
```
## type RepoCiCmd
RepoCiCmd commits the checkout's changes, as fossil's commit command does: every change, or only the named files. A pending merge is committed with the merged\-in checkin as a merge parent.
```go
type RepoCiCmd struct {
RecoveryFlags
NoSync bool `help:"Suppress optional autosync, not required-content recovery"`
Message string `short:"m" required:"" help:"Checkin comment"`
Files []string `arg:"" optional:"" help:"Files to commit (default: all changes)"`
User string `help:"Checkin user (default: OS username)"`
Branch string `help:"Branch name for this checkin"`
AllowConflict bool `help:"Commit files that still hold merge conflict markers"`
AllowEmpty bool `help:"Commit even when nothing has changed"`
NoSign bool `name:"nosign" help:"Do not sign this check-in, even with the clearsign setting on"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoCiCmd\) Run
```go
func (c *RepoCiCmd) Run(g *Globals) (err error)
```
## type RepoCloneCmd
RepoCloneCmd clones a remote Fossil repository.
```go
type RepoCloneCmd struct {
URL string `arg:"" optional:"" help:"Remote Fossil server URL"`
Path string `arg:"" optional:"" help:"Local path for new repository file"`
User string `short:"u" help:"Username for clone auth"`
Pass string `short:"p" help:"Password for clone auth"`
Invite string `help:"Invite token (from fossil invite)"`
Timeout time.Duration `default:"10m" help:"Abort the clone if it runs longer than this; 0 disables the deadline"`
Horizon string `help:"Clone a horizon store: elide blobs below this check-in, given as tip, a branch or tag, or its full 40 or 64 digit hash; the origin resolves a name, or the clone does over what an origin that cannot sends it; what each rule would hold needs a local repository ('repo cone cost') or a peer that exposes it out of band, as the origin's horizon-served URL may"`
Depth int `help:"With --horizon, step the check-in back this many check-ins along primary parents, stopping at a root"`
Leaves bool `help:"With --horizon, also hold every open leaf's ancestry back to the check-in, for merging across live branches offline; costly: on a repository with old unmerged branches that is nearly the whole history"`
Structural bool `help:"With --horizon, also elide the manifests below the check-in, deriving their branch and tag state from the origin's seed"`
Cone []string `name:"cone" help:"Clone a sparse store: fetch only this path's subtree (repeatable)"`
ConeNot []string `name:"cone-not" help:"Carve this path out of a --cone (repeatable)"`
SeedKey string `name:"seed-key" help:"The origin's public key, an SSH public key line or an OpenPGP fingerprint: with --structural, a horizon seed not signed by it is discarded and the clone falls back to the content horizon; kept as the horizon-seed-key setting"`
}
```
### func \(\*RepoCloneCmd\) Run
```go
func (c *RepoCloneCmd) Run(g *Globals) error
```
## type RepoCmd
RepoCmd is the top\-level command group for repository operations. Embed this in your CLI struct alongside Globals for a complete fossil CLI.
```go
type RepoCmd struct {
New RepoNewCmd `cmd:"" help:"Create a new repository"`
Clone RepoCloneCmd `cmd:"" help:"Clone a remote repository"`
Ci RepoCiCmd `cmd:"" help:"Checkin file changes"`
Co RepoCoCmd `cmd:"" help:"Checkout a version"`
Ls RepoLsCmd `cmd:"" help:"List files in a version"`
Timeline RepoTimelineCmd `cmd:"" help:"Show repository history"`
Snapshot RepoSnapshotCmd `cmd:"" help:"Commit a file's bytes onto trunk, no checkout needed"`
Log RepoLogCmd `cmd:"" help:"Show the check-ins that changed a path"`
Cat RepoCatCmd `cmd:"" help:"Output artifact content"`
Info RepoInfoCmd `cmd:"" help:"Repository statistics"`
Hash RepoHashCmd `cmd:"" help:"Hash files (SHA1 or SHA3)"`
Delta RepoDeltaCmd `cmd:"" help:"Delta create/apply operations"`
Config RepoConfigCmd `cmd:"" help:"Repository configuration"`
Query RepoQueryCmd `cmd:"" help:"Execute SQL against repository"`
Verify RepoVerifyCmd `cmd:"" help:"Verify repository integrity"`
Resolve RepoResolveCmd `cmd:"" help:"Resolve symbolic name to UUID"`
Extract RepoExtractCmd `cmd:"" help:"Extract files from a version"`
Wiki RepoWikiCmd `cmd:"" help:"Wiki page operations"`
Tag RepoTagCmd `cmd:"" help:"Tag operations"`
Open RepoOpenCmd `cmd:"" help:"Open a checkout in a directory"`
Status RepoStatusCmd `cmd:"" help:"Show working directory changes"`
Add RepoAddCmd `cmd:"" help:"Stage files for addition"`
Rm RepoRmCmd `cmd:"" help:"Stage files for removal"`
Rename RepoRenameCmd `cmd:"" help:"Rename a tracked file"`
Revert RepoRevertCmd `cmd:"" help:"Undo staging changes"`
Diff RepoDiffCmd `cmd:"" help:"Show changes vs a version"`
Merge RepoMergeCmd `cmd:"" help:"Merge a divergent version"`
Conflicts RepoConflictsCmd `cmd:"" help:"List/manage unresolved conflicts"`
MarkResolved RepoMergeResolveCmd `cmd:"" name:"mark-resolved" help:"Mark a conflict as resolved"`
Undo RepoUndoCmd `cmd:"" help:"Undo last operation"`
Redo RepoRedoCmd `cmd:"" help:"Redo undone operation"`
Stash RepoStashCmd `cmd:"" help:"Stash working changes"`
Bisect RepoBisectCmd `cmd:"" help:"Binary search for bugs"`
Annotate RepoAnnotateCmd `cmd:"" help:"Annotate file lines with version history"`
Blame RepoBlameCmd `cmd:"" help:"Alias for annotate"`
Branch RepoBranchCmd `cmd:"" help:"Branch operations"`
UV RepoUVCmd `cmd:"" name:"uv" help:"Unversioned file operations"`
Schema RepoSchemaCmd `cmd:"" help:"Synced table schema operations"`
User RepoUserCmd `cmd:"" help:"User management"`
Invite RepoInviteCmd `cmd:"" help:"Generate invite token for a user"`
Serve RepoServeCmd `cmd:"" help:"Serve repository over HTTP for sync/clone"`
Submanifests RepoSubmanifestsCmd `cmd:"" help:"Sub-manifest tree settings"`
Cone RepoConeCmd `cmd:"" help:"Show or edit a checkout's sparse cone"`
}
```
## type RepoCoCmd
RepoCoCmd checks out a version to the working directory.
```go
type RepoCoCmd struct {
Version string `arg:"" optional:"" help:"Version to checkout (default: tip)"`
Dir string `short:"d" help:"Output directory (default: current dir)" default:"."`
Force bool `help:"Overwrite existing files"`
}
```
### func \(\*RepoCoCmd\) Run
```go
func (c *RepoCoCmd) Run(g *Globals) error
```
## type RepoConeAddCmd
RepoConeAddCmd adds members to the cone.
```go
type RepoConeAddCmd struct {
RecoveryFlags
Paths []string `arg:"" help:"Paths to add"`
Not bool `help:"Add the paths as exclusions"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoConeAddCmd\) Run
```go
func (c *RepoConeAddCmd) Run(g *Globals) error
```
## type RepoConeCmd
RepoConeCmd shows or edits a checkout's sparse cone \(draft\-fossil\-sparse\-cone\-00\): the paths, directory subtrees or single files, that the checkout materializes and tracks. An exclusion carves a part out of an inclusion; the longest member covering a path decides.
```go
type RepoConeCmd struct {
Show RepoConeShowCmd `cmd:"" default:"withargs" help:"List the cone's members (\"dense\" when there is none)"`
Add RepoConeAddCmd `cmd:"" help:"Widen the cone, or with --not carve paths out of it"`
Remove RepoConeRemoveCmd `cmd:"" help:"Remove members of either polarity"`
Set RepoConeSetCmd `cmd:"" help:"Replace the cone"`
Dense RepoConeDenseCmd `cmd:"" help:"Drop the cone and materialize every file"`
Test RepoConeTestCmd `cmd:"" help:"Classify paths against the cone and name the deciding member"`
Store RepoConeStoreCmd `cmd:"" help:"Show or replace the repository's store cone, fetching what widening covers"`
Deepen RepoConeDeepenCmd `cmd:"" help:"Move the store's horizon down to reach a check-in, fetching the history it keeps"`
Compact RepoConeCompactCmd `cmd:"" help:"Drop held blobs the store cone elides (irreversible locally)"`
Cost RepoConeCostCmd `cmd:"" help:"Report what each horizon rule would hold at a check-in, from this repository"`
}
```
## type RepoConeCompactCmd
RepoConeCompactCmd is the narrow\-then\-compact deletion, zit's \`pack \-\-drop\-elided\`: it acts on the store cone, not a checkout's, and a later widening fetches the dropped blobs from a peer.
```go
type RepoConeCompactCmd struct{}
```
### func \(\*RepoConeCompactCmd\) Run
```go
func (c *RepoConeCompactCmd) Run(g *Globals) (err error)
```
## type RepoConeCostCmd
RepoConeCostCmd reports what a horizon at a check\-in would hold under each rule \(draft\-fossil\-horizon\-00 §3.4\): the check\-ins of the held set and the bytes of the blobs they name, stored and expanded, so the rule is chosen with its cost in view. It reads the local repository, which must hold the history; a clone has none yet, so the figures come before it, from a peer that exposes them \(an origin may name such an interface through horizon\-served\) or from a repository already held.
```go
type RepoConeCostCmd struct {
Checkin string `arg:"" help:"Check-in to place the horizon at: a name, branch or hash prefix"`
}
```
### func \(\*RepoConeCostCmd\) Run
```go
func (c *RepoConeCostCmd) Run(g *Globals) (err error)
```
## type RepoConeDeepenCmd
RepoConeDeepenCmd deepens the store's horizon \(draft\-fossil\-horizon\-00 §5.8\) so that it reaches a check\-in: C moves to that check\-in when it is an ancestor of C, else to a common ancestor of the two, which the remote is asked for when the store cannot see one. The rule, the depth and the path members stay. History comes from \-\-remote, else the saved last\-sync\-url.
```go
type RepoConeDeepenCmd struct {
RecoveryFlags
Checkin string `arg:"" help:"Check-in to reach: a full hash, which may lie below the horizon, or a name"`
}
```
### func \(\*RepoConeDeepenCmd\) Run
```go
func (c *RepoConeDeepenCmd) Run(g *Globals) (err error)
```
## type RepoConeDenseCmd
RepoConeDenseCmd drops the cone.
```go
type RepoConeDenseCmd struct {
RecoveryFlags
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoConeDenseCmd\) Run
```go
func (c *RepoConeDenseCmd) Run(g *Globals) error
```
## type RepoConeRemoveCmd
RepoConeRemoveCmd removes members from the cone.
```go
type RepoConeRemoveCmd struct {
RecoveryFlags
Paths []string `arg:"" help:"Members to remove"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoConeRemoveCmd\) Run
```go
func (c *RepoConeRemoveCmd) Run(g *Globals) error
```
## type RepoConeSetCmd
RepoConeSetCmd replaces the cone.
```go
type RepoConeSetCmd struct {
RecoveryFlags
Paths []string `arg:"" help:"Inclusion members"`
Not []string `help:"Exclusion members (repeatable)"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoConeSetCmd\) Run
```go
func (c *RepoConeSetCmd) Run(g *Globals) error
```
## type RepoConeShowCmd
RepoConeShowCmd lists the inclusions, then the exclusions marked '\!'.
```go
type RepoConeShowCmd struct {
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoConeShowCmd\) Run
```go
func (c *RepoConeShowCmd) Run(g *Globals) (err error)
```
## type RepoConeStoreCmd
RepoConeStoreCmd shows or replaces the repository's store cone, the policy form of demand fetch \(draft\-fossil\-sparse\-cone\-00 §6.4\): artifacts the new cone covers that the old one elided are pulled from \-\-remote, else the saved last\-sync\-url. With such artifacts and no peer it refuses and changes nothing.
A store cone may carry a horizon \(draft\-fossil\-horizon\-00\): \-\-horizon sets it, or deepens it when the check\-in is older than the current one, with \-\-structural taking the structural cut, and \-\-no\-horizon drops it. Either keeps the store's path members unless paths are given, which replace them.
```go
type RepoConeStoreCmd struct {
RecoveryFlags
Paths []string `arg:"" optional:"" help:"Inclusion members; none shows the store cone"`
Not []string `help:"Exclusion members (repeatable)"`
Dense bool `help:"Drop the store cone, fetching everything it elided"`
Horizon string `help:"Elide blobs below this check-in (name, branch or hash prefix), fetching what a deeper horizon keeps; 'repo cone cost' shows what each rule would hold"`
Leaves bool `help:"With --horizon, also hold every open leaf's ancestry back to the check-in, for merging across live branches offline; costly: on a repository with old unmerged branches that is nearly the whole history"`
Structural bool `help:"With --horizon, also elide the manifests below the check-in; the next pull fetches the origin's seed"`
NoHorizon bool `help:"Drop the horizon, keeping the path members, and fetch what it elided"`
}
```
### func \(\*RepoConeStoreCmd\) Run
```go
func (c *RepoConeStoreCmd) Run(g *Globals) (err error)
```
## type RepoConeTestCmd
RepoConeTestCmd classifies paths as zit's \`cone test\` does.
```go
type RepoConeTestCmd struct {
Paths []string `arg:"" help:"Paths to classify"`
Quiet bool `short:"q" help:"Print nothing; report through the exit status only"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoConeTestCmd\) Run
```go
func (c *RepoConeTestCmd) Run(g *Globals) (err error)
```
## type RepoConfigCmd
RepoConfigCmd groups configuration operations.
```go
type RepoConfigCmd struct {
Ls RepoConfigLsCmd `cmd:"" help:"List all config entries"`
Get RepoConfigGetCmd `cmd:"" help:"Get a config value"`
Set RepoConfigSetCmd `cmd:"" help:"Set a config value"`
}
```
## type RepoConfigGetCmd
RepoConfigGetCmd gets a single config value.
```go
type RepoConfigGetCmd struct {
Key string `arg:"" help:"Config key to get"`
}
```
### func \(\*RepoConfigGetCmd\) Run
```go
func (c *RepoConfigGetCmd) Run(g *Globals) error
```
## type RepoConfigLsCmd
RepoConfigLsCmd lists all config entries.
```go
type RepoConfigLsCmd struct{}
```
### func \(\*RepoConfigLsCmd\) Run
```go
func (c *RepoConfigLsCmd) Run(g *Globals) error
```
## type RepoConfigSetCmd
RepoConfigSetCmd sets a config value.
```go
type RepoConfigSetCmd struct {
Key string `arg:"" help:"Config key"`
Value string `arg:"" help:"Config value"`
}
```
### func \(\*RepoConfigSetCmd\) Run
```go
func (c *RepoConfigSetCmd) Run(g *Globals) error
```
## type RepoConflictsCmd
RepoConflictsCmd groups conflict management operations.
```go
type RepoConflictsCmd struct {
Ls RepoConflictsLsCmd `cmd:"" default:"1" help:"List all conflicts"`
Show RepoConflictsShowCmd `cmd:"" help:"Show all versions of a conflicted file"`
Pick RepoConflictsPickCmd `cmd:"" help:"Resolve by picking one version"`
Merge RepoConflictsMergeCmd `cmd:"" help:"Resolve by re-merging with a different strategy"`
Extract RepoConflictsExtractCmd `cmd:"" help:"Extract all versions to disk for manual editing"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
## type RepoConflictsExtractCmd
RepoConflictsExtractCmd extracts all versions to disk for manual editing.
```go
type RepoConflictsExtractCmd struct {
File string `arg:"" help:"Conflicted file to extract"`
Dir string `short:"d" help:"Output directory" default:"."`
}
```
### func \(\*RepoConflictsExtractCmd\) Run
```go
func (c *RepoConflictsExtractCmd) Run(g *Globals) error
```
## type RepoConflictsLsCmd
RepoConflictsLsCmd lists all conflicts.
```go
type RepoConflictsLsCmd struct{}
```
### func \(\*RepoConflictsLsCmd\) Run
```go
func (c *RepoConflictsLsCmd) Run(g *Globals) error
```
## type RepoConflictsMergeCmd
RepoConflictsMergeCmd resolves a conflict by re\-merging with a specified strategy.
```go
type RepoConflictsMergeCmd struct {
File string `arg:"" help:"Conflicted file to re-merge"`
Strategy string `help:"Strategy to use" default:"three-way"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoConflictsMergeCmd\) Run
```go
func (c *RepoConflictsMergeCmd) Run(g *Globals) error
```
## type RepoConflictsPickCmd
RepoConflictsPickCmd resolves a conflict by picking one version.
```go
type RepoConflictsPickCmd struct {
File string `arg:"" help:"Conflicted file to resolve"`
Local bool `help:"Keep local version" xor:"version"`
Remote bool `help:"Keep remote version" xor:"version"`
Base bool `help:"Revert to base version" xor:"version"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoConflictsPickCmd\) Run
```go
func (c *RepoConflictsPickCmd) Run(g *Globals) error
```
## type RepoConflictsShowCmd
RepoConflictsShowCmd shows all versions of a conflicted file.
```go
type RepoConflictsShowCmd struct {
File string `arg:"" help:"Conflicted file to show"`
}
```
### func \(\*RepoConflictsShowCmd\) Run
```go
func (c *RepoConflictsShowCmd) Run(g *Globals) error
```
## type RepoDeltaApplyCmd
RepoDeltaApplyCmd applies a delta to a source file.
```go
type RepoDeltaApplyCmd struct {
Source string `arg:"" help:"Source (original) file"`
Delta string `arg:"" help:"Delta file"`
Output string `short:"o" help:"Output file (default: stdout)"`
}
```
### func \(\*RepoDeltaApplyCmd\) Run
```go
func (c *RepoDeltaApplyCmd) Run(g *Globals) error
```
## type RepoDeltaCmd
RepoDeltaCmd groups delta create/apply operations.
```go
type RepoDeltaCmd struct {
Create RepoDeltaCreateCmd `cmd:"" help:"Create a delta between two files"`
Apply RepoDeltaApplyCmd `cmd:"" help:"Apply a delta to a source file"`
}
```
## type RepoDeltaCreateCmd
RepoDeltaCreateCmd creates a delta between two files.
```go
type RepoDeltaCreateCmd struct {
Source string `arg:"" help:"Source (original) file"`
Target string `arg:"" help:"Target (new) file"`
Output string `short:"o" help:"Output file (default: stdout)"`
}
```
### func \(\*RepoDeltaCreateCmd\) Run
```go
func (c *RepoDeltaCreateCmd) Run(g *Globals) error
```
## type RepoDiffCmd
RepoDiffCmd shows changes in the working directory vs a repository version, or, given two versions, the changes between them.
```go
type RepoDiffCmd struct {
Version string `arg:"" optional:"" help:"Version to diff against (default: tip)"`
To string `arg:"" optional:"" help:"Second version: diff Version against it instead of the working directory"`
Path string `arg:"" optional:"" help:"With two versions, only this file (default: every changed file)"`
JSON bool `help:"With two versions, print a JSON array of {name, unified} instead of text"`
Dir string `short:"d" help:"Working directory to compare" default:"."`
Unified int `short:"U" help:"Lines of context" default:"3"`
}
```
### func \(\*RepoDiffCmd\) Run
```go
func (c *RepoDiffCmd) Run(g *Globals) error
```
## type RepoExtractCmd
RepoExtractCmd extracts files from a version.
```go
type RepoExtractCmd struct {
Version string `help:"Version to extract from (default: tip)"`
Files []string `arg:"" optional:"" help:"Files to extract (default: all)"`
Dir string `short:"d" help:"Output directory" default:"."`
}
```
### func \(\*RepoExtractCmd\) Run
```go
func (c *RepoExtractCmd) Run(g *Globals) error
```
## type RepoHashCmd
RepoHashCmd hashes files using SHA1 or SHA3\-256.
```go
type RepoHashCmd struct {
Files []string `arg:"" required:"" help:"Files to hash"`
SHA3 bool `name:"sha3" help:"Use SHA3-256 instead of SHA1"`
}
```
### func \(\*RepoHashCmd\) Run
```go
func (c *RepoHashCmd) Run(g *Globals) error
```
## type RepoInfoCmd
RepoInfoCmd shows repository statistics.
```go
type RepoInfoCmd struct{}
```
### func \(\*RepoInfoCmd\) Run
```go
func (c *RepoInfoCmd) Run(g *Globals) error
```
## type RepoInviteCmd
RepoInviteCmd generates an invite token for a new user.
```go
type RepoInviteCmd struct {
Login string `arg:"" help:"Username for the invitee"`
Cap string `help:"Capability string (e.g. oi)" required:""`
URL string `help:"Sync URL to embed in token" default:""`
TTL time.Duration `help:"Token time-to-live (e.g. 24h)" default:"0"`
}
```
### func \(\*RepoInviteCmd\) Run
```go
func (c *RepoInviteCmd) Run(g *Globals) error
```
## type RepoLogCmd
RepoLogCmd lists the check\-ins that changed one path, newest first, as fossil finfo does.
```go
type RepoLogCmd struct {
Path string `required:"" help:"File name in the repository"`
JSON bool `help:"Print a JSON array instead of text"`
Limit int `short:"n" help:"Number of entries (default: all)"`
}
```
### func \(\*RepoLogCmd\) Run
```go
func (c *RepoLogCmd) Run(g *Globals) (err error)
```
## type RepoLsCmd
RepoLsCmd lists files in a version.
```go
type RepoLsCmd struct {
Version string `arg:"" optional:"" help:"Version to list (default: tip)"`
Long bool `short:"l" help:"Show sizes and hashes"`
}
```
### func \(\*RepoLsCmd\) Run
```go
func (c *RepoLsCmd) Run(g *Globals) error
```
## type RepoMergeCmd
RepoMergeCmd merges a divergent version into the current checkout. In a store with a structural horizon, a merge whose base lies below the horizon asks \-\-remote, else the saved last\-sync\-url, for the base and deepens the horizon to reach it before merging \(draft\-fossil\-horizon\-00 §5.5\).
```go
type RepoMergeCmd struct {
RecoveryFlags
Version string `arg:"" help:"Version to merge into current checkout"`
Strategy string `help:"Override merge strategy for all files"`
DryRun bool `help:"Show what would be merged without writing"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoMergeCmd\) Run
```go
func (c *RepoMergeCmd) Run(g *Globals) (err error)
```
## type RepoMergeResolveCmd
RepoMergeResolveCmd marks a file conflict as resolved.
A merge conflict needs no marking: as in fossil, a file is in conflict while it holds conflict markers, and editing them out resolves it. So this command refuses a file that still holds markers, and resolves the conflict\-fork entries the conflict\-fork strategy records.
```go
type RepoMergeResolveCmd struct {
File string `arg:"" help:"File to mark as resolved"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoMergeResolveCmd\) Run
```go
func (c *RepoMergeResolveCmd) Run(g *Globals) (err error)
```
## type RepoNewCmd
RepoNewCmd creates a new Fossil repository, at Path or else at the \-R path.
```go
type RepoNewCmd struct {
Path string `arg:"" optional:"" help:"Path for new repository file (default: -R)"`
User string `help:"Default user name" default:""`
}
```
### func \(\*RepoNewCmd\) Run
```go
func (c *RepoNewCmd) Run(g *Globals) error
```
## type RepoOpenCmd
RepoOpenCmd opens a checkout in a directory, creating the .fslckout database.
```go
type RepoOpenCmd struct {
Dir string `arg:"" optional:"" help:"Checkout directory (default: current dir)" default:"."`
Cone []string `name:"cone" help:"Make a sparse checkout: materialize and track only this path's subtree (repeatable)"`
ConeNot []string `name:"cone-not" help:"Exclude this path's subtree from an enclosing --cone (repeatable)"`
}
```
### func \(\*RepoOpenCmd\) Run
```go
func (c *RepoOpenCmd) Run(g *Globals) error
```
## type RepoQueryCmd
RepoQueryCmd executes raw SQL against the repository database.
```go
type RepoQueryCmd struct {
SQL string `arg:"" help:"SQL query to execute"`
}
```
### func \(\*RepoQueryCmd\) Run
```go
func (c *RepoQueryCmd) Run(g *Globals) error
```
## type RepoRedoCmd
RepoRedoCmd re\-applies the last undone operation.
```go
type RepoRedoCmd struct {
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoRedoCmd\) Run
```go
func (c *RepoRedoCmd) Run(g *Globals) error
```
## type RepoRenameCmd
RepoRenameCmd renames a tracked file in the checkout and on disk.
```go
type RepoRenameCmd struct {
From string `arg:"" help:"Current file name"`
To string `arg:"" help:"New file name"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoRenameCmd\) Run
```go
func (c *RepoRenameCmd) Run(g *Globals) (err error)
```
## type RepoResolveCmd
RepoResolveCmd resolves a symbolic name to UUID and RID.
```go
type RepoResolveCmd struct {
Name string `arg:"" help:"Symbolic name, UUID, or prefix to resolve (e.g. trunk, tip, UUID prefix)"`
}
```
### func \(\*RepoResolveCmd\) Run
```go
func (c *RepoResolveCmd) Run(g *Globals) error
```
## type RepoRevertCmd
RepoRevertCmd restores files to their checked\-out version.
```go
type RepoRevertCmd struct {
Files []string `arg:"" optional:"" help:"Files to revert (default: all)"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoRevertCmd\) Run
```go
func (c *RepoRevertCmd) Run(g *Globals) (err error)
```
## type RepoRmCmd
RepoRmCmd stages files for removal from the checkout.
```go
type RepoRmCmd struct {
Files []string `arg:"" required:"" help:"Files to stage for removal"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoRmCmd\) Run
```go
func (c *RepoRmCmd) Run(g *Globals) (err error)
```
## type RepoSchemaAddCmd
RepoSchemaAddCmd registers a new synced table.
```go
type RepoSchemaAddCmd struct {
Name string `arg:"" help:"Table name (without x_ prefix)"`
Columns string `required:"" help:"Column defs: name:type[:pk],... (e.g. peer_id:text:pk,addr:text)"`
Conflict string `default:"mtime-wins" enum:"self-write,mtime-wins,owner-write" help:"Conflict strategy"`
}
```
### func \(\*RepoSchemaAddCmd\) Run
```go
func (c *RepoSchemaAddCmd) Run(g *Globals) error
```
## type RepoSchemaCmd
RepoSchemaCmd groups synced table schema operations.
```go
type RepoSchemaCmd struct {
Add RepoSchemaAddCmd `cmd:"" help:"Register a new synced table"`
List RepoSchemaListCmd `cmd:"" help:"List registered synced tables"`
Show RepoSchemaShowCmd `cmd:"" help:"Show table schema details"`
Remove RepoSchemaRemoveCmd `cmd:"" help:"Remove a synced table registration"`
}
```
## type RepoSchemaListCmd
RepoSchemaListCmd lists registered synced tables.
```go
type RepoSchemaListCmd struct{}
```
### func \(\*RepoSchemaListCmd\) Run
```go
func (c *RepoSchemaListCmd) Run(g *Globals) error
```
## type RepoSchemaRemoveCmd
RepoSchemaRemoveCmd removes a synced table registration.
```go
type RepoSchemaRemoveCmd struct {
Name string `arg:"" help:"Table name (without x_ prefix)"`
}
```
### func \(\*RepoSchemaRemoveCmd\) Run
```go
func (c *RepoSchemaRemoveCmd) Run(g *Globals) error
```
## type RepoSchemaShowCmd
RepoSchemaShowCmd shows table schema details.
```go
type RepoSchemaShowCmd struct {
Name string `arg:"" help:"Table name (without x_ prefix)"`
}
```
### func \(\*RepoSchemaShowCmd\) Run
```go
func (c *RepoSchemaShowCmd) Run(g *Globals) error
```
## type RepoServeCmd
RepoServeCmd starts an HTTP xfer server for the repository so a remote fossil client \-\- the canonical binary or another instance of this tool \-\- can clone or sync against it. This is a thin wrapper: all serving logic lives in Repo.ServeHTTP; the command's job is to open the repo, resolve the bind address, and translate process interrupt into the context cancellation that ServeHTTP already honors. Access follows the user table, as with fossil serve: clients without credentials act as nobody, which may clone and pull a new repository but not push to it. A repository with a store cone refuses clones its cone does not cover unless \`repo config set partial\-mirror on\` marks it a partial mirror.
```go
type RepoServeCmd struct {
Addr string `short:"P" default:"127.0.0.1:8080" help:"Bind address as host:port"`
}
```
### func \(\*RepoServeCmd\) Run
```go
func (c *RepoServeCmd) Run(g *Globals) error
```
## type RepoSnapshotCmd
RepoSnapshotCmd commits the bytes of one file onto trunk without a checkout: the tip's other files carry forward, and bytes equal to the tip's are a no\-op that prints the existing tip.
```go
type RepoSnapshotCmd struct {
Message string `short:"m" required:"" help:"Checkin comment"`
Path string `required:"" help:"File name in the repository (relative, slash-separated); the bytes come from this path on disk unless --stdin"`
Stdin bool `help:"Read the bytes from stdin instead of from --path on disk"`
User string `help:"Checkin user (default: OS username)"`
JSON bool `help:"Print {\"uuid\":...,\"changed\":...} instead of the bare uuid"`
}
```
### func \(\*RepoSnapshotCmd\) Run
```go
func (c *RepoSnapshotCmd) Run(g *Globals) (err error)
```
## type RepoStashApplyCmd
RepoStashApplyCmd applies a stash entry without removing it.
```go
type RepoStashApplyCmd struct {
ID int64 `arg:"" optional:"" help:"Stash ID to apply (default: latest)"`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoStashApplyCmd\) Run
```go
func (c *RepoStashApplyCmd) Run(g *Globals) error
```
## type RepoStashClearCmd
RepoStashClearCmd removes all stash entries.
```go
type RepoStashClearCmd struct{}
```
### func \(\*RepoStashClearCmd\) Run
```go
func (c *RepoStashClearCmd) Run(g *Globals) error
```
## type RepoStashCmd
RepoStashCmd groups stash operations.
```go
type RepoStashCmd struct {
Save RepoStashSaveCmd `cmd:"" help:"Stash working changes"`
Pop RepoStashPopCmd `cmd:"" help:"Apply top stash and drop it"`
Apply RepoStashApplyCmd `cmd:"" help:"Apply stash without dropping"`
Ls RepoStashLsCmd `cmd:"" help:"List stash entries"`
Drop RepoStashDropCmd `cmd:"" help:"Remove stash entry"`
Clear RepoStashClearCmd `cmd:"" help:"Remove all stash entries"`
}
```
## type RepoStashDropCmd
RepoStashDropCmd removes a stash entry by ID.
```go
type RepoStashDropCmd struct {
ID int64 `arg:"" required:"" help:"Stash ID to drop"`
}
```
### func \(\*RepoStashDropCmd\) Run
```go
func (c *RepoStashDropCmd) Run(g *Globals) error
```
## type RepoStashLsCmd
RepoStashLsCmd lists stash entries.
```go
type RepoStashLsCmd struct{}
```
### func \(\*RepoStashLsCmd\) Run
```go
func (c *RepoStashLsCmd) Run(g *Globals) error
```
## type RepoStashPopCmd
RepoStashPopCmd pops the top stash entry.
```go
type RepoStashPopCmd struct {
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoStashPopCmd\) Run
```go
func (c *RepoStashPopCmd) Run(g *Globals) error
```
## type RepoStashSaveCmd
RepoStashSaveCmd saves working changes to the stash.
```go
type RepoStashSaveCmd struct {
Message string `short:"m" help:"Stash message" default:""`
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoStashSaveCmd\) Run
```go
func (c *RepoStashSaveCmd) Run(g *Globals) error
```
## type RepoStatusCmd
RepoStatusCmd shows the checkout's changes against its checked\-out version.
```go
type RepoStatusCmd struct {
Dir string `short:"d" help:"Checkout directory to scan" default:"."`
}
```
### func \(\*RepoStatusCmd\) Run
```go
func (c *RepoStatusCmd) Run(g *Globals) error
```
## type RepoSubmanifestsCmd
RepoSubmanifestsCmd groups the sub\-manifest settings \(draft\-fossil\-submanifests\-00\): whether new check\-ins are written as trees, and the partition parameter k they are built with.
```go
type RepoSubmanifestsCmd struct {
Enable RepoSubmanifestsEnableCmd `cmd:"" help:"Write new check-ins as sub-manifest trees"`
Retune RepoSubmanifestsRetuneCmd `cmd:"" help:"Re-pin the partition parameter for later commits"`
Disable RepoSubmanifestsDisableCmd `cmd:"" help:"Write new check-ins as flat manifests again"`
Status RepoSubmanifestsStatusCmd `cmd:"" help:"Show the sub-manifest settings and whether trees exist"`
}
```
## type RepoSubmanifestsDisableCmd
RepoSubmanifestsDisableCmd turns tree emission off; existing trees stay.
```go
type RepoSubmanifestsDisableCmd struct{}
```
### func \(\*RepoSubmanifestsDisableCmd\) Run
```go
func (c *RepoSubmanifestsDisableCmd) Run(g *Globals) error
```
## type RepoSubmanifestsEnableCmd
RepoSubmanifestsEnableCmd turns tree emission on, pinning k the first time.
```go
type RepoSubmanifestsEnableCmd struct {
K *int `name:"k" help:"Partition parameter, 1..12 (default: selected from the main-branch tip)"`
}
```
### func \(\*RepoSubmanifestsEnableCmd\) Run
```go
func (c *RepoSubmanifestsEnableCmd) Run(g *Globals) error
```
## type RepoSubmanifestsRetuneCmd
RepoSubmanifestsRetuneCmd re\-pins k; a re\-tune is always explicit.
```go
type RepoSubmanifestsRetuneCmd struct {
K int `name:"k" required:"" help:"New partition parameter, 1..12"`
}
```
### func \(\*RepoSubmanifestsRetuneCmd\) Run
```go
func (c *RepoSubmanifestsRetuneCmd) Run(g *Globals) error
```
## type RepoSubmanifestsStatusCmd
RepoSubmanifestsStatusCmd prints the three settings and whether the repository holds any S\-bearing artifact.
```go
type RepoSubmanifestsStatusCmd struct{}
```
### func \(\*RepoSubmanifestsStatusCmd\) Run
```go
func (c *RepoSubmanifestsStatusCmd) Run(g *Globals) error
```
## type RepoTagAddCmd
RepoTagAddCmd adds a tag to an artifact.
```go
type RepoTagAddCmd struct {
Tag string `arg:"" help:"Tag name"`
Value string `arg:"" optional:"" help:"Tag value (optional)"`
Version string `help:"Version to tag (default: tip)"`
}
```
### func \(\*RepoTagAddCmd\) Run
```go
func (c *RepoTagAddCmd) Run(g *Globals) error
```
## type RepoTagCmd
RepoTagCmd groups tag operations.
```go
type RepoTagCmd struct {
Ls RepoTagLsCmd `cmd:"" help:"List tags on an artifact"`
Add RepoTagAddCmd `cmd:"" help:"Add a tag to an artifact"`
}
```
## type RepoTagLsCmd
RepoTagLsCmd lists tags on an artifact.
```go
type RepoTagLsCmd struct {
Version string `arg:"" optional:"" help:"Version to list tags for (default: tip)"`
}
```
### func \(\*RepoTagLsCmd\) Run
```go
func (c *RepoTagLsCmd) Run(g *Globals) error
```
## type RepoTimelineCmd
RepoTimelineCmd shows repository timeline/history.
```go
type RepoTimelineCmd struct {
Limit int `short:"n" default:"20" help:"Number of entries"`
}
```
### func \(\*RepoTimelineCmd\) Run
```go
func (c *RepoTimelineCmd) Run(g *Globals) error
```
## type RepoUVCmd
RepoUVCmd groups unversioned file operations.
```go
type RepoUVCmd struct {
Ls RepoUVLsCmd `cmd:"" help:"List unversioned files"`
Put RepoUVPutCmd `cmd:"" help:"Add or update an unversioned file"`
Get RepoUVGetCmd `cmd:"" help:"Retrieve an unversioned file"`
Delete RepoUVDeleteCmd `cmd:"" help:"Delete an unversioned file (creates tombstone)"`
}
```
## type RepoUVDeleteCmd
RepoUVDeleteCmd deletes an unversioned file \(creates a tombstone\).
```go
type RepoUVDeleteCmd struct {
Name string `arg:"" help:"Name of the unversioned file to delete"`
}
```
### func \(\*RepoUVDeleteCmd\) Run
```go
func (c *RepoUVDeleteCmd) Run(g *Globals) error
```
## type RepoUVGetCmd
RepoUVGetCmd retrieves an unversioned file.
```go
type RepoUVGetCmd struct {
Name string `arg:"" help:"Name of the unversioned file"`
Output string `short:"o" help:"Output file (default: stdout)"`
}
```
### func \(\*RepoUVGetCmd\) Run
```go
func (c *RepoUVGetCmd) Run(g *Globals) error
```
## type RepoUVLsCmd
RepoUVLsCmd lists unversioned files.
```go
type RepoUVLsCmd struct{}
```
### func \(\*RepoUVLsCmd\) Run
```go
func (c *RepoUVLsCmd) Run(g *Globals) error
```
## type RepoUVPutCmd
RepoUVPutCmd adds or updates an unversioned file.
```go
type RepoUVPutCmd struct {
Name string `arg:"" help:"Name of the unversioned file"`
File string `arg:"" help:"Local file to upload"`
}
```
### func \(\*RepoUVPutCmd\) Run
```go
func (c *RepoUVPutCmd) Run(g *Globals) error
```
## type RepoUndoCmd
RepoUndoCmd undoes the last checkout operation.
```go
type RepoUndoCmd struct {
Dir string `short:"d" help:"Checkout directory" default:"."`
}
```
### func \(\*RepoUndoCmd\) Run
```go
func (c *RepoUndoCmd) Run(g *Globals) error
```
## type RepoUserCmd
RepoUserCmd groups user management operations.
```go
type RepoUserCmd struct {
Add UserAddCmd `cmd:"" help:"Create a new user"`
List UserListCmd `cmd:"" help:"List all users"`
Update UserUpdateCmd `cmd:"" help:"Update user capabilities"`
Rm UserRmCmd `cmd:"" help:"Delete a user"`
Passwd UserPasswdCmd `cmd:"" help:"Reset user password"`
}
```
## type RepoVerifyCmd
RepoVerifyCmd verifies repository integrity: every held artifact against its name, delta chains, phantom records and the derived tables. It reports what the store lacks by class \(sparse\-cone §5.2\): transit phantoms, which sync still wants, apart from artifacts the store cone deliberately elides, which are healthy and never an error.
\-\-seed first verifies a structural store's seed by deepening \(draft\-fossil\-horizon\-00 §5.9\): the history below the horizon is fetched from \-\-remote, else the saved last\-sync\-url, and what it derives is compared with what the seed asserted. The store is left a content\-horizon store, which holds no seed.
```go
type RepoVerifyCmd struct {
RecoveryFlags
Seed bool `help:"Verify the structural horizon's seed by fetching the history below it (keeps the content horizon)"`
}
```
### func \(\*RepoVerifyCmd\) Run
```go
func (c *RepoVerifyCmd) Run(g *Globals) error
```
## type RepoWikiCmd
RepoWikiCmd groups wiki operations.
```go
type RepoWikiCmd struct {
Ls RepoWikiLsCmd `cmd:"" help:"List wiki pages"`
Export RepoWikiExportCmd `cmd:"" help:"Export a wiki page"`
}
```
## type RepoWikiExportCmd
RepoWikiExportCmd exports a wiki page.
```go
type RepoWikiExportCmd struct {
Page string `arg:"" help:"Wiki page name or artifact UUID"`
Output string `short:"o" help:"Output file (default: stdout)"`
}
```
### func \(\*RepoWikiExportCmd\) Run
```go
func (c *RepoWikiExportCmd) Run(g *Globals) error
```
## type RepoWikiLsCmd
RepoWikiLsCmd lists wiki pages.
```go
type RepoWikiLsCmd struct{}
```
### func \(\*RepoWikiLsCmd\) Run
```go
func (c *RepoWikiLsCmd) Run(g *Globals) error
```
## type UserAddCmd
UserAddCmd creates a new user.
```go
type UserAddCmd struct {
Login string `arg:"" help:"Username"`
Cap string `help:"Capability string (e.g. oi)" required:""`
}
```
### func \(\*UserAddCmd\) Run
```go
func (c *UserAddCmd) Run(g *Globals) error
```
## type UserListCmd
UserListCmd lists all users.
```go
type UserListCmd struct{}
```
### func \(\*UserListCmd\) Run
```go
func (c *UserListCmd) Run(g *Globals) error
```
## type UserPasswdCmd
UserPasswdCmd resets a user's password.
```go
type UserPasswdCmd struct {
Login string `arg:"" help:"Username"`
}
```
### func \(\*UserPasswdCmd\) Run
```go
func (c *UserPasswdCmd) Run(g *Globals) error
```
## type UserRmCmd
UserRmCmd deletes a user.
```go
type UserRmCmd struct {
Login string `arg:"" help:"Username"`
}
```
### func \(\*UserRmCmd\) Run
```go
func (c *UserRmCmd) Run(g *Globals) error
```
## type UserUpdateCmd
UserUpdateCmd updates user capabilities.
```go
type UserUpdateCmd struct {
Login string `arg:"" help:"Username"`
Cap string `help:"New capability string" required:""`
}
```
### func \(\*UserUpdateCmd\) Run
```go
func (c *UserUpdateCmd) Run(g *Globals) error
```
## type VersionCmd
VersionCmd prints a single\-line, stable, machine\-parseable build identifier and exits 0.
```go
type VersionCmd struct{}
```
### func \(\*VersionCmd\) Run
```go
func (c *VersionCmd) Run() error
```
Run prints Version\(\) and always succeeds: there is nothing about the local environment that can make reporting the build identifier fail.
Generated by [gomarkdoc]()
---
# db/driver/modernc
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/sdk/db/driver/modernc/
Pure-Go SQLite driver, the libfossil default.
```go
import _ "github.com/danmestas/go-libfossil/db/driver/modernc"
```
Importing this package registers a `modernc.org/sqlite` driver (driver name `"sqlite"`) with libfossil's `db` registry. The package exports no public API of its own — register-on-import is the contract.
Use this driver when you want a static, CGo-free build with no native dependencies.
See [db](/docs/reference/sdk/db/api/) for the registry interface.
---
# db/driver/ncruces
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/sdk/db/driver/ncruces/
Wasm-friendly SQLite driver based on `ncruces/go-sqlite3`.
```go
import _ "github.com/danmestas/go-libfossil/db/driver/ncruces"
```
Importing this package registers an `ncruces` driver (driver name `"sqlite3"`) with libfossil's `db` registry. The package exports no public API of its own — register-on-import is the contract.
On `js/wasm` targets the package additionally imports `go-sqlite3-opfs`, routing the OPFS virtual filesystem through the same driver registration so browser-side builds get persistent storage automatically.
Use this driver when you need a smaller Wasm footprint than `modernc` provides, or for build configurations where `ncruces`'s tradeoffs are preferred.
See [db](/docs/reference/sdk/db/api/) for the registry interface.
---
# db
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/sdk/db/api/
# db
```go
import "github.com/danmestas/go-libfossil/db"
```
Package db provides a SQLite database layer with pluggable drivers.
Two SQLite drivers ship with libfossil — modernc \(pure Go, default\) and ncruces \(WASM\-capable\). Select one at build time by importing its driver package:
```
import _ "github.com/danmestas/go-libfossil/db/driver/modernc"
```
[Open](<#Open>) and [OpenWith](<#OpenWith>) handle DSN construction, WAL/pragma setup, and WASM\-specific workarounds. Use [DB.WithTx](<#DB.WithTx>) for transaction scoping with automatic rollback on error.
The [Querier](<#Querier>) interface \(Exec, QueryRow, Query\) is satisfied by both [DB](<#DB>) and [Tx](<#Tx>), allowing callers to write transaction\-agnostic code.
## Index
- [Constants](<#constants>)
- [func CreateRepoSchema\(d \*DB\) error](<#CreateRepoSchema>)
- [func DefaultPragmas\(\) map\[string\]string](<#DefaultPragmas>)
- [func OpenSQL\(path string, cfg OpenConfig, params map\[string\]string\) \(\*sql.DB, error\)](<#OpenSQL>)
- [func ReceiptID\(q Querier\) \(int64, error\)](<#ReceiptID>)
- [func Register\(cfg DriverConfig\)](<#Register>)
- [func SQLDriverAndDSN\(path string, cfg OpenConfig, params map\[string\]string\) \(string, string\)](<#SQLDriverAndDSN>)
- [func ScanInt\(v any\) \(int, bool\)](<#ScanInt>)
- [func ScanJulianDay\(v any\) \(float64, bool\)](<#ScanJulianDay>)
- [func ScanTextJulianDay\(v any\) \(float64, bool\)](<#ScanTextJulianDay>)
- [func ScanTime\(v any\) \(time.Time, bool\)](<#ScanTime>)
- [func SeedConfig\(d \*DB, rng simio.Rand, projectCode string\) error](<#SeedConfig>)
- [func SeedDefaultUsers\(d \*DB, rng simio.Rand\) error](<#SeedDefaultUsers>)
- [func SeedNobody\(d \*DB, caps string\) error](<#SeedNobody>)
- [func SeedUser\(d \*DB, login string\) error](<#SeedUser>)
- [type CheckpointMode](<#CheckpointMode>)
- [func \(m CheckpointMode\) String\(\) string](<#CheckpointMode.String>)
- [type DB](<#DB>)
- [func Open\(path string\) \(\*DB, error\)](<#Open>)
- [func OpenWith\(path string, cfg OpenConfig\) \(\*DB, error\)](<#OpenWith>)
- [func \(d \*DB\) ApplicationID\(\) \(int32, error\)](<#DB.ApplicationID>)
- [func \(d \*DB\) Bound\(\) bool](<#DB.Bound>)
- [func \(d \*DB\) Checkpoint\(mode CheckpointMode\) error](<#DB.Checkpoint>)
- [func \(d \*DB\) Close\(\) error](<#DB.Close>)
- [func \(d \*DB\) Driver\(\) string](<#DB.Driver>)
- [func \(d \*DB\) Exec\(query string, args ...any\) \(sql.Result, error\)](<#DB.Exec>)
- [func \(d \*DB\) Path\(\) string](<#DB.Path>)
- [func \(d \*DB\) PoolExecs\(\) int64](<#DB.PoolExecs>)
- [func \(d \*DB\) Query\(query string, args ...any\) \(\*sql.Rows, error\)](<#DB.Query>)
- [func \(d \*DB\) QueryRow\(query string, args ...any\) \*sql.Row](<#DB.QueryRow>)
- [func \(d \*DB\) SetApplicationID\(id int32\) error](<#DB.SetApplicationID>)
- [func \(d \*DB\) SqlDB\(\) \*sql.DB](<#DB.SqlDB>)
- [func \(d \*DB\) UseReceipt\(rc \*Receipt\) \*Receipt](<#DB.UseReceipt>)
- [func \(d \*DB\) WithBoundReadTx\(fn func\(bound \*DB\) error\) error](<#DB.WithBoundReadTx>)
- [func \(d \*DB\) WithBoundTx\(fn func\(bound \*DB\) error\) error](<#DB.WithBoundTx>)
- [func \(d \*DB\) WithReadTx\(fn func\(tx \*Tx\) error\) error](<#DB.WithReadTx>)
- [func \(d \*DB\) WithTx\(fn func\(tx \*Tx\) error\) error](<#DB.WithTx>)
- [func \(d \*DB\) WriteTxns\(\) int64](<#DB.WriteTxns>)
- [func \(d \*DB\) Writes\(\) int64](<#DB.Writes>)
- [type DriverConfig](<#DriverConfig>)
- [func RegisteredDriver\(\) \*DriverConfig](<#RegisteredDriver>)
- [type OpenConfig](<#OpenConfig>)
- [type Querier](<#Querier>)
- [type Receipt](<#Receipt>)
- [func \(rc \*Receipt\) ID\(q Querier\) \(int64, error\)](<#Receipt.ID>)
- [type Tx](<#Tx>)
- [func \(t \*Tx\) Exec\(query string, args ...any\) \(sql.Result, error\)](<#Tx.Exec>)
- [func \(t \*Tx\) Query\(query string, args ...any\) \(\*sql.Rows, error\)](<#Tx.Query>)
- [func \(t \*Tx\) QueryRow\(query string, args ...any\) \*sql.Row](<#Tx.QueryRow>)
- [func \(t \*Tx\) UseReceipt\(rc \*Receipt\) \*Receipt](<#Tx.UseReceipt>)
- [func \(t \*Tx\) WithSavepoint\(fn func\(tx \*Tx\) error\) error](<#Tx.WithSavepoint>)
## Constants
Fossil's hash\-policy config values, in Fossil's own order \(see \`fossil help hash\-policy\`\). The row holds the integer as text.
Naming follows from the value: SHA1 below HashPolicySHA3, SHA3 at or above it, except HashPolicyAuto, which names with SHA1 until a SHA3 artifact enters the repo. From HashPolicySHA3Only up, an artifact already stored under a legacy SHA1 name is not reused.
```go
const (
HashPolicySHA1 = 0
HashPolicyAuto = 1
HashPolicySHA3 = 2
HashPolicySHA3Only = 3
HashPolicyShunSHA1 = 4
)
```
## func CreateRepoSchema
```go
func CreateRepoSchema(d *DB) error
```
## func DefaultPragmas
```go
func DefaultPragmas() map[string]string
```
DefaultPragmas returns the default pragma settings.
## func OpenSQL
```go
func OpenSQL(path string, cfg OpenConfig, params map[string]string) (*sql.DB, error)
```
OpenSQL opens a raw \*sql.DB using the registered driver and the same DSN construction as OpenWith. params are appended as URI query parameters after driver pragmas, so callers can request options such as mode=ro without constructing driver\-specific DSNs.
## func ReceiptID
```go
func ReceiptID(q Querier) (int64, error)
```
ReceiptID returns the rcvid content stored through q is recorded under: the receipt of q's transaction, which is the one given by UseReceipt or, when none was, one made for that transaction alone. Content stored outside a transaction is a receipt of its own.
## func Register
```go
func Register(cfg DriverConfig)
```
Register registers a SQLite driver for use by Open/OpenWith. Must be called exactly once \(typically from a driver package's init\(\)\). Panics if called more than once.
## func SQLDriverAndDSN
```go
func SQLDriverAndDSN(path string, cfg OpenConfig, params map[string]string) (string, string)
```
SQLDriverAndDSN returns the driver name and DSN OpenWith would use, with optional URI query params appended after driver\-specific options.
## func ScanInt
```go
func ScanInt(v any) (int, bool)
```
ScanInt converts a scanned value to int. SQLite drivers differ on BOOLEAN columns: modernc returns int64, ncruces returns bool. Scan the column into an \`any\` variable, then pass it here.
## func ScanJulianDay
```go
func ScanJulianDay(v any) (float64, bool)
```
ScanJulianDay converts a scanned mtime value to a float64 Julian Day Number. SQLite drivers return mtime differently: modernc returns float64, ncruces returns time.Time for DATETIME/TIMESTAMP/DATE columns. Scan the column into an \`any\` variable, then pass it here.
The float64 branch returns the scanned value verbatim, and that exactness is load\-bearing rather than incidental. A float64 read off the row IS the stored value, so any "canonicalizing" transform can only move it away from what the row holds. Two consumers compare a scanned mtime against other stored mtimes and would silently return a wrong answer \(never an error\) if the value were perturbed even by one ulp:
- Timeline's pagination cursor, whose predicate \`e.mtime \< ? OR \(e.mtime = ? AND b.rid \< ?\)\` needs the cursor to compare exactly equal to the row it came from. Raise it by an ulp and that row repeats as the next page's first row; lower it and rows are skipped.
- tag propagation's \`tagxref.mtime \< ?\`, where SQLite compares the bound value against raw stored floats.
Neither is hypothetical for values this library did not write: a value another writer computed differently, or at finer than millisecond resolution, is not on the grid TimeToJulian writes, and snapping it to that grid changes it.
The time.Time branch is the one case where a transform is required rather than harmful — see timeToJulian.
## func ScanTextJulianDay
```go
func ScanTextJulianDay(v any) (float64, bool)
```
ScanTextJulianDay is ScanJulianDay for a column fossil writes as SQL text: event, plink, attachment and forumpost times. Those hold the 16\-digit form \(fsltype.TextJulianDayFromMillis\), so a time.Time a driver hands back is converted to that form rather than to the exact julianday\(\) ScanJulianDay gives for tagxref.mtime. A float64 passes through unchanged either way.
## func ScanTime
```go
func ScanTime(v any) (time.Time, bool)
```
ScanTime converts a scanned mtime value to time.Time, at the millisecond SQLite's date functions give it: the time a check\-in's D card records. Same driver\-compatibility rationale as ScanJulianDay.
## func SeedConfig
```go
func SeedConfig(d *DB, rng simio.Rand, projectCode string) error
```
SeedConfig seeds the config table with project\-code, server\-code, the schema markers, and hash\-policy. If projectCode is empty, a fresh 40\-char lowercase\-hex project\-code is generated from rng. If non\-empty, it must match ^\[0\-9a\-f\]\{40\}$; otherwise an error is returned and no rows are written. Server\-code is always generated.
hash\-policy is seeded to sha3, which is what \`fossil init\` writes and so what a repo created here has to say to name its artifacts the same way. A clone is the exception: it adopts its source's policy, not this one, so sync.Clone drops the row the same way it drops project\-code.
## func SeedDefaultUsers
```go
func SeedDefaultUsers(d *DB, rng simio.Rand) error
```
SeedDefaultUsers inserts the four built\-in users \`fossil new\` creates, with fossil's capabilities \(src/db.c db\_create\_default\_users\). nobody's set is what every unauthenticated sync request is granted, so it is clone, read and zip only: check\-in rights must be granted explicitly. developer and reader are templates other users inherit through v and u. anonymous gets a random password, as fossil gives it; the others have none, so no login can reach them \(VerifyLogin requires a stored password\).
## func SeedNobody
```go
func SeedNobody(d *DB, caps string) error
```
SeedNobody inserts a "nobody" user with the given capabilities. This controls anonymous access policy for the repo. A new repository gets fossil's built\-in users from SeedDefaultUsers instead.
## func SeedUser
```go
func SeedUser(d *DB, login string) error
```
## type CheckpointMode
CheckpointMode mirrors SQLite's PRAGMA wal\_checkpoint\(\\) argument.
```go
type CheckpointMode int
```
```go
const (
// CheckpointPassive checkpoints frames not held by readers; never blocks.
// Appropriate for periodic background checkpoints.
CheckpointPassive CheckpointMode = iota
// CheckpointFull blocks new writers until all frames are checkpointed.
CheckpointFull
// CheckpointRestart is FULL plus restarts the WAL file.
CheckpointRestart
// CheckpointTruncate is RESTART plus truncates the WAL file to zero bytes.
// Produces the most compact on-disk file and is what Close uses.
CheckpointTruncate
)
```
### func \(CheckpointMode\) String
```go
func (m CheckpointMode) String() string
```
## type DB
DB wraps a SQLite database connection.
```go
type DB struct {
// contains filtered or unexported fields
}
```
### func Open
```go
func Open(path string) (*DB, error)
```
Open opens a SQLite database with the registered driver and default pragmas.
### func OpenWith
```go
func OpenWith(path string, cfg OpenConfig) (*DB, error)
```
OpenWith opens a SQLite database with explicit configuration.
### func \(\*DB\) ApplicationID
```go
func (d *DB) ApplicationID() (int32, error)
```
### func \(\*DB\) Bound
```go
func (d *DB) Bound() bool
```
Bound reports whether d is a DB WithBoundTx handed its callback, whose statements run in a transaction that d does not own.
### func \(\*DB\) Checkpoint
```go
func (d *DB) Checkpoint(mode CheckpointMode) error
```
Checkpoint runs PRAGMA wal\_checkpoint\(\\) against the database. Safe to call on a live database. PASSIVE never blocks; TRUNCATE produces the most compact on\-disk layout. On a non\-WAL database the underlying PRAGMA is a no\-op and returns nil.
### func \(\*DB\) Close
```go
func (d *DB) Close() error
```
Close runs PRAGMA wal\_checkpoint\(TRUNCATE\) before closing the underlying connection so the on\-disk file is readable by external SQLite/fossil tooling. The WASM/WASI build path does not use WAL, so the checkpoint is skipped there. Checkpoint errors are joined with the close error so the connection is always closed regardless of checkpoint outcome.
### func \(\*DB\) Driver
```go
func (d *DB) Driver() string
```
### func \(\*DB\) Exec
```go
func (d *DB) Exec(query string, args ...any) (sql.Result, error)
```
### func \(\*DB\) Path
```go
func (d *DB) Path() string
```
### func \(\*DB\) PoolExecs
```go
func (d *DB) PoolExecs() int64
```
PoolExecs returns how many statements have been executed on the pool rather than inside a transaction. Each one runs in its own implicit transaction, so it costs a BEGIN IMMEDIATE and an fsync of its own and contests the database's single write lock independently of every other. A path that issues one per artifact therefore degrades from slow to failing the moment anything else writes, which is what issue \#200 cost a large pull. Tests pin the per\-round budget against this counter.
### func \(\*DB\) Query
```go
func (d *DB) Query(query string, args ...any) (*sql.Rows, error)
```
### func \(\*DB\) QueryRow
```go
func (d *DB) QueryRow(query string, args ...any) *sql.Row
```
### func \(\*DB\) SetApplicationID
```go
func (d *DB) SetApplicationID(id int32) error
```
### func \(\*DB\) SqlDB
```go
func (d *DB) SqlDB() *sql.DB
```
SqlDB returns the underlying \*sql.DB connection.
### func \(\*DB\) UseReceipt
```go
func (d *DB) UseReceipt(rc *Receipt) *Receipt
```
UseReceipt is Tx.UseReceipt for the transaction a bound DB runs in. Only a bound DB has a transaction to give it to.
### func \(\*DB\) WithBoundReadTx
```go
func (d *DB) WithBoundReadTx(fn func(bound *DB) error) error
```
WithBoundReadTx is WithBoundTx over a read transaction \(see WithReadTx\): a DB bound to one snapshot that takes no write lock. fn must not write.
### func \(\*DB\) WithBoundTx
```go
func (d *DB) WithBoundTx(fn func(bound *DB) error) error
```
WithBoundTx runs fn in one write transaction and hands it a DB bound to that transaction. Every statement on the bound DB runs in the transaction, and every WithTx or WithBoundTx on it is a savepoint there, so code written against a \*DB, including code that opens its own transactions, joins this one instead of contending for the write lock from a second connection. The transaction commits when fn returns nil and rolls back everything fn did otherwise. The bound DB is valid only until fn returns, and it cannot be closed or checkpointed, nor hand out its \*sql.DB. On a DB that is itself bound, WithBoundTx is a savepoint of that transaction, as WithTx is.
### func \(\*DB\) WithReadTx
```go
func (d *DB) WithReadTx(fn func(tx *Tx) error) error
```
WithReadTx runs fn in a read transaction: one snapshot of the database, taken without the write lock, so it neither waits for a writer nor holds one up. fn must not write; the drivers refuse or contest a write in it. The transaction ends when fn returns, with nothing to commit. On a bound DB it runs fn in the bound transaction, as WithTx does.
### func \(\*DB\) WithTx
```go
func (d *DB) WithTx(fn func(tx *Tx) error) error
```
WithTx runs fn in a write transaction, which commits when fn returns nil and rolls back otherwise. On a DB that WithBoundTx bound, it runs fn in a savepoint of the bound transaction instead, so it neither waits for a second connection nor commits on its own: fn's error undoes only fn's writes, and the bound transaction decides whether the rest commits.
### func \(\*DB\) WriteTxns
```go
func (d *DB) WriteTxns() int64
```
WriteTxns counts transactions begun through WithTx. Each is its own BEGIN IMMEDIATE and fsync, and each takes the single write lock independently, so a path that opens one per artifact is both slow and fragile under contention \-\- the cost issue \#200 measured at roughly three per received file. Tests pin the per\-round transaction budget against this, which PoolExecs cannot see: moving a per\-artifact write from the pool into its own transaction leaves PoolExecs unchanged while the real cost stays.
### func \(\*DB\) Writes
```go
func (d *DB) Writes() int64
```
Writes counts writes of this DB that have finished: pool Exec calls that returned and WithTx transactions that ended, committed or not. A reader that keeps what it read across calls compares two counts to learn whether a write may have landed in between; counting at the end, not the start, is what makes an unchanged count mean that. A write through another handle on the same file, in this process or another, is not counted.
## type DriverConfig
DriverConfig defines a SQLite driver's name and DSN builder.
```go
type DriverConfig struct {
Name string
BuildDSN func(path string, pragmas map[string]string) string
}
```
### func RegisteredDriver
```go
func RegisteredDriver() *DriverConfig
```
RegisteredDriver returns a copy of the currently registered driver config, or nil if none.
## type OpenConfig
OpenConfig allows callers to customize driver selection and pragmas.
```go
type OpenConfig struct {
Driver string // override driver name (empty = use registered driver)
Pragmas map[string]string // additional/override pragmas (merged with defaults)
}
```
## type Querier
Querier is the common interface satisfied by both \*DB and \*Tx. Functions that need to work inside transactions accept Querier instead of \*DB.
```go
type Querier interface {
Exec(query string, args ...any) (sql.Result, error)
QueryRow(query string, args ...any) *sql.Row
Query(query string, args ...any) (*sql.Rows, error)
}
```
## type Receipt
Receipt is one arrival of content in the repository: a commit, a sync or clone, a push a server accepts. Fossil records each as a row of rcvfrom, made the first time that arrival stores anything \(content\_rcvid\_init in src/content.c\), and every blob or unversioned file it stores names the row in its rcvid. A receipt that stores nothing leaves no row.
The fields are what the row will hold, read when it is made: UID is the user.uid the content came from, Nonce the nonce of the login card that authenticated it, and IPAddr the peer's address or, for local content, a description of its source. An empty Nonce or IPAddr is stored as NULL, as fossil stores an absent one. The row's mtime is the Julian day of the moment it is made, by Clock \(nil means real time\).
A Receipt is not safe for concurrent use.
```go
type Receipt struct {
UID int64
Nonce string
IPAddr string
Clock simio.Clock
// contains filtered or unexported fields
}
```
### func \(\*Receipt\) ID
```go
func (rc *Receipt) ID(q Querier) (int64, error)
```
ID returns the rcvid of the receipt's row, making the row through q if the receipt has none yet. Fossil holds that id for its process in g.rcvid and never looks at the row again \(src/content.c, content\_rcvid\_init\); a fossil whose transaction rolls back ends with it. A Receipt outlives transactions, so the transaction or savepoint that makes its row also forgets it if it rolls back \(Tx.forgetFrom\), and the next use makes a fresh row rather than naming one some other insert may since have taken. A kept id costs no query.
A row made through a Querier that is neither a Tx nor a DB is taken as kept; a row made in one transaction should not be used from another connection before that transaction commits.
## type Tx
```go
type Tx struct {
// contains filtered or unexported fields
}
```
### func \(\*Tx\) Exec
```go
func (t *Tx) Exec(query string, args ...any) (sql.Result, error)
```
### func \(\*Tx\) Query
```go
func (t *Tx) Query(query string, args ...any) (*sql.Rows, error)
```
### func \(\*Tx\) QueryRow
```go
func (t *Tx) QueryRow(query string, args ...any) *sql.Row
```
### func \(\*Tx\) UseReceipt
```go
func (t *Tx) UseReceipt(rc *Receipt) *Receipt
```
UseReceipt makes rc the receipt content stored in t is recorded under, and returns the one it replaces so a caller can put it back.
### func \(\*Tx\) WithSavepoint
```go
func (t *Tx) WithSavepoint(fn func(tx *Tx) error) error
```
WithSavepoint runs fn in a savepoint of t, as WithTx does on a DB bound to a transaction: fn's error undoes fn's writes alone and is returned, leaving t open with the writes before it.
Generated by [gomarkdoc]()
---
# dst
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/sdk/dst/api/
# dst
```go
import "github.com/danmestas/go-libfossil/dst"
```
## Index
- [func AllBlobUUIDsSorted\(r \*repo.Repo\) \(\[\]string, error\)](<#AllBlobUUIDsSorted>)
- [func CheckBlobIntegrity\(nodeID string, r \*repo.Repo\) error](<#CheckBlobIntegrity>)
- [func CheckConvergence\(master \*repo.Repo, leaves map\[NodeID\]\*repo.Repo\) error](<#CheckConvergence>)
- [func CheckDeltaChains\(nodeID string, r \*repo.Repo\) error](<#CheckDeltaChains>)
- [func CheckNoOrphanPhantoms\(nodeID string, r \*repo.Repo\) error](<#CheckNoOrphanPhantoms>)
- [func CheckSubsetOf\(master \*repo.Repo, leaves map\[NodeID\]\*repo.Repo\) error](<#CheckSubsetOf>)
- [func CheckTableSyncConvergence\(repos map\[string\]\*repo.Repo\) error](<#CheckTableSyncConvergence>)
- [func CheckTableSyncIntegrity\(nodeID string, r \*repo.Repo\) error](<#CheckTableSyncIntegrity>)
- [func CheckTagxrefIntegrity\(nodeID string, r \*repo.Repo\) error](<#CheckTagxrefIntegrity>)
- [func CheckUVConvergence\(master \*repo.Repo, leaves map\[NodeID\]\*repo.Repo\) error](<#CheckUVConvergence>)
- [func CheckUVIntegrity\(nodeID string, r \*repo.Repo\) error](<#CheckUVIntegrity>)
- [func CountBlobs\(r \*repo.Repo\) \(int, error\)](<#CountBlobs>)
- [func HasBlob\(r \*repo.Repo, uuid string\) bool](<#HasBlob>)
- [type Action](<#Action>)
- [type ActionType](<#ActionType>)
- [type DefaultNode](<#DefaultNode>)
- [func NewDefaultNode\(r \*repo.Repo, t sync.Transport, projectCode, serverCode string, opts DefaultNodeOpts\) \*DefaultNode](<#NewDefaultNode>)
- [func \(n \*DefaultNode\) Repo\(\) \*repo.Repo](<#DefaultNode.Repo>)
- [func \(n \*DefaultNode\) Tick\(ctx context.Context, event EventType\) Action](<#DefaultNode.Tick>)
- [type DefaultNodeOpts](<#DefaultNodeOpts>)
- [type Event](<#Event>)
- [type EventQueue](<#EventQueue>)
- [func \(q EventQueue\) Len\(\) int](<#EventQueue.Len>)
- [func \(q EventQueue\) Less\(i, j int\) bool](<#EventQueue.Less>)
- [func \(q \*EventQueue\) Pop\(\) any](<#EventQueue.Pop>)
- [func \(q \*EventQueue\) PopEvent\(\) \*Event](<#EventQueue.PopEvent>)
- [func \(q \*EventQueue\) Push\(x any\)](<#EventQueue.Push>)
- [func \(q \*EventQueue\) PushEvent\(e \*Event\)](<#EventQueue.PushEvent>)
- [func \(q EventQueue\) Swap\(i, j int\)](<#EventQueue.Swap>)
- [type EventType](<#EventType>)
- [type InvariantError](<#InvariantError>)
- [func \(e \*InvariantError\) Error\(\) string](<#InvariantError.Error>)
- [type MockFossil](<#MockFossil>)
- [func NewMockFossil\(r \*repo.Repo\) \*MockFossil](<#NewMockFossil>)
- [func \(f \*MockFossil\) Exchange\(ctx context.Context, req \*xfer.Message\) \(\*xfer.Message, error\)](<#MockFossil.Exchange>)
- [func \(f \*MockFossil\) Repo\(\) \*repo.Repo](<#MockFossil.Repo>)
- [func \(f \*MockFossil\) SetBuggify\(b libsync.BuggifyChecker\)](<#MockFossil.SetBuggify>)
- [func \(f \*MockFossil\) StoreArtifact\(data \[\]byte\) \(string, error\)](<#MockFossil.StoreArtifact>)
- [type Node](<#Node>)
- [type NodeID](<#NodeID>)
- [type PeerNetwork](<#PeerNetwork>)
- [func NewPeerNetwork\(rng \*rand.Rand\) \*PeerNetwork](<#NewPeerNetwork>)
- [func \(n \*PeerNetwork\) AddPeer\(id NodeID, r \*repo.Repo\)](<#PeerNetwork.AddPeer>)
- [func \(n \*PeerNetwork\) Heal\(id NodeID\)](<#PeerNetwork.Heal>)
- [func \(n \*PeerNetwork\) HealAll\(\)](<#PeerNetwork.HealAll>)
- [func \(n \*PeerNetwork\) Partition\(id NodeID\)](<#PeerNetwork.Partition>)
- [func \(n \*PeerNetwork\) SetBuggify\(b libsync.BuggifyChecker\)](<#PeerNetwork.SetBuggify>)
- [func \(n \*PeerNetwork\) SetDropRate\(rate float64\)](<#PeerNetwork.SetDropRate>)
- [func \(n \*PeerNetwork\) Transport\(source, target NodeID\) \*PeerTransport](<#PeerNetwork.Transport>)
- [type PeerTransport](<#PeerTransport>)
- [func \(t \*PeerTransport\) Exchange\(ctx context.Context, req \*xfer.Message\) \(\*xfer.Message, error\)](<#PeerTransport.Exchange>)
- [type SeededBuggify](<#SeededBuggify>)
- [func \(b \*SeededBuggify\) Check\(\_ string, probability float64\) bool](<#SeededBuggify.Check>)
- [type SimConfig](<#SimConfig>)
- [type SimNetwork](<#SimNetwork>)
- [func NewSimNetwork\(rng \*rand.Rand, upstream libsync.Transport\) \*SimNetwork](<#NewSimNetwork>)
- [func \(n \*SimNetwork\) Heal\(id NodeID\)](<#SimNetwork.Heal>)
- [func \(n \*SimNetwork\) HealAll\(\)](<#SimNetwork.HealAll>)
- [func \(n \*SimNetwork\) Partition\(id NodeID\)](<#SimNetwork.Partition>)
- [func \(n \*SimNetwork\) SetDropRate\(rate float64\)](<#SimNetwork.SetDropRate>)
- [func \(n \*SimNetwork\) SetTruncateRate\(rate float64\)](<#SimNetwork.SetTruncateRate>)
- [func \(n \*SimNetwork\) Transport\(nodeID NodeID\) \*SimTransport](<#SimNetwork.Transport>)
- [type SimTransport](<#SimTransport>)
- [func \(t \*SimTransport\) Exchange\(ctx context.Context, req \*xfer.Message\) \(\*xfer.Message, error\)](<#SimTransport.Exchange>)
- [type Simulator](<#Simulator>)
- [func New\(cfg SimConfig\) \(\*Simulator, error\)](<#New>)
- [func \(s \*Simulator\) CheckAllConverged\(master \*repo.Repo\) error](<#Simulator.CheckAllConverged>)
- [func \(s \*Simulator\) CheckAllTableSyncConverged\(\) error](<#Simulator.CheckAllTableSyncConverged>)
- [func \(s \*Simulator\) CheckAllUVConverged\(master \*repo.Repo\) error](<#Simulator.CheckAllUVConverged>)
- [func \(s \*Simulator\) CheckSafety\(\) error](<#Simulator.CheckSafety>)
- [func \(s \*Simulator\) CheckTombstoneConvergence\(t \*testing.T, masterRepo \*repo.Repo, tableName string, def repo.TableDef\)](<#Simulator.CheckTombstoneConvergence>)
- [func \(s \*Simulator\) Clock\(\) \*simio.SimClock](<#Simulator.Clock>)
- [func \(s \*Simulator\) Close\(\) error](<#Simulator.Close>)
- [func \(s \*Simulator\) Leaf\(id NodeID\) Node](<#Simulator.Leaf>)
- [func \(s \*Simulator\) LeafIDs\(\) \[\]NodeID](<#Simulator.LeafIDs>)
- [func \(s \*Simulator\) Network\(\) \*SimNetwork](<#Simulator.Network>)
- [func \(s \*Simulator\) Run\(maxSteps int\) error](<#Simulator.Run>)
- [func \(s \*Simulator\) RunUntil\(deadline time.Time\) error](<#Simulator.RunUntil>)
- [func \(s \*Simulator\) ScheduleSyncNow\(id NodeID\)](<#Simulator.ScheduleSyncNow>)
- [func \(s \*Simulator\) ScheduleUVDelete\(id NodeID, at time.Time, name string, mtime int64\)](<#Simulator.ScheduleUVDelete>)
- [func \(s \*Simulator\) ScheduleUVWrite\(id NodeID, at time.Time, name string, data \[\]byte, mtime int64\)](<#Simulator.ScheduleUVWrite>)
- [func \(s \*Simulator\) SetMasterRepo\(r \*repo.Repo\)](<#Simulator.SetMasterRepo>)
- [func \(s \*Simulator\) Step\(\) \(bool, error\)](<#Simulator.Step>)
## func AllBlobUUIDsSorted
```go
func AllBlobUUIDsSorted(r *repo.Repo) ([]string, error)
```
AllBlobUUIDsSorted returns a sorted slice of UUIDs for deterministic comparison.
## func CheckBlobIntegrity
```go
func CheckBlobIntegrity(nodeID string, r *repo.Repo) error
```
CheckBlobIntegrity verifies that every blob in the repo has a UUID matching the hash of its expanded content. This catches corruption from buggify \(content.Expand byte\-flip\) or storage bugs.
## func CheckConvergence
```go
func CheckConvergence(master *repo.Repo, leaves map[NodeID]*repo.Repo) error
```
CheckConvergence verifies that every leaf repo contains the same set of artifact UUIDs as the master repo.
## func CheckDeltaChains
```go
func CheckDeltaChains(nodeID string, r *repo.Repo) error
```
CheckDeltaChains verifies that every delta's srcid points to an existing blob \(no dangling references\).
## func CheckNoOrphanPhantoms
```go
func CheckNoOrphanPhantoms(nodeID string, r *repo.Repo) error
```
CheckNoOrphanPhantoms verifies that phantom entries reference blobs that actually exist in the blob table \(they're just missing content\).
## func CheckSubsetOf
```go
func CheckSubsetOf(master *repo.Repo, leaves map[NodeID]*repo.Repo) error
```
CheckSubsetOf verifies that all artifacts in the master are present in every leaf. Unlike CheckConvergence, this allows leaves to have extra artifacts \(useful when only pull is being tested\).
## func CheckTableSyncConvergence
```go
func CheckTableSyncConvergence(repos map[string]*repo.Repo) error
```
CheckTableSyncConvergence verifies that all repos have identical rows for every shared synced table. Tables present in some repos but not others are skipped \(schema may still be propagating\).
## func CheckTableSyncIntegrity
```go
func CheckTableSyncIntegrity(nodeID string, r *repo.Repo) error
```
CheckTableSyncIntegrity verifies that: 1. Every row in every synced table has a valid PK hash. 2. Every row's mtime is positive. 3. The computed catalog hash is 40 hex chars.
## func CheckTagxrefIntegrity
```go
func CheckTagxrefIntegrity(nodeID string, r *repo.Repo) error
```
CheckTagxrefIntegrity verifies that: 1. Every tagxref.rid references a valid blob 2. Every tagxref.tagid references a valid tag 3. Propagated entries \(srcid=0\) have tagtype=2 4. No tagxref.rid references a phantom blob
## func CheckUVConvergence
```go
func CheckUVConvergence(master *repo.Repo, leaves map[NodeID]*repo.Repo) error
```
CheckUVConvergence verifies that all repos have identical UV content hashes.
## func CheckUVIntegrity
```go
func CheckUVIntegrity(nodeID string, r *repo.Repo) error
```
CheckUVIntegrity verifies that every non\-tombstone entry in the unversioned table has a hash matching the SHA1 of its decompressed content, and sz matches the content length.
## func CountBlobs
```go
func CountBlobs(r *repo.Repo) (int, error)
```
CountBlobs returns the number of non\-phantom blobs in the repo.
## func HasBlob
```go
func HasBlob(r *repo.Repo, uuid string) bool
```
HasBlob checks if a specific artifact exists in the repo.
## type Action
Action is the result of processing a single event via Tick.
```go
type Action struct {
Type ActionType
Result *sync.SyncResult // non-nil when Type == ActionSynced
Err error // non-nil on sync failure
}
```
## type ActionType
ActionType classifies the result of processing an event.
```go
type ActionType int
```
```go
const (
ActionNone ActionType = iota
ActionSynced // a sync round was executed
)
```
## type DefaultNode
DefaultNode is a simple Node that calls sync.Sync directly. No NATS, no HTTP — just a repo \+ transport.
```go
type DefaultNode struct {
// contains filtered or unexported fields
}
```
### func NewDefaultNode
```go
func NewDefaultNode(r *repo.Repo, t sync.Transport, projectCode, serverCode string, opts DefaultNodeOpts) *DefaultNode
```
NewDefaultNode creates a Node backed by sync.Sync.
### func \(\*DefaultNode\) Repo
```go
func (n *DefaultNode) Repo() *repo.Repo
```
Repo returns the node's Fossil repository.
### func \(\*DefaultNode\) Tick
```go
func (n *DefaultNode) Tick(ctx context.Context, event EventType) Action
```
Tick executes a sync cycle for the given event.
## type DefaultNodeOpts
DefaultNodeOpts configures a DefaultNode.
```go
type DefaultNodeOpts struct {
Push bool
Pull bool
UV bool
XTableSync bool
Private bool
Buggify sync.BuggifyChecker
}
```
## type Event
Event is a scheduled occurrence in the simulation.
```go
type Event struct {
Time time.Time
Type EventType
NodeID NodeID
UVName string // for EvUVWrite/EvUVDelete: file name
UVData []byte // for EvUVWrite: file content
UVMTime int64 // for EvUVWrite/EvUVDelete: mtime
}
```
## type EventQueue
EventQueue is a min\-heap of events ordered by time.
```go
type EventQueue []*Event
```
### func \(EventQueue\) Len
```go
func (q EventQueue) Len() int
```
### func \(EventQueue\) Less
```go
func (q EventQueue) Less(i, j int) bool
```
### func \(\*EventQueue\) Pop
```go
func (q *EventQueue) Pop() any
```
### func \(\*EventQueue\) PopEvent
```go
func (q *EventQueue) PopEvent() *Event
```
PopEvent removes and returns the earliest event.
### func \(\*EventQueue\) Push
```go
func (q *EventQueue) Push(x any)
```
### func \(\*EventQueue\) PushEvent
```go
func (q *EventQueue) PushEvent(e *Event)
```
PushEvent adds an event to the queue.
### func \(EventQueue\) Swap
```go
func (q EventQueue) Swap(i, j int)
```
## type EventType
EventType classifies simulation events.
```go
type EventType int
```
```go
const (
EvTimer EventType = iota // leaf poll timer fired
EvSyncNow // manual sync trigger for a leaf
EvUVWrite // write a UV file to a node's repo
EvUVDelete // delete a UV file from a node's repo
)
```
## type InvariantError
InvariantError records which invariant failed, on which node, and why.
```go
type InvariantError struct {
Invariant string
NodeID string // "master" or leaf ID
Detail string
}
```
### func \(\*InvariantError\) Error
```go
func (e *InvariantError) Error() string
```
## type MockFossil
MockFossil simulates a Fossil master server using the real HandleSync handler. It manages its own repo and implements sync.Transport by dispatching xfer messages to HandleSyncWithOpts.
```go
type MockFossil struct {
// contains filtered or unexported fields
}
```
### func NewMockFossil
```go
func NewMockFossil(r *repo.Repo) *MockFossil
```
NewMockFossil creates a MockFossil backed by the given repo.
### func \(\*MockFossil\) Exchange
```go
func (f *MockFossil) Exchange(ctx context.Context, req *xfer.Message) (*xfer.Message, error)
```
Exchange handles one xfer request/response round by delegating to the real HandleSyncWithOpts. This ensures the DST tests exercise the same code path as production servers.
### func \(\*MockFossil\) Repo
```go
func (f *MockFossil) Repo() *repo.Repo
```
Repo returns the mock fossil's repository \(for seeding and invariants\).
### func \(\*MockFossil\) SetBuggify
```go
func (f *MockFossil) SetBuggify(b libsync.BuggifyChecker)
```
SetBuggify configures fault injection for the handler.
### func \(\*MockFossil\) StoreArtifact
```go
func (f *MockFossil) StoreArtifact(data []byte) (string, error)
```
StoreArtifact adds a raw artifact to the mock fossil's repo. Returns the UUID. Used by tests to seed the master with content.
## type Node
Node represents a sync participant in the simulation. The simulator drives events by calling Tick; the Node executes a sync cycle and reports the result. EdgeSync can implement this by wrapping its agent.Agent; the DST provides a DefaultNode for in\-module testing.
```go
type Node interface {
Tick(ctx context.Context, event EventType) Action
Repo() *repo.Repo
}
```
## type NodeID
NodeID identifies a node in the simulation.
```go
type NodeID string
```
## type PeerNetwork
PeerNetwork simulates a peer\-to\-peer network where each leaf's Exchange calls HandleSync on the designated peer's repo. No bridge, no central server — pure leaf\-to\-leaf sync under DST control.
```go
type PeerNetwork struct {
// contains filtered or unexported fields
}
```
### func NewPeerNetwork
```go
func NewPeerNetwork(rng *rand.Rand) *PeerNetwork
```
NewPeerNetwork creates a simulated peer network.
### func \(\*PeerNetwork\) AddPeer
```go
func (n *PeerNetwork) AddPeer(id NodeID, r *repo.Repo)
```
AddPeer registers a leaf's repo as a sync target for other leaves.
### func \(\*PeerNetwork\) Heal
```go
func (n *PeerNetwork) Heal(id NodeID)
```
Heal removes a node from the partition set.
### func \(\*PeerNetwork\) HealAll
```go
func (n *PeerNetwork) HealAll()
```
HealAll removes all partitions.
### func \(\*PeerNetwork\) Partition
```go
func (n *PeerNetwork) Partition(id NodeID)
```
Partition isolates a node — all messages to/from it are dropped.
### func \(\*PeerNetwork\) SetBuggify
```go
func (n *PeerNetwork) SetBuggify(b libsync.BuggifyChecker)
```
SetBuggify configures fault injection for the handler.
### func \(\*PeerNetwork\) SetDropRate
```go
func (n *PeerNetwork) SetDropRate(rate float64)
```
SetDropRate sets the probability that any message is dropped.
### func \(\*PeerNetwork\) Transport
```go
func (n *PeerNetwork) Transport(source, target NodeID) *PeerTransport
```
Transport returns a sync.Transport for the given source node that routes to a specific target peer's HandleSync.
## type PeerTransport
PeerTransport implements sync.Transport by routing through the PeerNetwork to a specific target peer.
```go
type PeerTransport struct {
// contains filtered or unexported fields
}
```
### func \(\*PeerTransport\) Exchange
```go
func (t *PeerTransport) Exchange(ctx context.Context, req *xfer.Message) (*xfer.Message, error)
```
Exchange sends a request through the peer network to the target's HandleSync.
## type SeededBuggify
SeededBuggify implements sync.BuggifyChecker with a deterministic PRNG.
```go
type SeededBuggify struct {
// contains filtered or unexported fields
}
```
### func \(\*SeededBuggify\) Check
```go
func (b *SeededBuggify) Check(_ string, probability float64) bool
```
Check returns true with the given probability, using the seeded PRNG.
## type SimConfig
SimConfig configures a simulation run.
```go
type SimConfig struct {
Seed int64
NumLeaves int
PollInterval time.Duration
TmpDir string // directory for repo files
Upstream libsync.Transport // mock Fossil master
Buggify bool // enable BUGGIFY fault injection
UV bool // sync unversioned files
Private bool // sync private artifacts
SafetyCheckInterval int // run CheckSafety() every N steps; 0 = disabled
}
```
## type SimNetwork
SimNetwork simulates the network between leaf agents and the upstream transport \(mock Fossil master\). It supports message dropping, response truncation, and network partitions, controlled by a seeded PRNG for deterministic behavior.
In the original EdgeSync DST, messages routed through a Bridge that forwarded to the upstream. Since Bridge.HandleRequest was just upstream.Exchange, SimNetwork now calls the upstream directly.
```go
type SimNetwork struct {
// contains filtered or unexported fields
}
```
### func NewSimNetwork
```go
func NewSimNetwork(rng *rand.Rand, upstream libsync.Transport) *SimNetwork
```
NewSimNetwork creates a simulated network connected to the given upstream transport.
### func \(\*SimNetwork\) Heal
```go
func (n *SimNetwork) Heal(id NodeID)
```
Heal removes a node from the partition set.
### func \(\*SimNetwork\) HealAll
```go
func (n *SimNetwork) HealAll()
```
HealAll removes all partitions.
### func \(\*SimNetwork\) Partition
```go
func (n *SimNetwork) Partition(id NodeID)
```
Partition isolates a node — all messages to/from it are dropped.
### func \(\*SimNetwork\) SetDropRate
```go
func (n *SimNetwork) SetDropRate(rate float64)
```
SetDropRate sets the probability that any message is dropped entirely.
### func \(\*SimNetwork\) SetTruncateRate
```go
func (n *SimNetwork) SetTruncateRate(rate float64)
```
SetTruncateRate sets the probability that a response is truncated \(random suffix of cards dropped\). Simulates partial delivery.
### func \(\*SimNetwork\) Transport
```go
func (n *SimNetwork) Transport(nodeID NodeID) *SimTransport
```
Transport returns a sync.Transport for the given node that routes messages through this simulated network to the upstream.
## type SimTransport
SimTransport implements sync.Transport by routing through the SimNetwork.
```go
type SimTransport struct {
// contains filtered or unexported fields
}
```
### func \(\*SimTransport\) Exchange
```go
func (t *SimTransport) Exchange(ctx context.Context, req *xfer.Message) (*xfer.Message, error)
```
Exchange sends a request through the simulated network to the upstream.
## type Simulator
Simulator drives a deterministic simulation of multiple leaf nodes syncing through a simulated network to an upstream transport \(mock Fossil master\).
```go
type Simulator struct {
Seed int64
// Stats
Steps int
TotalSyncs int
TotalErrors int
TotalUVSent int
TotalUVRecvd int
TotalUVGimmes int
// contains filtered or unexported fields
}
```
### func New
```go
func New(cfg SimConfig) (*Simulator, error)
```
New creates a Simulator with the given configuration. It creates leaf repos, nodes, and the simulated network. All I/O is local SQLite — no NATS or HTTP connections are made.
### func \(\*Simulator\) CheckAllConverged
```go
func (s *Simulator) CheckAllConverged(master *repo.Repo) error
```
CheckAllConverged checks convergence between master and all leaves. Only meaningful after a fault\-free sync period.
### func \(\*Simulator\) CheckAllTableSyncConverged
```go
func (s *Simulator) CheckAllTableSyncConverged() error
```
CheckAllTableSyncConverged checks table sync convergence across all nodes.
### func \(\*Simulator\) CheckAllUVConverged
```go
func (s *Simulator) CheckAllUVConverged(master *repo.Repo) error
```
CheckAllUVConverged checks UV convergence between master and all leaves.
### func \(\*Simulator\) CheckSafety
```go
func (s *Simulator) CheckSafety() error
```
CheckSafety runs all safety invariants on every node in the simulation.
### func \(\*Simulator\) CheckTombstoneConvergence
```go
func (s *Simulator) CheckTombstoneConvergence(t *testing.T, masterRepo *repo.Repo, tableName string, def repo.TableDef)
```
CheckTombstoneConvergence verifies that all nodes agree on which rows are tombstones for each synced table. Two nodes may have different catalog hashes during convergence, but once converged, their tombstone sets must match.
### func \(\*Simulator\) Clock
```go
func (s *Simulator) Clock() *simio.SimClock
```
Clock returns the simulator's virtual clock.
### func \(\*Simulator\) Close
```go
func (s *Simulator) Close() error
```
Close cleans up all leaf node repos and disables buggify. Iterates leafIDs \(not map\) for deterministic error reporting.
### func \(\*Simulator\) Leaf
```go
func (s *Simulator) Leaf(id NodeID) Node
```
Leaf returns the node for the given node ID.
### func \(\*Simulator\) LeafIDs
```go
func (s *Simulator) LeafIDs() []NodeID
```
LeafIDs returns the ordered list of leaf node IDs.
### func \(\*Simulator\) Network
```go
func (s *Simulator) Network() *SimNetwork
```
Network returns the simulated network for fault injection.
### func \(\*Simulator\) Run
```go
func (s *Simulator) Run(maxSteps int) error
```
Run processes up to maxSteps events. Returns nil on success or the first invariant/error encountered.
### func \(\*Simulator\) RunUntil
```go
func (s *Simulator) RunUntil(deadline time.Time) error
```
RunUntil processes events until the clock reaches the given time.
### func \(\*Simulator\) ScheduleSyncNow
```go
func (s *Simulator) ScheduleSyncNow(id NodeID)
```
ScheduleSyncNow injects a SyncNow event for the given leaf at the current time.
### func \(\*Simulator\) ScheduleUVDelete
```go
func (s *Simulator) ScheduleUVDelete(id NodeID, at time.Time, name string, mtime int64)
```
ScheduleUVDelete injects a UV delete event for the given node at the specified time.
### func \(\*Simulator\) ScheduleUVWrite
```go
func (s *Simulator) ScheduleUVWrite(id NodeID, at time.Time, name string, data []byte, mtime int64)
```
ScheduleUVWrite injects a UV write event for the given node at the specified time.
### func \(\*Simulator\) SetMasterRepo
```go
func (s *Simulator) SetMasterRepo(r *repo.Repo)
```
SetMasterRepo registers the master repo for UV events targeting "master".
### func \(\*Simulator\) Step
```go
func (s *Simulator) Step() (bool, error)
```
Step processes the next event in the queue. Returns false if the queue is empty.
Generated by [gomarkdoc]()
---
# libfossil
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/sdk/libfossil/api/
# libfossil
```go
import "github.com/danmestas/go-libfossil"
```
## Index
- [Constants](<#constants>)
- [Variables](<#variables>)
- [func Clone\(ctx context.Context, path string, t Transport, opts CloneOpts\) \(\*Repo, \*CloneResult, error\)](<#Clone>)
- [func JulianToTime\(j float64\) time.Time](<#JulianToTime>)
- [func TimeToJulian\(t time.Time\) float64](<#TimeToJulian>)
- [type AnnotateOpts](<#AnnotateOpts>)
- [type AnnotatedLine](<#AnnotatedLine>)
- [type Attachment](<#Attachment>)
- [type AttachmentOpts](<#AttachmentOpts>)
- [type BisectSession](<#BisectSession>)
- [type BuggifyChecker](<#BuggifyChecker>)
- [type Checkout](<#Checkout>)
- [func \(c \*Checkout\) Add\(patterns \[\]string\) \(int, error\)](<#Checkout.Add>)
- [func \(c \*Checkout\) Checkin\(opts CheckoutCommitOpts\) \(rid int64, uuid string, err error\)](<#Checkout.Checkin>)
- [func \(c \*Checkout\) CheckinContext\(ctx context.Context, opts CheckoutCommitOpts, required RequiredContentOpts\) \(int64, string, error\)](<#Checkout.CheckinContext>)
- [func \(c \*Checkout\) Close\(\) error](<#Checkout.Close>)
- [func \(c \*Checkout\) Cone\(\) \*Cone](<#Checkout.Cone>)
- [func \(c \*Checkout\) Conflicts\(\) \(\[\]string, error\)](<#Checkout.Conflicts>)
- [func \(c \*Checkout\) Dir\(\) string](<#Checkout.Dir>)
- [func \(c \*Checkout\) Extract\(rid int64, opts ExtractOpts\) error](<#Checkout.Extract>)
- [func \(c \*Checkout\) HasChanges\(\) \(bool, error\)](<#Checkout.HasChanges>)
- [func \(c \*Checkout\) Merge\(opts CheckoutMergeOpts\) \(CheckoutMergeResult, error\)](<#Checkout.Merge>)
- [func \(c \*Checkout\) MergeContext\(ctx context.Context, opts CheckoutMergeOpts, required RequiredContentOpts\) \(CheckoutMergeResult, error\)](<#Checkout.MergeContext>)
- [func \(c \*Checkout\) Redo\(\) \(UndoResult, error\)](<#Checkout.Redo>)
- [func \(c \*Checkout\) Remove\(patterns \[\]string\) error](<#Checkout.Remove>)
- [func \(c \*Checkout\) Rename\(oldName, newName string\) error](<#Checkout.Rename>)
- [func \(c \*Checkout\) ResolveVersion\(name string\) \(int64, error\)](<#Checkout.ResolveVersion>)
- [func \(c \*Checkout\) ResolveWorking\(name string\) \(WorkingVersion, error\)](<#Checkout.ResolveWorking>)
- [func \(c \*Checkout\) Revert\(opts RevertOpts\) error](<#Checkout.Revert>)
- [func \(c \*Checkout\) SetCone\(next \*Cone\) \(ConeChange, error\)](<#Checkout.SetCone>)
- [func \(c \*Checkout\) SetConeContext\(ctx context.Context, next \*Cone, required RequiredContentOpts\) \(ConeChange, error\)](<#Checkout.SetConeContext>)
- [func \(c \*Checkout\) Status\(\) \(\[\]CheckoutChange, error\)](<#Checkout.Status>)
- [func \(c \*Checkout\) Undo\(\) \(UndoResult, error\)](<#Checkout.Undo>)
- [func \(c \*Checkout\) Update\(opts UpdateOpts\) \(UpdateResult, error\)](<#Checkout.Update>)
- [func \(c \*Checkout\) UpdateContext\(ctx context.Context, opts UpdateOpts, required RequiredContentOpts\) \(UpdateResult, error\)](<#Checkout.UpdateContext>)
- [func \(c \*Checkout\) Version\(\) \(int64, string, error\)](<#Checkout.Version>)
- [func \(c \*Checkout\) WouldFork\(\) \(bool, error\)](<#Checkout.WouldFork>)
- [type CheckoutChange](<#CheckoutChange>)
- [type CheckoutCommitOpts](<#CheckoutCommitOpts>)
- [type CheckoutCreateOpts](<#CheckoutCreateOpts>)
- [type CheckoutMergeFile](<#CheckoutMergeFile>)
- [type CheckoutMergeOpts](<#CheckoutMergeOpts>)
- [type CheckoutMergeResult](<#CheckoutMergeResult>)
- [type CheckoutObserver](<#CheckoutObserver>)
- [func NopCheckoutObserver\(\) CheckoutObserver](<#NopCheckoutObserver>)
- [func StdoutCheckoutObserver\(\) CheckoutObserver](<#StdoutCheckoutObserver>)
- [type CheckoutOpenOpts](<#CheckoutOpenOpts>)
- [type CheckpointMode](<#CheckpointMode>)
- [type CloneOpts](<#CloneOpts>)
- [type CloneResult](<#CloneResult>)
- [type CommitEnd](<#CommitEnd>)
- [type CommitOpts](<#CommitOpts>)
- [type CommitStart](<#CommitStart>)
- [type Cone](<#Cone>)
- [func NewCone\(include, exclude \[\]string\) \(\*Cone, error\)](<#NewCone>)
- [func NewHorizonCone\(include, exclude \[\]string, h Horizon\) \(\*Cone, error\)](<#NewHorizonCone>)
- [func \(c \*Cone\) Covers\(path string\) bool](<#Cone.Covers>)
- [func \(c \*Cone\) Decide\(path string\) \(covered bool, member string\)](<#Cone.Decide>)
- [func \(c \*Cone\) Digest\(\) string](<#Cone.Digest>)
- [func \(c \*Cone\) Exclude\(\) \[\]string](<#Cone.Exclude>)
- [func \(c \*Cone\) Horizon\(\) \(Horizon, bool\)](<#Cone.Horizon>)
- [func \(c \*Cone\) Include\(\) \[\]string](<#Cone.Include>)
- [func \(c \*Cone\) String\(\) string](<#Cone.String>)
- [func \(c \*Cone\) With\(include, exclude \[\]string\) \(\*Cone, error\)](<#Cone.With>)
- [func \(c \*Cone\) WithHorizon\(h \*Horizon\) \(\*Cone, error\)](<#Cone.WithHorizon>)
- [func \(c \*Cone\) Without\(paths \[\]string\) \(\*Cone, error\)](<#Cone.Without>)
- [type ConeChange](<#ConeChange>)
- [type CreateOpts](<#CreateOpts>)
- [type Cursor](<#Cursor>)
- [type DiffEntry](<#DiffEntry>)
- [type EventKind](<#EventKind>)
- [type ExternalRef](<#ExternalRef>)
- [type ExtractEnd](<#ExtractEnd>)
- [type ExtractOpts](<#ExtractOpts>)
- [type ExtractStart](<#ExtractStart>)
- [type FileEntry](<#FileEntry>)
- [type FileToCommit](<#FileToCommit>)
- [type Fork](<#Fork>)
- [type FramedTransport](<#FramedTransport>)
- [type FslError](<#FslError>)
- [func \(e \*FslError\) Error\(\) string](<#FslError.Error>)
- [func \(e \*FslError\) Unwrap\(\) error](<#FslError.Unwrap>)
- [type FslID](<#FslID>)
- [type FslSize](<#FslSize>)
- [type HTTPOption](<#HTTPOption>)
- [func WithHTTPClient\(c \*http.Client\) HTTPOption](<#WithHTTPClient>)
- [type HandleEnd](<#HandleEnd>)
- [type HandleOpts](<#HandleOpts>)
- [type HandleStart](<#HandleStart>)
- [type Horizon](<#Horizon>)
- [type HorizonRule](<#HorizonRule>)
- [func \(r HorizonRule\) String\(\) string](<#HorizonRule.String>)
- [type HorizonRuleCost](<#HorizonRuleCost>)
- [type LogEntry](<#LogEntry>)
- [type LogOpts](<#LogOpts>)
- [type MergeConflict](<#MergeConflict>)
- [type MergeConflictError](<#MergeConflictError>)
- [func \(e \*MergeConflictError\) Error\(\) string](<#MergeConflictError.Error>)
- [func \(e \*MergeConflictError\) Is\(target error\) bool](<#MergeConflictError.Is>)
- [type MergeResult](<#MergeResult>)
- [type MockTransport](<#MockTransport>)
- [func \(t \*MockTransport\) RoundTrip\(\_ context.Context, payload \[\]byte\) \(\[\]byte, error\)](<#MockTransport.RoundTrip>)
- [type OriginNamer](<#OriginNamer>)
- [type PullOpts](<#PullOpts>)
- [type RC](<#RC>)
- [func \(rc RC\) String\(\) string](<#RC.String>)
- [type RefusedError](<#RefusedError>)
- [func \(e \*RefusedError\) Error\(\) string](<#RefusedError.Error>)
- [type Repo](<#Repo>)
- [func Create\(path string, opts CreateOpts\) \(\*Repo, error\)](<#Create>)
- [func Open\(path string\) \(\*Repo, error\)](<#Open>)
- [func \(r \*Repo\) Ancestry\(opts LogOpts\) \(\[\]LogEntry, error\)](<#Repo.Ancestry>)
- [func \(r \*Repo\) Annotate\(opts AnnotateOpts\) \(\[\]AnnotatedLine, error\)](<#Repo.Annotate>)
- [func \(r \*Repo\) AnnotateContext\(ctx context.Context, opts AnnotateOpts, required RequiredContentOpts\) \(\[\]AnnotatedLine, error\)](<#Repo.AnnotateContext>)
- [func \(r \*Repo\) AttachmentAdd\(target, name string, content \[\]byte, opts AttachmentOpts\) \(artifact string, err error\)](<#Repo.AttachmentAdd>)
- [func \(r \*Repo\) AttachmentAddExternal\(target string, ref ExternalRef, opts AttachmentOpts\) \(artifact string, err error\)](<#Repo.AttachmentAddExternal>)
- [func \(r \*Repo\) AttachmentDelete\(target, name string, opts AttachmentOpts\) \(artifact string, err error\)](<#Repo.AttachmentDelete>)
- [func \(r \*Repo\) AttachmentRead\(target, name string\) \(a Attachment, content \[\]byte, err error\)](<#Repo.AttachmentRead>)
- [func \(r \*Repo\) Attachments\(target string\) \(\[\]Attachment, error\)](<#Repo.Attachments>)
- [func \(r \*Repo\) BranchOpenLeaf\(name string\) \(rid int64, open bool, err error\)](<#Repo.BranchOpenLeaf>)
- [func \(r \*Repo\) BranchTip\(name string\) \(int64, error\)](<#Repo.BranchTip>)
- [func \(r \*Repo\) Checkpoint\(mode CheckpointMode\) error](<#Repo.Checkpoint>)
- [func \(r \*Repo\) Close\(\) error](<#Repo.Close>)
- [func \(r \*Repo\) Commit\(opts CommitOpts\) \(int64, string, error\)](<#Repo.Commit>)
- [func \(r \*Repo\) CommitContext\(ctx context.Context, opts CommitOpts, required RequiredContentOpts\) \(int64, string, error\)](<#Repo.CommitContext>)
- [func \(r \*Repo\) CompactStore\(ctx context.Context\) \(int, error\)](<#Repo.CompactStore>)
- [func \(r \*Repo\) Config\(key string\) \(string, error\)](<#Repo.Config>)
- [func \(r \*Repo\) CreateCheckout\(dir string, opts CheckoutCreateOpts\) \(\*Checkout, error\)](<#Repo.CreateCheckout>)
- [func \(r \*Repo\) CreateUser\(opts UserOpts\) error](<#Repo.CreateUser>)
- [func \(r \*Repo\) CurrentTrunk\(\) \(int64, error\)](<#Repo.CurrentTrunk>)
- [func \(r \*Repo\) DB\(\) \*db.DB](<#Repo.DB>)
- [func \(r \*Repo\) DeepenHorizon\(ctx context.Context, target string, required RequiredContentOpts\) \(\*SyncResult, error\)](<#Repo.DeepenHorizon>)
- [func \(r \*Repo\) DeleteUser\(login string\) error](<#Repo.DeleteUser>)
- [func \(r \*Repo\) DetectForks\(\) \(\[\]Fork, error\)](<#Repo.DetectForks>)
- [func \(r \*Repo\) Diff\(ridA, ridB int64, filePath string\) \(\[\]DiffEntry, error\)](<#Repo.Diff>)
- [func \(r \*Repo\) DiffContext\(ctx context.Context, ridA, ridB int64, filePath string, required RequiredContentOpts\) \(\[\]DiffEntry, error\)](<#Repo.DiffContext>)
- [func \(r \*Repo\) DisableSubmanifests\(\) error](<#Repo.DisableSubmanifests>)
- [func \(r \*Repo\) EnableSubmanifests\(k int\) \(pinned int, warning string, err error\)](<#Repo.EnableSubmanifests>)
- [func \(r \*Repo\) FindCommonAncestor\(ridA, ridB int64\) \(int64, error\)](<#Repo.FindCommonAncestor>)
- [func \(r \*Repo\) GetUser\(login string\) \(User, error\)](<#Repo.GetUser>)
- [func \(r \*Repo\) HandleSync\(ctx context.Context, payload \[\]byte\) \(\[\]byte, error\)](<#Repo.HandleSync>)
- [func \(r \*Repo\) HandleSyncWithOpts\(ctx context.Context, payload \[\]byte, opts HandleOpts\) \(\[\]byte, error\)](<#Repo.HandleSyncWithOpts>)
- [func \(r \*Repo\) HorizonCost\(checkin string\) \(\[\]HorizonRuleCost, error\)](<#Repo.HorizonCost>)
- [func \(r \*Repo\) Inner\(\) \*repo.Repo](<#Repo.Inner>)
- [func \(r \*Repo\) ListConflictForks\(\) \(\[\]string, error\)](<#Repo.ListConflictForks>)
- [func \(r \*Repo\) ListFiles\(rid int64\) \(\[\]FileEntry, error\)](<#Repo.ListFiles>)
- [func \(r \*Repo\) ListFilesIn\(rid int64, c \*Cone\) \(\[\]FileEntry, error\)](<#Repo.ListFilesIn>)
- [func \(r \*Repo\) ListUsers\(\) \(\[\]User, error\)](<#Repo.ListUsers>)
- [func \(r \*Repo\) Merge\(srcBranch, dstBranch, message, user string\) \(int64, string, error\)](<#Repo.Merge>)
- [func \(r \*Repo\) OpenCheckout\(dir string, opts CheckoutOpenOpts\) \(\*Checkout, error\)](<#Repo.OpenCheckout>)
- [func \(r \*Repo\) Path\(\) string](<#Repo.Path>)
- [func \(r \*Repo\) Pull\(ctx context.Context, url string, opts PullOpts\) \(\*SyncResult, error\)](<#Repo.Pull>)
- [func \(r \*Repo\) ReadBlob\(uuid string\) \(\[\]byte, error\)](<#Repo.ReadBlob>)
- [func \(r \*Repo\) ReadFile\(rid int64, filePath string\) \(\[\]byte, error\)](<#Repo.ReadFile>)
- [func \(r \*Repo\) ReadFileAt\(version string, filePath string\) \(\[\]byte, error\)](<#Repo.ReadFileAt>)
- [func \(r \*Repo\) ReadFileAtContext\(ctx context.Context, version string, filePath string, required RequiredContentOpts\) \(\[\]byte, error\)](<#Repo.ReadFileAtContext>)
- [func \(r \*Repo\) ReadFileContext\(ctx context.Context, rid int64, filePath string, required RequiredContentOpts\) \(\[\]byte, error\)](<#Repo.ReadFileContext>)
- [func \(r \*Repo\) Redo\(dir string\) error](<#Repo.Redo>)
- [func \(r \*Repo\) ResolveBranchStart\(name string\) \(int64, error\)](<#Repo.ResolveBranchStart>)
- [func \(r \*Repo\) ResolveConflictFork\(filename string\) error](<#Repo.ResolveConflictFork>)
- [func \(r \*Repo\) ResolveVersion\(name string\) \(int64, error\)](<#Repo.ResolveVersion>)
- [func \(r \*Repo\) RetuneSubmanifests\(k int\) error](<#Repo.RetuneSubmanifests>)
- [func \(r \*Repo\) ServeHTTP\(ctx context.Context, addr string\) error](<#Repo.ServeHTTP>)
- [func \(r \*Repo\) SetCaps\(login, caps string\) error](<#Repo.SetCaps>)
- [func \(r \*Repo\) SetConfig\(key, value string\) error](<#Repo.SetConfig>)
- [func \(r \*Repo\) SetContentCache\(bytes int64\)](<#Repo.SetContentCache>)
- [func \(r \*Repo\) SetHashPolicy\(name string\) error](<#Repo.SetHashPolicy>)
- [func \(r \*Repo\) SetPassword\(login, password string\) error](<#Repo.SetPassword>)
- [func \(r \*Repo\) SetStoreCone\(c \*Cone\) error](<#Repo.SetStoreCone>)
- [func \(r \*Repo\) SetStoreConeContext\(ctx context.Context, next \*Cone, required RequiredContentOpts\) \(\*SyncResult, error\)](<#Repo.SetStoreConeContext>)
- [func \(r \*Repo\) StashApply\(dir string, id int64\) error](<#Repo.StashApply>)
- [func \(r \*Repo\) StashClear\(\) error](<#Repo.StashClear>)
- [func \(r \*Repo\) StashDrop\(id int64\) error](<#Repo.StashDrop>)
- [func \(r \*Repo\) StashList\(\) \(\[\]StashEntry, error\)](<#Repo.StashList>)
- [func \(r \*Repo\) StashPop\(dir string\) error](<#Repo.StashPop>)
- [func \(r \*Repo\) StashSave\(dir, comment string\) error](<#Repo.StashSave>)
- [func \(r \*Repo\) StoreCone\(\) \(\*Cone, error\)](<#Repo.StoreCone>)
- [func \(r \*Repo\) Sync\(ctx context.Context, t Transport, opts SyncOpts\) \(\*SyncResult, error\)](<#Repo.Sync>)
- [func \(r \*Repo\) Tag\(opts TagOpts\) \(int64, error\)](<#Repo.Tag>)
- [func \(r \*Repo\) TicketAdd\(opts TicketOpts\) \(ticketUUID, artifact string, err error\)](<#Repo.TicketAdd>)
- [func \(r \*Repo\) TicketChanges\(ticket string\) \(\[\]TicketChange, error\)](<#Repo.TicketChanges>)
- [func \(r \*Repo\) TicketList\(\) \(\[\]Ticket, error\)](<#Repo.TicketList>)
- [func \(r \*Repo\) TicketSet\(ticket string, opts TicketOpts\) \(artifact string, err error\)](<#Repo.TicketSet>)
- [func \(r \*Repo\) Timeline\(opts TimelineOpts\) \(\[\]TimelineEntry, error\)](<#Repo.Timeline>)
- [func \(r \*Repo\) TimelineSince\(hash string, kinds ...EventKind\) \(\[\]TimelineEntry, error\)](<#Repo.TimelineSince>)
- [func \(r \*Repo\) UUIDFromRID\(rid int64\) \(string, error\)](<#Repo.UUIDFromRID>)
- [func \(r \*Repo\) UVDelete\(name string, mtime time.Time\) error](<#Repo.UVDelete>)
- [func \(r \*Repo\) UVList\(\) \(\[\]UVEntry, error\)](<#Repo.UVList>)
- [func \(r \*Repo\) UVRead\(name string\) \(\[\]byte, int64, string, error\)](<#Repo.UVRead>)
- [func \(r \*Repo\) UVWrite\(name string, content \[\]byte, mtime time.Time\) error](<#Repo.UVWrite>)
- [func \(r \*Repo\) Undo\(dir string\) error](<#Repo.Undo>)
- [func \(r \*Repo\) Verify\(\) error](<#Repo.Verify>)
- [func \(r \*Repo\) VerifySeed\(ctx context.Context, required RequiredContentOpts\) \(\*SeedVerification, error\)](<#Repo.VerifySeed>)
- [func \(r \*Repo\) WithTx\(fn func\(tx \*db.Tx\) error\) error](<#Repo.WithTx>)
- [func \(r \*Repo\) XferHandler\(\) http.HandlerFunc](<#Repo.XferHandler>)
- [type RequiredContentOpts](<#RequiredContentOpts>)
- [type RevertOpts](<#RevertOpts>)
- [type RoundStats](<#RoundStats>)
- [type ScanEnd](<#ScanEnd>)
- [type SeedMismatch](<#SeedMismatch>)
- [type SeedVerification](<#SeedVerification>)
- [type SessionEnd](<#SessionEnd>)
- [type SessionStart](<#SessionStart>)
- [type StashEntry](<#StashEntry>)
- [type StatusEntry](<#StatusEntry>)
- [type SyncObserver](<#SyncObserver>)
- [func NopSyncObserver\(\) SyncObserver](<#NopSyncObserver>)
- [func StdoutSyncObserver\(\) SyncObserver](<#StdoutSyncObserver>)
- [type SyncOpts](<#SyncOpts>)
- [type SyncResult](<#SyncResult>)
- [type TableSyncEnd](<#TableSyncEnd>)
- [type TableSyncStart](<#TableSyncStart>)
- [type TagOpts](<#TagOpts>)
- [type TagSpec](<#TagSpec>)
- [type Ticket](<#Ticket>)
- [type TicketChange](<#TicketChange>)
- [type TicketOpts](<#TicketOpts>)
- [type TimelineEntry](<#TimelineEntry>)
- [type TimelineOpts](<#TimelineOpts>)
- [type Transport](<#Transport>)
- [func NewHTTPTransport\(url string, opts ...HTTPOption\) Transport](<#NewHTTPTransport>)
- [type TransportFunc](<#TransportFunc>)
- [func \(f TransportFunc\) RoundTrip\(ctx context.Context, payload \[\]byte\) \(\[\]byte, error\)](<#TransportFunc.RoundTrip>)
- [type UVEntry](<#UVEntry>)
- [type UndoResult](<#UndoResult>)
- [type UpdateChange](<#UpdateChange>)
- [type UpdateOpts](<#UpdateOpts>)
- [type UpdateResult](<#UpdateResult>)
- [type User](<#User>)
- [type UserOpts](<#UserOpts>)
- [type WorkingVersion](<#WorkingVersion>)
## Constants
```go
const (
CheckpointPassive = db.CheckpointPassive
CheckpointFull = db.CheckpointFull
CheckpointRestart = db.CheckpointRestart
CheckpointTruncate = db.CheckpointTruncate
)
```
```go
const (
PhantomSize FslSize = fsltype.PhantomSize
FossilApplicationID int32 = fsltype.FossilApplicationID
)
```
SettingClearsign names the boolean repository setting, read as fossil reads one, that signs every check\-in a commit or a branch creation stores, as fossil and zit sign them. Off by default. It signs no horizon seed: see SettingHorizonSeedCommand. The signed text is the artifact, so a signed check\-in's name is the hash of the signed bytes; a signer that fails abandons the commit. CommitOpts.NoSign and CheckoutCommitOpts.NoSign store one check\-in unsigned, as fossil's \-\-nosign does, and fossil's legacy omitsign setting turns signing off.
```go
const SettingClearsign = clearsign.SettingClearsign
```
SettingHorizonHistoryURL names the repository setting an origin appends to pragma horizon\-served as the interface, outside the sync protocol, at which history below a horizon can be queried \(draft\-fossil\-horizon\-00 §6.2.5\). It must be one http or https URL with a host, no white space and no credentials; SetConfig refuses any other value, and an empty one names none. Horizon clients report it as CloneResult.HorizonHistoryURL and SyncResult.HorizonHistoryURL.
```go
const SettingHorizonHistoryURL = repo.SettingHorizonHistoryURL
```
SettingHorizonSeedCommand names the command an origin signs the structural horizon seeds it mints with \(draft\-fossil\-horizon\-00 §5.2.4\), configured apart from SettingClearsign and SettingPGPCommand: unset or off, seeds go out unsigned even when check\-ins are signed. It follows pgp\-command's contract, and an SSH signer must sign in the namespace fossil\-horizon\-seed, never fossilscm, so that a check\-in's signature cannot stand in for a seed's:
```
ssh-keygen -q -Y sign -n fossil-horizon-seed -f ~/.ssh/id_ed25519
```
An origin whose command signs in another namespace serves no seed: the request fails.
```go
const SettingHorizonSeedCommand = clearsign.SettingSeedCommand
```
SettingHorizonSeedKey names the setting holding the public key a clone verifies its origin's structural horizon seeds with \(draft\-fossil\-horizon\-00 §5.2.4\): an SSH public key line, verified in process, or the fingerprint of an OpenPGP key in the user's gpg keyring, verified by gpg. With a key set, a seed that is unsigned or not signed by that key is discarded and the clone falls back to the content horizon; unset, an unsigned seed is accepted. An SSH signature must carry the namespace fossil\-horizon\-seed. CloneOpts.SeedKey sets it when the clone is made. It is the clone's own: an origin never sends it.
SSH signatures are verified only in a program that imports package github.com/danmestas/go\-libfossil/sshverify; without it, a seed under an SSH key is refused with ErrSSHVerifierUnavailable.
```go
const SettingHorizonSeedKey = clearsign.SettingSeedKey
```
SettingPGPCommand names the signing command, as fossil names it. Unset, it is fossil's default, "gpg \-\-clearsign \-o ", run as COMMAND OUTFILE INFILE. A command whose name starts with "ssh" is run as COMMAND INFILE and writes a detached INFILE.sig, which is wrapped in fossil's SSH envelope; to sign with an SSH key, set it to
```
ssh-keygen -q -Y sign -n fossilscm -f ~/.ssh/id_ed25519
```
The namespace must be fossilscm for a clone to verify the signature. The command runs through the shell, so \~ and environment variables expand.
```go
const SettingPGPCommand = clearsign.SettingPGPCommand
```
SettingPartialMirror names the boolean repository setting, read as fossil reads one \("on", "yes", "true" or a nonzero number\), that lets a sparse store serve a clone its cone does not cover \(draft\-fossil\-sparse\-cone\-00 §5.3\). Unset, such a clone is refused with one error card and no content, since the clone would receive elided phantoms it must resolve elsewhere. It governs every serving surface: HandleSync, XferHandler and ServeHTTP.
```go
const SettingPartialMirror = internalsync.SettingPartialMirror
```
## Variables
ErrAmbiguousVersion is returned by ResolveVersion when a UUID prefix matches more than one artifact \(collision\). Callers can match with errors.Is.
```go
var ErrAmbiguousVersion = errors.New("libfossil: ambiguous version prefix")
```
ErrArtifactNotFound is returned by UUIDFromRID when the given RID does not correspond to any artifact in the repository's blob table, and by ReadBlob for a hash the blob table does not name. Callers can match with errors.Is.
```go
var ErrArtifactNotFound = errors.New("libfossil: artifact not found")
```
ErrAttachmentNotFound is returned when a target has no current attachment by the name asked for: none was ever added, or the latest was deleted. Callers can match with errors.Is.
```go
var ErrAttachmentNotFound = errors.New("libfossil: attachment not found")
```
ErrBelowHorizon is matched by the error of an operation that needs history below the store's structural horizon \(draft\-fossil\-horizon\-00 §5.5\), such as a merge whose base lies there. The message names the horizon and the remedy, a deepen \(Repo.DeepenHorizon\).
```go
var ErrBelowHorizon = content.ErrBelowHorizon
```
ErrFileNotFound is returned by ReadFile when the requested filePath is not tracked in the given checkin. Callers can match with errors.Is.
```go
var ErrFileNotFound = errors.New("libfossil: file not found in checkin")
```
ErrHorizonCheckout is returned when a checkout is given a cone that carries a horizon. A content horizon \(draft\-fossil\-horizon\-00 §3.2\) is a property of the repository's store, not of a checkout: set it on the store cone with Repo.SetStoreCone.
```go
var ErrHorizonCheckout = errors.New("libfossil: a horizon is a property of the repository's store, not of a checkout")
```
ErrMergeConflict is returned \(wrapped in a MergeConflictError\) when Merge detects any unresolved three\-way conflicts. Callers can check with errors.Is\(err, ErrMergeConflict\) without caring about the file list.
```go
var ErrMergeConflict = errors.New("libfossil: merge has conflicts")
```
ErrNoStoreCone is returned by CompactStore on a dense store: with no cone on record nothing is elided, and compaction never guesses a policy.
```go
var ErrNoStoreCone = errors.New("libfossil: the store has no cone")
```
ErrOutsideCone is wrapped by the refusal of a checkout operation that would act on a path its sparse cone does not cover: adding, removing, renaming, reverting or committing such a path, or a merge that would change one. The message names the path, the cone and the member that excluded it.
```go
var ErrOutsideCone = checkout.ErrOutsideCone
```
ErrSSHVerifierUnavailable is the error, wrapped, of an operation that had to verify an SSH signature in a program that does not link the verifier: a sync or rebuild of a store whose SettingHorizonSeedKey is an SSH key fails with it, and the seed is neither accepted nor discarded. Import package github.com/danmestas/go\-libfossil/sshverify for its side effect to verify SSH signatures; the library leaves that cryptography out of programs that never verify one.
```go
var ErrSSHVerifierUnavailable = clearsign.ErrSSHVerifierUnavailable
```
ErrVersionNotFound is returned by ResolveVersion when the requested version string does not match any artifact in the repository. Callers can match with errors.Is.
```go
var ErrVersionNotFound = errors.New("libfossil: version not found")
```
## func Clone
```go
func Clone(ctx context.Context, path string, t Transport, opts CloneOpts) (*Repo, *CloneResult, error)
```
Clone performs a full repository clone from a remote Fossil server. It creates a new repository at the given path, runs the clone protocol until convergence, and returns the opened Repo handle and a result summary. On error, the partially\-created repo file is removed. Artifacts the clone rounds leave missing stay phantoms unless opts.PullPhantoms is set.
## func JulianToTime
```go
func JulianToTime(j float64) time.Time
```
JulianToTime converts a Fossil Julian day number to time.Time.
## func TimeToJulian
```go
func TimeToJulian(t time.Time) float64
```
TimeToJulian converts a time.Time to a Fossil Julian day number.
## type AnnotateOpts
AnnotateOpts configures an annotate operation.
```go
type AnnotateOpts struct {
FilePath string
StartRID int64
}
```
## type AnnotatedLine
AnnotatedLine is a single line of blame/annotate output.
BelowHorizon marks a line whose origin lies at or below the store's horizon C \(draft\-fossil\-horizon\-00 §5.5\), which the store does not hold the history to attribute: UUID, User and Date then describe C itself, at which the line is reported as originating, not the check\-in that added it. A deepen \(repo cone deepen\) brings the history that attributes it; on a content\-horizon store AnnotateContext fetches it.
Such a result is partial, and the marks are its only signal: Annotate returns it with no error, as does an AnnotateContext that reached its demand\-fetch limit or whose peer could not supply a version.
```go
type AnnotatedLine struct {
Text string
UUID string
User string
Date time.Time
BelowHorizon bool
}
```
## type Attachment
Attachment is one current attachment: the latest artifact for its name on its target, when that artifact adds a file rather than deleting it.
```go
type Attachment struct {
// Artifact is the hash of the control artifact that attached the file.
Artifact string `json:"artifact"`
// Target is the full ticket id, wiki page name or tech note id.
Target string `json:"target"`
Name string `json:"name"`
// Source is the hash of the blob the A card names: the file itself, or
// the stub when External is set.
Source string `json:"source"`
Comment string `json:"comment"`
User string `json:"user"`
Time time.Time `json:"time"`
// Size is the file's length in bytes: the blob's, or External.Size. It
// is -1 when the blob has not arrived in this repository.
Size int64 `json:"size"`
// External, when not nil, says the bytes live outside the repository:
// Source is a stub, and AttachmentRead returns no content.
External *ExternalRef `json:"external,omitempty"`
}
```
## type AttachmentOpts
AttachmentOpts is who makes an attachment change, when, and why.
```go
type AttachmentOpts struct {
// User is the change's author, its U card.
User string
// Comment is the attachment's description, its C card. Fossil writes
// none on a deletion, so AttachmentDelete refuses one.
Comment string
// Time is the change's D card; zero means now.
Time time.Time
}
```
## type BisectSession
BisectSession holds state for a binary\-search bisect operation.
```go
type BisectSession struct {
// contains filtered or unexported fields
}
```
## type BuggifyChecker
BuggifyChecker controls fault injection for deterministic simulation testing.
```go
type BuggifyChecker interface {
Check(site string, probability float64) bool
}
```
## type Checkout
Checkout represents a working directory linked to a Fossil repository. A Checkout is not safe for concurrent use. Callers must serialize access to a single Checkout instance.
```go
type Checkout struct {
// contains filtered or unexported fields
}
```
### func \(\*Checkout\) Add
```go
func (c *Checkout) Add(patterns []string) (int, error)
```
Add adds files to version tracking. Returns the number of files added. Files already tracked are silently skipped.
### func \(\*Checkout\) Checkin
```go
func (c *Checkout) Checkin(opts CheckoutCommitOpts) (rid int64, uuid string, err error)
```
Checkin creates a new checkin from the checkout working directory: every change, or only opts.Files \(a directory names every tracked file under it\). Pending merges become merge parents. Like fossil's commit, it refuses a name it does not track, a commit with nothing changed and no merge pending \(unless opts.AllowEmpty\), files that still hold merge conflict markers \(unless opts.AllowConflict\), and committing only some files of a pending merge. Returns the RID and UUID of the new checkin manifest.
### func \(\*Checkout\) CheckinContext
```go
func (c *Checkout) CheckinContext(ctx context.Context, opts CheckoutCommitOpts, required RequiredContentOpts) (int64, string, error)
```
CheckinContext recovers necessary parent structure from an explicit peer, outside checkout locks and repository transactions. It then gathers checkout changes afresh; no stale file list is replayed after network I/O.
### func \(\*Checkout\) Close
```go
func (c *Checkout) Close() error
```
Close closes the checkout database. Does NOT close the repo.
### func \(\*Checkout\) Cone
```go
func (c *Checkout) Cone() *Cone
```
Cone returns the checkout's cone, or nil when the checkout is dense.
### func \(\*Checkout\) Conflicts
```go
func (c *Checkout) Conflicts() ([]string, error)
```
Conflicts lists the files, sorted, that hold merge conflict markers: the files a merge or update left in conflict and nobody has resolved yet.
### func \(\*Checkout\) Dir
```go
func (c *Checkout) Dir() string
```
Dir returns the checkout directory path.
### func \(\*Checkout\) Extract
```go
func (c *Checkout) Extract(rid int64, opts ExtractOpts) error
```
Extract writes files from the specified checkin to the working directory and makes it the checkout's current version. Unless opts.Force is set it refuses, changing nothing, when the checkout has unsaved changes or when an untracked file sits where the checkin would write different content.
### func \(\*Checkout\) HasChanges
```go
func (c *Checkout) HasChanges() (bool, error)
```
HasChanges reports whether the checkout has any added, removed, renamed or modified files. It reads only the checkout database, so an edit on disk counts once Status \(or an Extract or Commit\) has scanned it; adds, removals and renames always count. Missing files need the disk; Status reports them.
### func \(\*Checkout\) Merge
```go
func (c *Checkout) Merge(opts CheckoutMergeOpts) (CheckoutMergeResult, error)
```
Merge merges another checkin into the checkout, as fossil's merge command does, leaving the result for the next Checkin to commit with the merged\-in checkin as a merge parent. A file both sides changed is merged by its strategy; when the strategy cannot reconcile the changes, fossil's conflict markers are written into the file, and the file is in conflict until they are edited out \(see Conflicts\). A conflicted merge is not an error.
Merge refuses, changing nothing, when the repository lacks content it needs or an added file would overwrite an untracked one. A file edited here but deleted in the merged\-in version is kept and reported. Renames are followed as fossil's merge follows them: a change made under one name is merged into the file under another, and a file the merged\-in checkin renamed is renamed here too \(CheckoutMergeFile.RenamedTo\); Merge refuses to rename one onto a file the checkout keeps or an untracked file. Merging the checkout's own version or one of its ancestors does nothing. The common ancestor is the one fossil's merge diffs against: the most recent common ancestor of the merged\-in checkin and the checkout's version with every checkin already merged into it.
Under a structural horizon Merge refuses, with an error matching ErrBelowHorizon, a merge whose base it does not hold or whose sides' ancestry crosses the horizon \(draft\-fossil\-horizon\-00 §5.5\): only the origin can name that base, and MergeContext with a Peer asks it.
### func \(\*Checkout\) MergeContext
```go
func (c *Checkout) MergeContext(ctx context.Context, opts CheckoutMergeOpts, required RequiredContentOpts) (CheckoutMergeResult, error)
```
MergeContext is Merge with the remedy of draft\-fossil\-horizon\-00 §5.5. Under a structural horizon, when Peer is given and the ancestry of either side crosses the horizon, it asks the origin for the base \(§6.2.4\) even when the store's own walk finds one, since over part of the graph the common ancestor found need not be the nearest. It deepens the horizon to the base the origin names when the store does not hold it \(Repo.DeepenHorizon\) and merges over that base. When the origin does not answer, or without a Peer, it is Merge, which refuses a merge whose ancestry crosses the horizon; when nothing crosses it is Merge. A merge never runs against a base the store does not hold.
The query names the merged\-in version over the checkout's version and each plain merge pending in it. It carries at most two such secondaries, so a merge that must ask with two merges already pending is refused: asking without them would name a base Fossil's merge would not choose.
### func \(\*Checkout\) Redo
```go
func (c *Checkout) Redo() (UndoResult, error)
```
Redo reverses the last Undo, reporting the files it changed as Undo does.
### func \(\*Checkout\) Remove
```go
func (c *Checkout) Remove(patterns []string) error
```
Remove removes files from version tracking. Newly added files are deleted from vfile; committed files are marked as deleted.
### func \(\*Checkout\) Rename
```go
func (c *Checkout) Rename(oldName, newName string) error
```
Rename marks a tracked file as renamed and moves it on disk.
### func \(\*Checkout\) ResolveVersion
```go
func (c *Checkout) ResolveVersion(name string) (int64, error)
```
ResolveVersion resolves name as Repo.ResolveVersion does and also resolves the names relative to this checkout \(repo\-state\-00 Section 8.3\), tried after "tip" and before every other form, as fossil's symbolic\_name\_to\_rid does \(src/name.c\):
```
"current" the checkout's version
"prev", "previous" its primary parent
"next" a child: a primary child first, then the newest link
```
A keyword that names nothing, such as "prev" at a root, falls through to the other forms, as in fossil. The forms that take a name, such as "root:current", resolve it the same way. "ckout" is an ordinary name here: it is a keyword only in the checkout\-inclusive request, ResolveWorking.
### func \(\*Checkout\) ResolveWorking
```go
func (c *Checkout) ResolveWorking(name string) (WorkingVersion, error)
```
ResolveWorking is fossil's checkout\-inclusive request \("ci\+", symbolic\_name\_to\_rid in src/name.c\): while the checkout has a version, "ckout" names its working files, reported as Working. Every other name, and "ckout" in a checkout with no version, resolves as ResolveVersion does, except that only check\-ins can answer: the request is a check\-in request, so a technote or wiki page carrying the tag does not.
As repo\-state\-00 Section 8.3 says, only "ci\+" has the keyword, so ResolveVersion and Repo.ResolveBranchStart read "ckout" as an ordinary name. It is a keyword only as the whole name: in "root:ckout" or "ckout." the inner name is resolved as a plain check\-in request, as in fossil.
### func \(\*Checkout\) Revert
```go
func (c *Checkout) Revert(opts RevertOpts) error
```
Revert restores files to their checkout version state. If opts.Files is empty, reverts all changed files. A renamed file goes back to its committed name, whatever holds that name now. Each file it writes or deletes is saved for undo first, so Undo brings it back.
### func \(\*Checkout\) SetCone
```go
func (c *Checkout) SetCone(next *Cone) (ConeChange, error)
```
SetCone replaces the checkout's cone; nil makes it dense. Files of the checked\-out version that the new cone covers and the old one did not are written; files it no longer covers are removed from the working directory \(the repository keeps them\). It refuses, changing nothing, when a file leaving the cone has local changes or a file entering it would overwrite an untracked file. A cone with a horizon is refused with ErrHorizonCheckout.
### func \(\*Checkout\) SetConeContext
```go
func (c *Checkout) SetConeContext(ctx context.Context, next *Cone, required RequiredContentOpts) (ConeChange, error)
```
SetConeContext recovers only structure needed to enumerate next and payloads newly entering the checkout. Explicit widening durably extends store coverage; an attempted fetch retains that intent even on failure. No\-peer refusal does not change policy. Planning and local checks repeat before files move. A cone with a horizon is refused with ErrHorizonCheckout before anything moves.
### func \(\*Checkout\) Status
```go
func (c *Checkout) Status() ([]CheckoutChange, error)
```
Status scans the working directory and returns the changed files, each classified as added, deleted, missing, renamed or modified, the way fossil's changes command classifies them. Wraps ScanChanges \+ VisitChanges.
### func \(\*Checkout\) Undo
```go
func (c *Checkout) Undo() (UndoResult, error)
```
Undo reverses the last operation that saved undo state, such as Update, as fossil's undo does: the files it changed come back, and the checkout's file list and version are as they were before it. Fossil's undo reads the same state, so either tool can undo the other's update.
### func \(\*Checkout\) Update
```go
func (c *Checkout) Update(opts UpdateOpts) (UpdateResult, error)
```
Update moves the checkout to a new version as fossil's update does, carrying uncommitted changes into it: a locally edited file is merged three ways \(the checkout's version as base, the working file as local, the target as other\), one the target deletes is kept untracked, and pending adds and removals stay pending. See UpdateResult for how to distinguish a clean update from one that wrote conflict markers into working\-tree files.
### func \(\*Checkout\) UpdateContext
```go
func (c *Checkout) UpdateContext(ctx context.Context, opts UpdateOpts, required RequiredContentOpts) (UpdateResult, error)
```
UpdateContext recovers necessary structure and selected update payloads from an explicit peer before checkout mutation. An implicit target is resolved once; after recovery the local update replans against fresh working files.
### func \(\*Checkout\) Version
```go
func (c *Checkout) Version() (int64, string, error)
```
Version returns the current checkout version \(RID and UUID\).
### func \(\*Checkout\) WouldFork
```go
func (c *Checkout) WouldFork() (bool, error)
```
WouldFork reports whether committing on the checkout's version would create a fork: the version already has a child on its own branch \(for example after a pull\), so the commit would become a second leaf. This is fossil's "Would fork" commit refusal; run Update first, or commit to a new branch. Another open leaf elsewhere on the branch does not count.
## type CheckoutChange
CheckoutChange describes a single file change in the checkout.
Change is "added", "modified", "deleted", "renamed" or "missing", or one of the mode changes fossil's changes command reports: "executable" \(became executable\), "symlink" \(became a symlink\), "unexec" \(lost the executable bit\) or "unlink" \(was a symlink, now is not\).
```go
type CheckoutChange struct {
Name string
Change string
}
```
## type CheckoutCommitOpts
CommitOpts configures creating a checkin from the checkout. \(This type is distinct from the existing libfossil.CommitOpts which takes explicit file content for direct repo commits without a checkout.\)
```go
type CheckoutCommitOpts struct {
Message string
User string
Branch string // empty = current branch
Tags []string // additional tag names
Delta bool
// Files limits the commit to these checkout-relative paths; empty
// commits every change.
Files []string
// AllowConflict commits files that still hold merge conflict markers,
// which Checkin otherwise refuses, as fossil's commit does.
AllowConflict bool
// AllowEmpty commits even when nothing changed, which Checkin otherwise
// refuses, as fossil's commit does.
AllowEmpty bool
// NoSign stores the check-in unsigned even when the repository's
// clearsign setting is on (SettingClearsign): fossil's commit --nosign.
NoSign bool
}
```
## type CheckoutCreateOpts
CheckoutCreateOpts configures creating a new checkout.
```go
type CheckoutCreateOpts struct {
Env *simio.Env // nil = real env
Observer CheckoutObserver // nil = nop
// Cone makes the checkout sparse: Extract, Update and Status work only on
// the files it covers, and Checkin carries the others from the parent
// unchanged. nil is dense. A cone with a horizon is refused with
// ErrHorizonCheckout. See Cone and Checkout.SetCone.
Cone *Cone
}
```
## type CheckoutMergeFile
CheckoutMergeFile reports what a merge did to one file. Action is one of:
```
"updated" - only the merged-in version changed it; its content was taken
"merged" - both sides changed it and the strategy combined them cleanly
"conflict" - merged with conflict markers written into the file
"kept" - could not be merged; the checkout's copy is unchanged (see Reason)
"forked" - the conflict-fork strategy recorded every version for later
"added" - new in the merged-in version
"deleted" - deleted in the merged-in version, unchanged here
"mode" - only the merged-in version changed its executable bit; taken
"renamed" - only renamed: the merged-in version renamed it, this checkout did not
```
Name is the file's name in the checkout before the merge. RenamedTo is set when the merge also renamed it, as fossil's merge does a file the merged\-in version renamed and the checkout did not.
```go
type CheckoutMergeFile struct {
Name string
Action string
Strategy string // the strategy that merged it, when one ran
Reason string // why the file was kept, for "kept"
RenamedTo string // its new name, when the merge renamed it
}
```
## type CheckoutMergeOpts
CheckoutMergeOpts configures Checkout.Merge.
```go
type CheckoutMergeOpts struct {
Version int64 // rid of the checkin to merge in; required
Strategy string // strategy for every file; "" picks one per file
DryRun bool // report what the merge would do, changing nothing
}
```
## type CheckoutMergeResult
CheckoutMergeResult reports a merge.
```go
type CheckoutMergeResult struct {
Ancestor int64 // the common ancestor the merge diffed against
Files []CheckoutMergeFile // sorted by name; empty when there was nothing to merge
}
```
## type CheckoutObserver
CheckoutObserver receives lifecycle callbacks during checkout/commit operations. Use NopCheckoutObserver\(\) for a silent no\-op, or StdoutCheckoutObserver\(\) for stderr logging.
```go
type CheckoutObserver interface {
ExtractStarted(info ExtractStart)
ExtractFileCompleted(name string, change UpdateChange)
ExtractCompleted(info ExtractEnd)
ScanStarted(dir string)
ScanCompleted(info ScanEnd)
CommitStarted(info CommitStart)
CommitCompleted(info CommitEnd)
Error(err error)
}
```
### func NopCheckoutObserver
```go
func NopCheckoutObserver() CheckoutObserver
```
NopCheckoutObserver returns a CheckoutObserver that silently discards all events.
### func StdoutCheckoutObserver
```go
func StdoutCheckoutObserver() CheckoutObserver
```
StdoutCheckoutObserver returns a CheckoutObserver that logs events to stderr.
## type CheckoutOpenOpts
CheckoutOpenOpts configures opening an existing checkout.
```go
type CheckoutOpenOpts struct {
SearchParents bool // search parent dirs for .fslckout
Env *simio.Env // nil = real env
Observer CheckoutObserver
}
```
## type CheckpointMode
CheckpointMode mirrors SQLite's PRAGMA wal\_checkpoint\(\\) argument.
```go
type CheckpointMode = db.CheckpointMode
```
## type CloneOpts
CloneOpts configures a clone operation.
```go
type CloneOpts struct {
User string
Password string
Observer SyncObserver
Buggify BuggifyChecker // fault injection for DST (nil = no faults)
// PullPhantoms, when set, makes Clone fetch the artifacts the clone
// rounds left missing with a follow-up pull session, and fail with an
// error if any are still missing after it. That pull departs from
// Section 8.2 of the sync draft, which sends no clone, pull or push card
// after clone_seqno 0, so it is off by default: like fossil(1)'s clone,
// Clone then succeeds with those artifacts left as phantoms.
PullPhantoms bool
// StoreCone, when non-nil, clones straight into a sparse store: only the
// artifacts the cone covers are fetched, and the cone becomes the store's
// policy. With a horizon (NewHorizonCone) it clones a content-horizon
// store, eliding the blobs below the horizon; CloneResult.HorizonServed
// counts the replies whose server cut at the horizon, the others being
// classified locally. A server that will not serve a sparse clone
// refuses it (SettingPartialMirror).
StoreCone *Cone
// SeedKey, when set, is the origin's public key, kept as the clone's
// SettingHorizonSeedKey: every structural horizon seed is verified against
// it, in this clone and every later pull and rebuild.
SeedKey string
// Horizon, when set, is the horizon to clone at, its Checkin a full hash
// or a name: "tip", a branch or tag. HorizonDepth steps it back that
// many check-ins along primary parents. A name, or any depth, is
// resolved by the origin (draft-fossil-horizon-00 §6.2.7); against one
// that does not, the clone resolves it over what it received at the
// content horizon and says so in CloneResult. Only the resolved check-in
// is stored. StoreCone then gives the path members alone: a horizon on
// both is an error, as is a depth without a Horizon.
Horizon *Horizon
HorizonDepth int
}
```
## type CloneResult
CloneResult reports what happened during a clone.
```go
type CloneResult struct {
Rounds int
BlobsRecvd int
ArtifactsLinked int
ProjectCode string
ServerCode string
Messages []string
// Elided counts the artifacts left absent under the store cone, and
// ConeServed the replies whose server acknowledged applying it (pragma
// cone-served, draft-fossil-sparse-cone-00 §6.3). Both are zero for a
// dense clone.
Elided, ConeServed int
// HorizonServed counts the replies whose server served the store's
// horizon (pragma horizon-served, draft-fossil-horizon-00 §6.2.5); the
// other rounds were classified locally. StructuralServed counts those
// served at the structural level, and HorizonFallback reports a
// structural clone that kept the content horizon instead (§6.3).
HorizonServed int
StructuralServed int
HorizonFallback bool
// HorizonHistoryURL is the interface outside the sync protocol at which
// the origin says history below a horizon can be queried (pragma
// horizon-served URL, draft-fossil-horizon-00 §6.2.5), the last a reply
// named; "" when none did. An origin names it with
// SettingHorizonHistoryURL.
HorizonHistoryURL string
// HorizonResolved is the check-in CloneOpts.Horizon named resolved to and
// HorizonSteps the steps back it took, fewer than HorizonDepth when
// history ran out; HorizonResolvedLocally reports that the origin did
// not resolve the name and the clone did, at the content horizon.
HorizonResolved string
HorizonSteps int
HorizonResolvedLocally bool
}
```
## type CommitEnd
CommitEnd describes the completion of a commit operation.
```go
type CommitEnd struct {
UUID string
RID int64
}
```
## type CommitOpts
CommitOpts configures a commit operation.
```go
type CommitOpts struct {
// Files is the caller-supplied subset of the new commit's tree. When
// ParentID is non-zero, Files is MERGED with the parent's tracked files:
// any name in Files overrides the parent's entry, and any file tracked
// at the parent but not in Files is carried forward into the new manifest.
// This matches `fossil ci`'s full-tree semantics — supplying just the
// changed files does not erase the rest of the tree.
Files []FileToCommit
Comment string
User string
Tags []TagSpec
Time time.Time
ParentID int64
// MergeParents lists additional parents for a merge commit. The resulting
// manifest's P-card is [ParentID, MergeParents...]; ParentID is the primary
// parent (branch tip being committed onto), and each MergeParent contributes
// a secondary plink row (isprim=0).
MergeParents []int64
Delta bool
// PartialManifest, when true, skips the parent-file merge: the resulting
// manifest contains only Files. This is the legacy pre-fix behavior and
// effectively erases every file tracked at the parent that is not in
// Files. Default false matches `fossil ci`. Set true only when the
// caller intentionally wants omission-to-mean-deletion (e.g., tests for
// the diff/deletion path); a dedicated deletion API may replace this
// escape hatch later.
PartialManifest bool
// NoSign stores the check-in unsigned even when the repository's
// clearsign setting is on (SettingClearsign): fossil's commit --nosign.
NoSign bool
}
```
## type CommitStart
CommitStart describes the beginning of a commit operation.
```go
type CommitStart struct {
Comment string
User string
Files int
}
```
## type Cone
Cone is a sparse checkout's cone \(draft\-fossil\-sparse\-cone\-00 §3\): a set of repository paths, each a directory subtree or a single file, included or excluded. The longest member covering a path decides whether the cone covers it, so an exclusion carves a part out of an inclusion, and a deeper inclusion re\-includes part of an exclusion. Members are literal prefixes, never patterns. A Cone is immutable; With and Without return new ones.
```go
type Cone struct {
// contains filtered or unexported fields
}
```
### func NewCone
```go
func NewCone(include, exclude []string) (*Cone, error)
```
NewCone builds a cone from inclusion and exclusion members. Members are validated as tracked paths, sorted, and reduced to the minimal set with the same coverage, so input order never matters. It refuses an empty inclusion list, more than 255 members, an exclusion no inclusion covers, and a path given with both polarities.
### func NewHorizonCone
```go
func NewHorizonCone(include, exclude []string, h Horizon) (*Cone, error)
```
NewHorizonCone builds a cone with a horizon and optional path members. With no include members the cone is horizon\-only: it covers every path and only the horizon narrows what is held. Checkin is normalized to lower case.
### func \(\*Cone\) Covers
```go
func (c *Cone) Covers(path string) bool
```
Covers reports whether the cone covers path.
### func \(\*Cone\) Decide
```go
func (c *Cone) Decide(path string) (covered bool, member string)
```
Decide reports whether the cone covers path and the member that decided: the longest member covering it, of either polarity, or "" when none does. Under longest match the deciding member is often not the first one listed, so naming it is what explains a verdict.
### func \(\*Cone\) Digest
```go
func (c *Cone) Digest() string
```
Digest is the SHA3\-256 of the cone's canonical serialization, in hex: equal for equal coverage, and the same value zit computes.
### func \(\*Cone\) Exclude
```go
func (c *Cone) Exclude() []string
```
Exclude returns the exclusion members, sorted.
### func \(\*Cone\) Horizon
```go
func (c *Cone) Horizon() (Horizon, bool)
```
Horizon returns the cone's horizon, if it has one.
### func \(\*Cone\) Include
```go
func (c *Cone) Include() []string
```
Include returns the inclusion members, sorted.
### func \(\*Cone\) String
```go
func (c *Cone) String() string
```
String lists the members in sorted order, an exclusion marked with '\!'. A horizon leads, as "horizon \ \ content".
### func \(\*Cone\) With
```go
func (c *Cone) With(include, exclude []string) (*Cone, error)
```
With returns the cone with more members: the incremental form of widening \(or, with exclusions, of narrowing\).
### func \(\*Cone\) WithHorizon
```go
func (c *Cone) WithHorizon(h *Horizon) (*Cone, error)
```
WithHorizon returns the cone with its horizon replaced by h, keeping the path members; nil drops the horizon. Dropping the horizon of a horizon\-only cone is an error, since nothing is left: make the store dense with Repo.SetStoreCone\(nil\).
### func \(\*Cone\) Without
```go
func (c *Cone) Without(paths []string) (*Cone, error)
```
Without returns the cone with every member equal to one of paths removed, of either polarity. A path that is not a member removes nothing.
## type ConeChange
ConeChange reports what Checkout.SetCone did to the working directory.
```go
type ConeChange struct {
Materialized []string // newly covered files written, sorted
Dematerialized []string // newly uncovered files removed, sorted
}
```
## type CreateOpts
CreateOpts configures repository creation.
```go
type CreateOpts struct {
User string
// ProjectCode optionally sets the repo's project-code. Empty
// generates a fresh one (current behavior). When non-empty, must
// be 40-char lowercase hex (^[0-9a-f]{40}$) — matching the format
// of generated project-codes. Invalid values return an error
// before any file is written.
ProjectCode string
// Rand provides random bytes for project-code and server-code generation.
// Nil defaults to crypto/rand (production). Set to simio.NewSeededRand
// for deterministic simulation testing.
Rand simio.Rand
}
```
## type Cursor
Cursor is an opaque pagination token for Timeline. Obtain one from a returned TimelineEntry's Cursor field and pass it back as the next TimelineOpts.After to resume enumeration immediately after that entry. The zero Cursor means "start from the newest event".
Cursor's representation is deliberately hidden: it cannot be constructed from a timestamp and a rid by calling code, only obtained from a TimelineEntry the library already produced. See fsltype.Cursor's doc comment for why — a hand\-built cursor derived from a rounded time.Time is not guaranteed to match the row it claims to follow exactly, which is what silently reintroduces skipped or duplicated rows at a page boundary. LogEntry, Ancestry's result type, has no Cursor field at all: Ancestry's pagination is by Start/Limit, so there is no valid cursor to obtain from it, and the type reflects that instead of documenting it as a hazard.
```go
type Cursor = fsltype.Cursor
```
## type DiffEntry
DiffEntry describes a unified diff for a single file.
```go
type DiffEntry struct {
Name string
Unified string
}
```
## type EventKind
EventKind is the type discriminator on a Timeline entry: one of the single\-letter codes fossil assigns per event.type row. The zero value means "all kinds" — the default Timeline uses when no Type is given.
```go
type EventKind = fsltype.EventKind
```
```go
const (
// EventKindCheckin is a check-in ('ci').
EventKindCheckin EventKind = fsltype.EventKindCheckin
// EventKindTechnote is a technote / event artifact ('e').
EventKindTechnote EventKind = fsltype.EventKindTechnote
// EventKindForum is a forum post ('f').
EventKindForum EventKind = fsltype.EventKindForum
// EventKindTag is a tag/control artifact ('g').
EventKindTag EventKind = fsltype.EventKindTag
// EventKindTicket is a ticket change ('t').
EventKindTicket EventKind = fsltype.EventKindTicket
// EventKindWiki is a wiki page edit ('w').
EventKindWiki EventKind = fsltype.EventKindWiki
)
```
## type ExternalRef
ExternalRef names an attachment's bytes held outside the repository, in a bucket the host binds and keys attachments/\ \(Quarry RFC 0016\). The repository holds only a stub naming them; this library never reads or writes the bytes themselves.
```go
type ExternalRef struct {
// SHA256 is the bytes' SHA-256, 64 lowercase hex digits.
SHA256 string `json:"sha256"`
// Size is the bytes' length.
Size int64 `json:"size"`
// Type is the declared mimetype.
Type string `json:"type"`
// Name is the file's name, a bare name with no directory.
Name string `json:"name"`
}
```
## type ExtractEnd
ExtractEnd describes the completion of a checkout extraction.
```go
type ExtractEnd struct {
FilesWritten int
}
```
## type ExtractOpts
ExtractOpts configures file extraction from a checkin.
```go
type ExtractOpts struct {
Force bool // overwrite uncommitted changes
// Warn receives what fossil's checkout warns about and proceeds past:
// a present R card that disagrees with the files the check-in names,
// checked over every file, whatever the cone. nil discards warnings.
Warn func(message string)
}
```
## type ExtractStart
ExtractStart describes the beginning of a checkout extraction.
```go
type ExtractStart struct {
RID int64
Dir string
}
```
## type FileEntry
FileEntry describes a file in a manifest.
```go
type FileEntry struct {
Name string
UUID string
Perm string
}
```
## type FileToCommit
FileToCommit describes a file to include in a commit. OldName, when set, records that the file was renamed from that name in the parent, as fossil's commit records a rename \(the F card's old\-name field\).
UUID, when set, carries an artifact the repository already holds into the check\-in by its name: the F card names it verbatim and nothing is read or stored, so a caller that knows a file's hash from ListFiles can carry it forward without its content. Content must be nil when UUID is set, and UUID must be a lowercase SHA1 or SHA3\-256 hash. The artifact must be held, except under a store cone that elides it \(draft\-fossil\-sparse\-cone\-00 §5.3\); an unknown or phantom UUID fails the commit. This is what PartialManifest callers use to name the unchanged part of a tree without reading it.
```go
type FileToCommit struct {
Name string
Content []byte
Perm string
OldName string
UUID string
}
```
## type Fork
Fork describes a divergence point between two branches.
```go
type Fork struct {
Ancestor int64
LocalTip int64
RemoteTip int64
}
```
## type FramedTransport
FramedTransport is an optional interface a Transport may also implement when it can report the framing signal — such as an HTTP Content\-Type header — that accompanied a reply. Sync uses that signal to select the reply's §4 framing instead of assuming the §4.1 compressed container, which matters when the peer is a different Fossil\-protocol implementation that legitimately replies with the uncompressed plain\-card framing \(e.g. a real Fossil server's clone reply\).
It is optional in the same way http.Flusher is optional on an http.ResponseWriter: a Transport whose framing is fixed by convention \(both ends always emit the compressed container\) need not implement it, and sync falls back to assuming the compressed framing for such a Transport. Callers type\-assert a Transport to FramedTransport and use RoundTripFramed when it is available. Reporting the signal as a return value keeps it per\-call, with no shared mutable state to make a reused Transport unsafe across concurrent calls.
```go
type FramedTransport interface {
Transport
// RoundTripFramed behaves like RoundTrip but also returns the framing
// signal (e.g. the reply's Content-Type) the wire carried for this reply,
// or "" when the transport observed none.
RoundTripFramed(ctx context.Context, payload []byte) (resp []byte, contentType string, err error)
}
```
## type FslError
```go
type FslError struct {
Code RC
Msg string
Cause error
}
```
### func \(\*FslError\) Error
```go
func (e *FslError) Error() string
```
### func \(\*FslError\) Unwrap
```go
func (e *FslError) Unwrap() error
```
## type FslID
FslID is a row\-id in the blob table \(content\-addressed artifacts\).
```go
type FslID = fsltype.FslID
```
## type FslSize
FslSize represents a blob size; negative values indicate phantom blobs.
```go
type FslSize = fsltype.FslSize
```
## type HTTPOption
HTTPOption configures an HTTP transport.
```go
type HTTPOption func(*httpTransport)
```
### func WithHTTPClient
```go
func WithHTTPClient(c *http.Client) HTTPOption
```
WithHTTPClient sets a custom http.Client for the transport.
## type HandleEnd
HandleEnd describes the completion of a server\-side sync handle.
```go
type HandleEnd struct {
FilesSent, FilesRecvd int
}
```
## type HandleOpts
HandleOpts configures server\-side sync handling.
```go
type HandleOpts struct {
Observer SyncObserver
Buggify BuggifyChecker
// LoginCookie is the value of the request's x-f-l-c cookie, which
// carries the login card once the client has seen a server-version
// that reads it (§6.5). This server announces such a release, so fossil
// and go-libfossil clients move their login there after the first
// round: an embedder that serves HandleSyncWithOpts over HTTP must pass
// the cookie through, or those rounds arrive as nobody. XferHandler and
// ServeHTTP read it themselves.
LoginCookie string
// RemoteAddr is the address the request came from, recorded in the
// rcvfrom row of whatever it pushes, as fossil records REMOTE_ADDR.
// XferHandler and ServeHTTP fill it themselves.
RemoteAddr string
// MaxStoredBytes, when positive, is the most bytes one pushed artifact
// may occupy as this repository stores it (compressed, and as a delta
// when pushed as one). Set it when the host's storage caps a single
// value: a push carrying a bigger artifact is refused whole with an
// error naming the artifact, its stored size and the limit, and the
// file and check-in when this repository already holds a check-in
// listing it, so nothing from that request is stored, and
// HandleSyncWithOpts returns a *RefusedError with that reply. 0, the
// default, stores any size. Clones and pulls served from the repository
// are not limited. XferHandler and ServeHTTP serve without a budget: a
// host that needs one serves HandleSyncWithOpts itself.
MaxStoredBytes int
}
```
## type HandleStart
HandleStart describes the beginning of a server\-side sync handle.
```go
type HandleStart struct {
RemoteAddr string
}
```
## type Horizon
Horizon is the temporal member of a store cone \(draft\-fossil\-horizon\-00 §3.1\): blobs only check\-ins outside the held set name are elided. The zero value of Structural is the content horizon \(§4\), which keeps every manifest.
```go
type Horizon struct {
// Checkin is the check-in's full artifact hash, 40 or 64 hex digits; as
// CloneOpts.Horizon it may also be a name the origin resolves.
Checkin string
Rule HorizonRule
// Structural takes the structural cut (§5): the manifests below the
// check-in are elided too, and a seed the origin mints stands in for the
// branch and tag state they carried. A clone or pull whose origin does
// not serve it falls back to the content horizon and reports so.
Structural bool
}
```
## type HorizonRule
HorizonRule is the held\-set rule a horizon names \(draft\-fossil\-horizon\-00 §3.1\).
```go
type HorizonRule int
```
```go
const (
// HorizonDescendants holds the check-in and every descendant of it. It is
// the zero value.
HorizonDescendants HorizonRule = iota
// HorizonLeaves holds the descendants plus every open leaf's ancestry back
// to its nearest common ancestor with the check-in. Choose it knowingly
// (§3.1): it is what lets a client merge across live branches offline, and
// it holds every descendant of where each childless check-in's history
// meets the check-in's. On a repository with old unmerged branches that is
// nearly the whole history: 20,907 of 21,008 check-ins of fossil-scm.org's.
HorizonLeaves
)
```
### func \(HorizonRule\) String
```go
func (r HorizonRule) String() string
```
String names the rule as the operator spells it.
## type HorizonRuleCost
HorizonRuleCost is what one rule would hold for a horizon at a check\-in \(draft\-fossil\-horizon\-00 §3.4\): the check\-ins of the held set K and the blobs those check\-ins' F cards name, the contents a clone at that horizon keeps.
```go
type HorizonRuleCost struct {
Rule HorizonRule
Checkins int // check-ins in K
Blobs int // distinct blobs K's check-ins name, held here
StoredBytes int64 // what those blobs occupy in the repository, compressed and delta-encoded
ExpandedBytes int64 // their sizes once expanded
}
```
## type LogEntry
LogEntry represents a single event, as returned by Ancestry. Parents is populated for Kind == EventKindCheckin and empty for every other kind — plink, which Parents is derived from, only relates check\-in artifacts, so that is correct, not a gap.
LogEntry carries no pagination cursor: Ancestry paginates by LogOpts.Start/Limit, not by cursor, so there would be nothing valid to put in one. See TimelineEntry, Timeline's result type, for the cursor\-carrying counterpart — this split means an Ancestry entry's \(necessarily invalid\) cursor can no longer be obtained at all, let alone passed to Timeline by mistake.
```go
type LogEntry struct {
RID int64
UUID string
Comment string
User string
Time time.Time
Kind EventKind
Parents []string
// HorizonParents is the subset of Parents that the store's origin
// labelled below its structural horizon: each is the horizon, not a
// missing parent and not damage (draft-fossil-horizon-00 §5.4). Ancestry
// ends at such a parent without an error.
HorizonParents []string
}
```
## type LogOpts
LogOpts configures an Ancestry query: a first\-parent walk starting from a specific check\-in.
```go
type LogOpts struct {
Start int64
Limit int
}
```
## type MergeConflict
MergeConflict describes a conflict region in a file.
```go
type MergeConflict struct {
Name string
StartLine int
EndLine int
}
```
## type MergeConflictError
MergeConflictError reports which files had unresolved merge conflicts. Files is sorted alphabetically for deterministic output.
```go
type MergeConflictError struct {
Files []string
}
```
### func \(\*MergeConflictError\) Error
```go
func (e *MergeConflictError) Error() string
```
### func \(\*MergeConflictError\) Is
```go
func (e *MergeConflictError) Is(target error) bool
```
## type MergeResult
MergeResult describes the outcome of a merge.
```go
type MergeResult struct {
Clean bool
Conflicts []MergeConflict
}
```
## type MockTransport
MockTransport is a test double that delegates to a handler function.
```go
type MockTransport struct {
Handler func(req []byte) []byte
}
```
### func \(\*MockTransport\) RoundTrip
```go
func (t *MockTransport) RoundTrip(_ context.Context, payload []byte) ([]byte, error)
```
RoundTrip calls the Handler function if set, otherwise returns empty bytes.
## type OriginNamer
OriginNamer is an optional interface a Transport may also implement to name the peer it addresses. Sync and Clone keep the horizon cursor of draft\-fossil\-horizon\-00 §6.2.6 per origin under that name: "A client that converged records the cursor of the last reply together with the count of its own artifacts at the end of the exchange, per origin", and "A client holding a cursor for the origin it addresses sends it on every request, and MAY then announce nothing with igot on a pull\-only request."
The name must identify the same peer across sessions and must carry no credential, since it is written into the repository's config table; the HTTP transport uses its URL with any password removed. A Transport that does not implement it, or returns "", keeps no cursor, and each of its pulls announces the client's whole holding with igot, which §6.2.6 allows.
```go
type OriginNamer interface {
Transport
SyncOrigin() string
}
```
## type PullOpts
PullOpts configures a pull\-only sync. Fields are a subset of SyncOpts; Pull is hard\-coded true and Push is hard\-coded false to keep the API surface honest about what Pull does.
```go
type PullOpts struct {
// ProjectCode optionally pins the expected server project code. Empty
// accepts whatever the peer advertises (matches existing Sync semantics).
ProjectCode string
// MaxSend caps the bytes the client will send per round (mostly clones
// of clients with UV files). Zero leaves the existing default in place.
MaxSend int
// Observer receives sync-progress events. nil disables observation.
Observer SyncObserver
}
```
## type RC
```go
type RC int
```
```go
const (
RCOK RC = 0
RCError RC = 100
RCNYI RC = 101
RCOOM RC = 102
RCMisuse RC = 103
RCRange RC = 104
RCAccess RC = 105
RCIO RC = 106
RCNotFound RC = 107
RCAlreadyExists RC = 108
RCConsistency RC = 109
RCRepoNeedsRebuild RC = 110
RCNotARepo RC = 111
RCRepoVersion RC = 112
RCDB RC = 113
RCBreak RC = 114
RCStepRow RC = 115
RCStepDone RC = 116
RCStepError RC = 117
RCType RC = 118
RCNotACkout RC = 119
RCRepoMismatch RC = 120
RCChecksumMismatch RC = 121
RCLocked RC = 122
RCConflict RC = 123
RCSizeMismatch RC = 124
RCPhantom RC = 125
RCUnsupported RC = 126
)
```
### func \(RC\) String
```go
func (rc RC) String() string
```
## type RefusedError
RefusedError is the error HandleSyncWithOpts returns, beside the reply, when HandleOpts.MaxStoredBytes refused the request. Message is the text of the reply's one error card.
```go
type RefusedError struct {
Message string
}
```
### func \(\*RefusedError\) Error
```go
func (e *RefusedError) Error() string
```
Error returns Message.
## type Repo
Repo is an opaque handle to a Fossil repository.
```go
type Repo struct {
// contains filtered or unexported fields
}
```
### func Create
```go
func Create(path string, opts CreateOpts) (*Repo, error)
```
Create creates a new Fossil repository at the given path.
### func Open
```go
func Open(path string) (*Repo, error)
```
Open opens an existing Fossil repository.
### func \(\*Repo\) Ancestry
```go
func (r *Repo) Ancestry(opts LogOpts) ([]LogEntry, error)
```
Ancestry walks the primary\-parent chain starting from opts.Start: it emits that check\-in, follows plink where isprim=1 to its parent, and repeats until the chain ends or opts.Limit is reached. This only ever sees first\-parent ancestors of opts.Start — a merge's second parent, a sibling branch head, or any check\-in outside that one chain will never appear. Use Timeline for a repository\-wide view.
### func \(\*Repo\) Annotate
```go
func (r *Repo) Annotate(opts AnnotateOpts) ([]AnnotatedLine, error)
```
Annotate attributes each line of a file to the commit that last changed it.
On a store whose cone has a horizon, lines the walk cannot attribute past the horizon are reported as originating at it, marked BelowHorizon; AnnotateContext fetches the history that attributes them on a content horizon store.
### func \(\*Repo\) AnnotateContext
```go
func (r *Repo) AnnotateContext(ctx context.Context, opts AnnotateOpts, required RequiredContentOpts) ([]AnnotatedLine, error)
```
AnnotateContext is Annotate with a demand fetch: on a content\-horizon store, each version of the file the walk reads below the horizon and the store does not hold is fetched from Peer as the walk reaches it, so only what the walk reads is fetched and lines are attributed to the check\-ins below the horizon that added them. A walk needing more than one demand fetch's 200 payloads stops there and returns what it attributed, the rest marked BelowHorizon, as does a walk that crosses a structural horizon, since naming those check\-ins is a deepen. A start check\-in below the horizon is fetched as ReadFileContext fetches it.
### func \(\*Repo\) AttachmentAdd
```go
func (r *Repo) AttachmentAdd(target, name string, content []byte, opts AttachmentOpts) (artifact string, err error)
```
AttachmentAdd attaches content to target under name, as \`fossil attachment add\` does, and returns the hash of the control artifact. content is stored as one ordinary blob; only name's final path component is kept. The attachment table and timeline reflect it when AttachmentAdd returns, and both artifacts are queued to sync. The attachment becomes the latest for name: a zero opts.Time stamps it after whatever is latest now, and an explicit one no later than that is refused.
target is a wiki page's exact name, or a ticket's or tech note's id or a unique prefix of it. Fossil is told the kind; here it is inferred, and a name more than one kind answers to is refused as ambiguous.
Content that is a well\-formed external\-attachment stub is refused, since it would read back as an external attachment rather than as the bytes given: use AttachmentAddExternal. Content that parses as a fossil artifact is compressed and its name gains ".gz", as fossil does.
### func \(\*Repo\) AttachmentAddExternal
```go
func (r *Repo) AttachmentAddExternal(target string, ref ExternalRef, opts AttachmentOpts) (artifact string, err error)
```
AttachmentAddExternal attaches the file ref names without its bytes, and returns the hash of the control artifact. The blob stored is the stub Quarry writes, and the control artifact's N card carries ref.Type, so stock fossil shows a small text file under ref.Name. The host stores the bytes; this records only where they are found. target resolves as in AttachmentAdd.
### func \(\*Repo\) AttachmentDelete
```go
func (r *Repo) AttachmentDelete(target, name string, opts AttachmentOpts) (artifact string, err error)
```
AttachmentDelete removes the current attachment name from target, as fossil's attachment page does: a new control artifact whose A card names no file. The bytes stay in history. It returns the artifact's hash, and ErrAttachmentNotFound when target has no current attachment by that name. opts.Time, when set, must be later than the attachment's; when zero, the deletion is stamped after it even if it lies in the future.
### func \(\*Repo\) AttachmentRead
```go
func (r *Repo) AttachmentRead(target, name string) (a Attachment, content []byte, err error)
```
AttachmentRead returns target's current attachment name and its bytes. For an external attachment the bytes are not here: content is nil and a.External says where the host keeps them. It returns ErrAttachmentNotFound when there is no such current attachment.
### func \(\*Repo\) Attachments
```go
func (r *Repo) Attachments(target string) ([]Attachment, error)
```
Attachments returns target's current attachments, the most recently attached first: one per name, leaving out deleted ones, as fossil lists them on the target's page. target resolves as in AttachmentAdd.
### func \(\*Repo\) BranchOpenLeaf
```go
func (r *Repo) BranchOpenLeaf(name string) (rid int64, open bool, err error)
```
BranchOpenLeaf names one open leaf of the named branch \(repo\-state\-00 Section 9\): a check\-in whose active branch tag is name, that is a leaf and that is not closed. When the branch has several, the one with the greatest event.mtime is returned, as the draft requires of any report that names a single open leaf. open is false, with a nil error, when the branch has no open leaf: it is not open. An empty name is an error, as in BranchTip; the main branch is named by CurrentTrunk and MainBranch.
Unlike BranchTip, which returns the branch's newest check\-in whether or not it is a leaf or closed, this is fossil's \`branch info\` answer. fossil's query \(branch\_is\_open, src/branch.c\) takes whichever open leaf it meets first; this follows the draft's greatest\-mtime rule.
### func \(\*Repo\) BranchTip
```go
func (r *Repo) BranchTip(name string) (int64, error)
```
BranchTip returns the RID of the most recent checkin on the named branch. Resolves via the 'branch' propagating tag: the tip is the checkin with the latest event.mtime whose tagxref still has that branch value active. Returns an error if no such branch exists in the repository.
### func \(\*Repo\) Checkpoint
```go
func (r *Repo) Checkpoint(mode CheckpointMode) error
```
Checkpoint runs PRAGMA wal\_checkpoint\(\\) against the repository. Safe to call on a live repo. CheckpointPassive is non\-blocking and appropriate for periodic background checkpoints. CheckpointTruncate produces a maximally compact on\-disk file readable by external fossil tooling without a subsequent Close.
### func \(\*Repo\) Close
```go
func (r *Repo) Close() error
```
Close closes the repository and releases resources. As part of close, a WAL TRUNCATE checkpoint is run so the on\-disk repo file is readable by external fossil/SQLite tooling.
### func \(\*Repo\) Commit
```go
func (r *Repo) Commit(opts CommitOpts) (int64, string, error)
```
Commit creates a new checkin manifest with the given files and returns the RID \(row ID\) and UUID of the newly created artifact. When ParentID is non\-zero, files tracked at the parent but absent from opts.Files are carried forward into the new manifest \(full\-tree semantics — see the CommitOpts.Files doc\).
### func \(\*Repo\) CommitContext
```go
func (r *Repo) CommitContext(ctx context.Context, opts CommitOpts, required RequiredContentOpts) (int64, string, error)
```
CommitContext creates a commit, recovering required parent structure from Peer when necessary. Recovery runs without a write transaction and never broadens the store cone. The operation is retried once from the caller's original inputs after revalidating immutable parents and construction policy.
### func \(\*Repo\) CompactStore
```go
func (r *Repo) CompactStore(ctx context.Context) (int, error)
```
CompactStore is the narrow\-then\-compact deletion \(§5.4\), the only one the sparse draft defines: it turns held blobs that only out\-of\-cone F cards name into elided phantoms and returns how many it dropped. Structural artifacts, sub\-manifest nodes whose cards prove an elision, unsent or private content and the delta bases of retained artifacts stay held. A later widening fetches the dropped blobs again; it is not reversible locally.
### func \(\*Repo\) Config
```go
func (r *Repo) Config(key string) (string, error)
```
Config reads a configuration value from the repo's config table.
### func \(\*Repo\) CreateCheckout
```go
func (r *Repo) CreateCheckout(dir string, opts CheckoutCreateOpts) (*Checkout, error)
```
CreateCheckout creates a new checkout directory linked to this repository. The directory is created if it does not exist. The checkout is initialized to the tip checkin.
### func \(\*Repo\) CreateUser
```go
func (r *Repo) CreateUser(opts UserOpts) error
```
CreateUser creates a new user in the repository.
### func \(\*Repo\) CurrentTrunk
```go
func (r *Repo) CurrentTrunk() (int64, error)
```
CurrentTrunk returns the current trunk \(repo\-state\-00 Section 9\): the check\-in that the main\-branch setting's name resolves to by ResolveVersion's procedure, "trunk" when the setting is unset. Only check\-ins can answer, as in fossil's check\-in request, so a technote tagged with the name does not. The setting is local and not synced, so two repositories holding the same artifacts can name different trunks. fossil resolves it the same way where it needs one: update outside a checkout \(src/update.c, a "ci" request\) and open with no version \(src/db.c\). A name that resolves to nothing returns ErrVersionNotFound \(wrapped\).
### func \(\*Repo\) DB
```go
func (r *Repo) DB() *db.DB
```
DB returns the underlying database handle for raw SQL queries. Use this when the high\-level Repo methods don't cover your use case.
### func \(\*Repo\) DeepenHorizon
```go
func (r *Repo) DeepenHorizon(ctx context.Context, target string, required RequiredContentOpts) (*SyncResult, error)
```
DeepenHorizon moves the store cone's horizon to an ancestor C' of its C from which target is reachable, and synchronizes at C' through Peer \(draft\-fossil\-horizon\-00 §5.8\): target itself when it is C's ancestor, else a common ancestor of the two, asked of the origin when the store cannot see one. The rule, the depth and the path members stay; nothing held is replaced. A structural store discards the seed for C before it takes the one for C', and a transfer that fails puts both back.
target is a full check\-in hash, which may name a check\-in the store does not hold, or any name ResolveVersion resolves. Deepening needs a Peer and a store cone with a horizon.
### func \(\*Repo\) DeleteUser
```go
func (r *Repo) DeleteUser(login string) error
```
DeleteUser removes a user from the repository.
### func \(\*Repo\) DetectForks
```go
func (r *Repo) DetectForks() ([]Fork, error)
```
DetectForks finds divergent branches in the repository.
### func \(\*Repo\) Diff
```go
func (r *Repo) Diff(ridA, ridB int64, filePath string) ([]DiffEntry, error)
```
Diff returns the unified diff\(s\) between ridA and ridB.
When filePath is non\-empty, returns 0 or 1 entries for that single file: the file is treated as empty bytes on any side where it is absent, so additions and deletions render as pure insert/delete hunks. An empty slice is returned when both sides are byte\-identical.
When filePath is empty, returns one entry per file that changed between the two checkins \(the union of files across both sides where the content UUID differs or the file exists on only one side\). Entries are sorted by Name for deterministic ordering. An empty slice is returned when the two checkins have identical file sets and content.
Whole\-checkin enumeration is currently name\-keyed: a rename with no content change surfaces as a delete of the old name plus an add of the new name, and permission\-only changes are not reflected. Proper rename and perm\-change detection \(via the underlying mlink table\) is tracked as a follow\-up; per\-file diff behaviour for an explicitly named file is unchanged.
### func \(\*Repo\) DiffContext
```go
func (r *Repo) DiffContext(ctx context.Context, ridA, ridB int64, filePath string, required RequiredContentOpts) ([]DiffEntry, error)
```
DiffContext is Diff with ReadFileContext's demand fetch: what both sides lack below the horizon is fetched in one round.
### func \(\*Repo\) DisableSubmanifests
```go
func (r *Repo) DisableSubmanifests() error
```
DisableSubmanifests turns tree manifest emission off: later commits are flat, and the pinned k is kept for a later EnableSubmanifests. Existing trees remain \-\- artifact names are immutable, so they cannot be retired \-\- and keep being served, though peers that do not announce sub\-manifest support are still refused them. Unlike the CLI's disable, it returns no note saying so; an embedder that wants to tell its operator must do it itself.
### func \(\*Repo\) EnableSubmanifests
```go
func (r *Repo) EnableSubmanifests(k int) (pinned int, warning string, err error)
```
EnableSubmanifests turns on tree manifest emission \(draft\-fossil\-submanifests\-00 §8.1\): from the next commit, check\-ins carry their file sets as sub\-manifest trees. k is the partition parameter, 1 to 12, or 0 to select one. A valid pinned k is kept, and an explicit k that differs from it is refused: changing it is RetuneSubmanifests. A stored k outside 1..12 is no valid pin: an explicit k replaces it, and k = 0 is refused, naming the stored value. With no stored k, an explicit k is pinned, or one is selected as zit selects it: the §6.3 cost over one check\-in's file set, refined by replaying recent history. The check\-in analyzed is the current trunk \(CurrentTrunk, the main\-branch tip, which the next commit most plausibly descends from\); when no check\-in is on the main branch, it is the newest check\-in by time, the highest rid breaking a tie. A request that cannot be met \-\- k outside 1..12, no check\-in at all to select from, or a file set no k partitions \-\- changes neither setting. It returns the pinned k and the warning the operator must be shown: peers that do not announce sub\-manifest support, every fossil among them, are refused trees, and trees cannot be retired.
### func \(\*Repo\) FindCommonAncestor
```go
func (r *Repo) FindCommonAncestor(ridA, ridB int64) (int64, error)
```
FindCommonAncestor finds the most recent common ancestor of two checkins, the merge base fossil's merge\-base command names.
### func \(\*Repo\) GetUser
```go
func (r *Repo) GetUser(login string) (User, error)
```
GetUser returns information about a user.
### func \(\*Repo\) HandleSync
```go
func (r *Repo) HandleSync(ctx context.Context, payload []byte) ([]byte, error)
```
HandleSync processes an incoming xfer request \(server\-side\). The payload is a raw xfer\-encoded byte slice; the response is also raw bytes.
### func \(\*Repo\) HandleSyncWithOpts
```go
func (r *Repo) HandleSyncWithOpts(ctx context.Context, payload []byte, opts HandleOpts) ([]byte, error)
```
HandleSyncWithOpts processes an incoming xfer request with optional configuration.
When opts.MaxStoredBytes refuses the request, it returns both the encoded reply, which holds only the error card naming the artifact, and a \*RefusedError carrying that card's text \(match it with errors.As\). The request was rolled back, so nothing from it is stored. A host serving the reply over HTTP sends it as usual, and the client prints the error; a host whose storage aborts the whole call on any rollback can report RefusedError.Message itself. Every other error returns a nil reply.
### func \(\*Repo\) HorizonCost
```go
func (r *Repo) HorizonCost(checkin string) ([]HorizonRuleCost, error)
```
HorizonCost reports, for each rule in order \(descendants, leaves\), what a horizon at checkin would hold, so the choice of rule is made with its cost in view \(§3.4\). checkin is any name ResolveVersion accepts. The figures are read from this repository, which must hold the history: a blob it does not hold is left out of the byte counts, and a sub\-manifest it lacks is an error.
### func \(\*Repo\) Inner
```go
func (r *Repo) Inner() *repo.Repo
```
Inner returns the underlying internal repo handle. This is exported for use by in\-module packages \(e.g., cli/\) that need direct access to the repo DB for raw SQL or internal package calls.
### func \(\*Repo\) ListConflictForks
```go
func (r *Repo) ListConflictForks() ([]string, error)
```
ListConflictForks returns filenames with unresolved conflict\-fork entries.
### func \(\*Repo\) ListFiles
```go
func (r *Repo) ListFiles(rid int64) ([]FileEntry, error)
```
ListFiles returns the files in a manifest identified by blob row\-id.
### func \(\*Repo\) ListFilesIn
```go
func (r *Repo) ListFilesIn(rid int64, c *Cone) ([]FileEntry, error)
```
ListFilesIn is ListFiles narrowed to the files c covers; a nil cone lists every file. The cone is applied as a range predicate at the one enumeration seam every file set comes from \(draft\-fossil\-sparse\-cone\-00 §4.1\), so the result is ListFiles filtered by c.Covers, flat or tree, and a cone's horizon plays no part. On a tree manifest the walk skips every sub\-manifest whose key range c cannot cover, so the cost follows the cone plus the spine rather than the whole tree \(§4.5\). Over a sparse store that makes it the listing that does not reach an elided out\-of\-cone node; a cone that reaches a node the store lacks still fails, naming it.
### func \(\*Repo\) ListUsers
```go
func (r *Repo) ListUsers() ([]User, error)
```
ListUsers returns all users in the repository.
### func \(\*Repo\) Merge
```go
func (r *Repo) Merge(srcBranch, dstBranch, message, user string) (int64, string, error)
```
Merge performs a three\-way merge of srcBranch into dstBranch and creates a merge commit whose primary parent is dstBranch's tip and whose secondary parent is srcBranch's tip. Returns \(rid, uuid\) of the new commit on success.
Conflict policy: any unresolved conflict in any file aborts the whole operation with a \*MergeConflictError — no commit is written. Callers should check errors.Is\(err, ErrMergeConflict\) to detect this case.
File handling:
- Present on both sides: three\-way merge against the common ancestor.
- Present on only one side \(new file\): kept as\-is.
- In ancestor, missing on one side, unchanged on the other: agreed deletion.
- In ancestor, missing on one side, modified on the other: modify/delete conflict.
Files are matched across the three versions following renames, as fossil's merge matches them: a file srcBranch renamed and dstBranch did not takes srcBranch's name, and the commit records the rename; a file dstBranch renamed keeps its name.
### func \(\*Repo\) OpenCheckout
```go
func (r *Repo) OpenCheckout(dir string, opts CheckoutOpenOpts) (*Checkout, error)
```
OpenCheckout opens an existing checkout directory linked to this repository.
### func \(\*Repo\) Path
```go
func (r *Repo) Path() string
```
Path returns the filesystem path to the repository file.
### func \(\*Repo\) Pull
```go
func (r *Repo) Pull(ctx context.Context, url string, opts PullOpts) (*SyncResult, error)
```
Pull fetches commits and ancillary objects from a Fossil HTTP peer and applies them to this repo. It is a strict pull — nothing is sent.
Tiger Style: hostile inputs panic via assert at the boundary; transport failures return wrapped errors. Idempotent on a repo already at peer's tip \(returns a SyncResult with Rounds=0–1 and FilesRecvd=0\).
### func \(\*Repo\) ReadBlob
```go
func (r *Repo) ReadBlob(uuid string) ([]byte, error)
```
ReadBlob returns the content of the artifact named uuid, a full SHA1 or SHA3\-256 hash in lowercase hex, such as the UUID ListFiles reports for a file. An unknown hash returns ErrArtifactNotFound \(wrapped\). An artifact the store knows but does not hold, such as one elided below a horizon or outside a sparse cone, fails as ReadFile of a path naming it does.
### func \(\*Repo\) ReadFile
```go
func (r *Repo) ReadFile(rid int64, filePath string) ([]byte, error)
```
ReadFile returns the bytes of filePath as they existed in checkin rid. Returns ErrFileNotFound \(wrapped\) if the file is not tracked in that checkin. A file that exists but is empty returns \(\[\]byte\{\}, nil\).
### func \(\*Repo\) ReadFileAt
```go
func (r *Repo) ReadFileAt(version string, filePath string) ([]byte, error)
```
ReadFileAt reads filePath from the checkin identified by a symbolic version name \(e.g. "tip", "trunk", a branch name, a UUID, or a UUID prefix\). It calls ResolveVersion to obtain the RID, then delegates to ReadFile. Use ReadFile directly when you already have a numeric RID.
### func \(\*Repo\) ReadFileAtContext
```go
func (r *Repo) ReadFileAtContext(ctx context.Context, version string, filePath string, required RequiredContentOpts) ([]byte, error)
```
ReadFileAtContext is ReadFileAt with ReadFileContext's demand fetch. The version is resolved locally; a structural store resolves a check\-in below its horizon by the full hash its phantom carries.
### func \(\*Repo\) ReadFileContext
```go
func (r *Repo) ReadFileContext(ctx context.Context, rid int64, filePath string, required RequiredContentOpts) ([]byte, error)
```
ReadFileContext is ReadFile that, when the check\-in's file lies below the store cone's horizon and the store does not hold it, fetches exactly the artifacts the read needs from Peer and reads again: the payload, and for a structural store first the check\-in's manifest, baseline and nodes. This is the targeted demand fetch of draft\-fossil\-horizon\-00 §4.4: the store cone and its horizon do not move, and what is fetched is held content a later CompactStore may drop. Without a Peer the read fails as ReadFile does, matching ErrBelowHorizon and naming the horizon. A read needing more than one demand fetch carries is refused; DeepenHorizon is its remedy.
### func \(\*Repo\) Redo
```go
func (r *Repo) Redo(dir string) error
```
Redo re\-applies the last undone operation. Requires a checkout database; not yet wired.
### func \(\*Repo\) ResolveBranchStart
```go
func (r *Repo) ResolveBranchStart(name string) (int64, error)
```
ResolveBranchStart resolves name as ResolveVersion does, then returns the first check\-in of the branch the result is on: fossil's branch\-type \("br"\) request \(repo\-state\-00 Section 8.3\), the start of the branch rather than the check\-in its name most recently points at. As Section 8.3 requires, the request is rewritten to a check\-in request first: only check\-ins can match, so a technote tagged with the branch name does not shadow the branch.
Section 8.3 walks back from the result of every form. fossil\(1\) walks only from a "tag:NAME" or plain\-name result \(symbolic\_name\_to\_rid, src/name.c\), so there a hash, "tip" or a date names the check\-in itself; this follows the draft.
### func \(\*Repo\) ResolveConflictFork
```go
func (r *Repo) ResolveConflictFork(filename string) error
```
ResolveConflictFork marks a conflict\-fork entry as resolved.
### func \(\*Repo\) ResolveVersion
```go
func (r *Repo) ResolveVersion(name string) (int64, error)
```
ResolveVersion resolves a symbolic version name to a repository artifact RID.
It follows fossil's symbolic\_name\_to\_rid \(src/name.c\) for the forms go\-libfossil supports, in fossil's order:
1. "tip" — newest check\-in by event.mtime. "" is treated as "tip".
2. "rid:N" — the literal RID N, when that artifact exists.
3. "tag:NAME" — the check\-in carrying sym\-NAME with the greatest event.mtime \(forces a tag lookup even for a hex NAME\).
4. "root:BR" / "start:BR" / "merge\-in:BR" — the origin, first check\-in, or most recent merge\-in of branch BR \(BR itself resolved recursively\).
5. "NAME:TIMESTAMP" — the check\-in carrying sym\-NAME with the greatest event.mtime at or before TIMESTAMP \(UTC\).
6. Artifact hash or 4–64 char hex prefix — unique match against blob.uuid, tried before a plain tag; more than one match is ambiguous.
7. Plain symbolic name — the check\-in carrying sym\-NAME with the greatest event.mtime \(resolves "trunk" and branch names\).
8. Trailing\-punctuation retry — a name pasted from prose that ends in one or two punctuation characters is retried without them.
No match returns ErrVersionNotFound \(wrapped\); a hex prefix matching more than one artifact returns ErrAmbiguousVersion \(wrapped\).
The names relative to an open checkout \("current", "prev", "next"\) are ordinary names here, as in fossil outside a checkout; Checkout's ResolveVersion resolves them.
### func \(\*Repo\) RetuneSubmanifests
```go
func (r *Repo) RetuneSubmanifests(k int) error
```
RetuneSubmanifests pins a new partition parameter k, 1 to 12, for the commits that follow \(draft\-fossil\-submanifests\-00 §6.4\). It is forward only: existing check\-ins are unchanged, and the next tree shares only the sub\-manifests whose bytes are the same under both values.
### func \(\*Repo\) ServeHTTP
```go
func (r *Repo) ServeHTTP(ctx context.Context, addr string) error
```
ServeHTTP starts an HTTP server that accepts Fossil xfer requests. Blocks until ctx is cancelled.
Requests are authorized against the user table: one without a login card gets nobody's capabilities, which in a new repository allow clone and pull but not push. Grant more with SetCaps. Like XferHandler it serves with default HandleOpts, so no HandleOpts.MaxStoredBytes budget applies.
### func \(\*Repo\) SetCaps
```go
func (r *Repo) SetCaps(login, caps string) error
```
SetCaps updates a user's capability string.
### func \(\*Repo\) SetConfig
```go
func (r *Repo) SetConfig(key, value string) error
```
SetConfig writes a configuration value to the repo's config table. A SettingHorizonSeedKey value that is neither an SSH public key nor an OpenPGP fingerprint is refused.
### func \(\*Repo\) SetContentCache
```go
func (r *Repo) SetContentCache(bytes int64)
```
SetContentCache attaches a cache of expanded artifact content, bounded by bytes, that ReadFile, ReadFileAt, ReadBlob, ListFiles, ListFilesIn, Diff and Annotate share. It replaces any cache already attached; bytes \<= 0 detaches it. The default is no cache. It is safe to call while other goroutines read.
The bound covers expanded content; the parses of the few check\-in manifests read most recently are kept beside it. Any write through this Repo empties the cache. A write by another process, or through another Repo opened on the same file, does not: attach a cache only where this Repo is the store's one writer, or where no other writer purges, compacts or narrows it.
### func \(\*Repo\) SetHashPolicy
```go
func (r *Repo) SetHashPolicy(name string) error
```
SetHashPolicy sets the repository's hash policy by name, as \`fossil hash\-policy NEW\-POLICY\` does. name must be exactly one of sha1, auto, sha3, sha3\-only or shun\-sha1: the match is case\-sensitive, and any other string is an error that leaves the policy unchanged. Asking for auto in a repository that already holds a SHA3\-named artifact sets sha3.
### func \(\*Repo\) SetPassword
```go
func (r *Repo) SetPassword(login, password string) error
```
SetPassword updates a user's password.
### func \(\*Repo\) SetStoreCone
```go
func (r *Repo) SetStoreCone(c *Cone) error
```
SetStoreCone replaces the store cone; nil makes the store dense. It moves no content. Narrowing deletes nothing \(§5.4\). Widening makes the newly covered elided phantoms wanted again, and the next pull fetches them. While a cone is set, a commit carries no R card \(§4.3\).
A cone may carry a horizon \(draft\-fossil\-horizon\-00\): setting one elides blobs only check\-ins below it name, and SetStoreCone moves no content, as for any narrowing. Setting an older check\-in, or nil, widens, and SetStoreConeContext fetches what the deeper horizon keeps.
### func \(\*Repo\) SetStoreConeContext
```go
func (r *Repo) SetStoreConeContext(ctx context.Context, next *Cone, required RequiredContentOpts) (*SyncResult, error)
```
SetStoreConeContext is the policy form of demand fetch \(draft\-fossil\-sparse\-cone\-00 §6.4\): it replaces the store cone and, when that makes elided artifacts wanted again, pulls them from Peer through an ordinary cone\-scoped synchronization. With artifacts to fetch and no Peer it refuses, naming the elided count and the missing peer, and leaves the cone unchanged. A transfer that fails after the cone is replaced keeps the wider policy, so the next ordinary pull heals it. Narrowing needs no peer.
### func \(\*Repo\) StashApply
```go
func (r *Repo) StashApply(dir string, id int64) error
```
StashApply restores a stash entry by ID without removing it.
### func \(\*Repo\) StashClear
```go
func (r *Repo) StashClear() error
```
StashClear removes all stash entries.
### func \(\*Repo\) StashDrop
```go
func (r *Repo) StashDrop(id int64) error
```
StashDrop removes a stash entry by ID.
### func \(\*Repo\) StashList
```go
func (r *Repo) StashList() ([]StashEntry, error)
```
StashList returns all stash entries.
### func \(\*Repo\) StashPop
```go
func (r *Repo) StashPop(dir string) error
```
StashPop restores the most recent stash entry and removes it.
### func \(\*Repo\) StashSave
```go
func (r *Repo) StashSave(dir, comment string) error
```
StashSave saves working\-tree changes to the stash. Requires a checkout database; not yet wired \(Repo only wraps the repo DB\).
### func \(\*Repo\) StoreCone
```go
func (r *Repo) StoreCone() (*Cone, error)
```
StoreCone returns the repository's store cone, or nil when the store is dense. A store cone \(draft\-fossil\-sparse\-cone\-00 §5\) is local policy: an artifact the cone leaves out may be deliberately absent, an elided phantom, which synchronization does not request and verification counts apart from damage. A cone this version cannot read is lost policy and reads as nil, so the store heals toward dense.
### func \(\*Repo\) Sync
```go
func (r *Repo) Sync(ctx context.Context, t Transport, opts SyncOpts) (*SyncResult, error)
```
Sync runs a sync session against the given transport.
### func \(\*Repo\) Tag
```go
func (r *Repo) Tag(opts TagOpts) (int64, error)
```
Tag creates a control artifact that adds a tag to a target artifact. Returns the UUID of the tag control artifact.
As \`fossil tag add\` does, a name with a reserved prefix \("wiki\-", "tkt\-", "event\-"\) is refused, and when the target is a check\-in the name is placed in the symbolic namespace with a "sym\-" prefix so it resolves as a tag in commands such as update and merge; other artifact types keep the name bare. Pass the raw name: fossil prefixes the raw name, so "x" becomes "sym\-x" and "sym\-x" becomes "sym\-sym\-x" on a check\-in.
### func \(\*Repo\) TicketAdd
```go
func (r *Repo) TicketAdd(opts TicketOpts) (ticketUUID, artifact string, err error)
```
TicketAdd creates a ticket, as \`fossil ticket add\` does, and returns its new id and the hash of the artifact that created it. The ticket table reflects it when TicketAdd returns, and the artifact is queued to sync.
### func \(\*Repo\) TicketChanges
```go
func (r *Repo) TicketChanges(ticket string) ([]TicketChange, error)
```
TicketChanges returns the changes of the ticket whose id is or starts with ticket, oldest first: the order fossil applies them in, as \`fossil ticket history\` lists them.
### func \(\*Repo\) TicketList
```go
func (r *Repo) TicketList() ([]Ticket, error)
```
TicketList returns every ticket, the most recently changed first.
### func \(\*Repo\) TicketSet
```go
func (r *Repo) TicketSet(ticket string, opts TicketOpts) (artifact string, err error)
```
TicketSet changes the ticket whose id is or starts with ticket, as \`fossil ticket set\` does, and returns the hash of the change artifact.
### func \(\*Repo\) Timeline
```go
func (r *Repo) Timeline(opts TimelineOpts) ([]TimelineEntry, error)
```
Timeline enumerates the repository's events newest\-first: every row of the event table by default, or just the rows matching opts.Type when set \(canonical \`fossil timeline\`'s \-t/\-\-type default: no filter unless one is given\). Unlike Ancestry, this does not start from or require any particular check\-in — it is a repository\-wide view, not a walk.
Ordering is \(mtime DESC, rid DESC\), a total order with rid as a true tie\-break at exact mtime equality — a deliberate improvement over canonical fossil, which orders by mtime DESC alone with no tie\-break, and paginates with a bare\-timestamp web cursor carrying a one\-second slop, so rows at a page boundary there can repeat or be skipped. Do not "fix" this back to canonical: it is intentional. To page through the full result set, pass the last entry's Cursor back as the next call's TimelineOpts.After — never construct a cursor from a TimelineEntry's Time by hand; see Cursor's doc comment for why that round trip is lossy.
### func \(\*Repo\) TimelineSince
```go
func (r *Repo) TimelineSince(hash string, kinds ...EventKind) ([]TimelineEntry, error)
```
TimelineSince returns the timeline entries whose artifacts arrived in this repository after the artifact with full hash hash: stored by a later receipt \(a later push, pull or local write\), or later within the same one. kinds, when given, keeps only entries of those kinds. The entries are in Timeline's order, newest first; an empty result is an empty slice, not nil. A hash the repository does not hold is an error.
Arrival, not timestamp, is what "after" means, because the question this answers is what a push brought in: an artifact made on a machine whose clock runs behind the given artifact's still arrived after it. Arrival order is this repository's own, so two clones can answer differently.
### func \(\*Repo\) UUIDFromRID
```go
func (r *Repo) UUIDFromRID(rid int64) (string, error)
```
UUIDFromRID returns the UUID \(manifest hash\) of the artifact identified by rid. The UUID is the stable, content\-addressed identifier for the artifact; the rid is a repository\-local integer that may differ across clones.
Returns ErrArtifactNotFound \(wrapped\) if no artifact with the given rid exists in the repository. The wrapped error message includes the offending rid. Other errors surface as\-is.
### func \(\*Repo\) UVDelete
```go
func (r *Repo) UVDelete(name string, mtime time.Time) error
```
UVDelete marks an unversioned file as deleted \(tombstone\).
### func \(\*Repo\) UVList
```go
func (r *Repo) UVList() ([]UVEntry, error)
```
UVList returns all unversioned file entries.
### func \(\*Repo\) UVRead
```go
func (r *Repo) UVRead(name string) ([]byte, int64, string, error)
```
UVRead reads an unversioned file from the repository. Returns the content, mtime \(unix seconds\), and content hash.
### func \(\*Repo\) UVWrite
```go
func (r *Repo) UVWrite(name string, content []byte, mtime time.Time) error
```
UVWrite writes an unversioned file to the repository.
### func \(\*Repo\) Undo
```go
func (r *Repo) Undo(dir string) error
```
Undo reverts the last commit or merge. Requires a checkout database; not yet wired.
### func \(\*Repo\) Verify
```go
func (r *Repo) Verify() error
```
Verify checks repository integrity \(blob checksums, delta chains\).
### func \(\*Repo\) VerifySeed
```go
func (r *Repo) VerifySeed(ctx context.Context, required RequiredContentOpts) (*SeedVerification, error)
```
VerifySeed verifies a structural store's seed by deepening \(draft\-fossil\-horizon\-00 §5.9\): it fetches every manifest below the horizon from Peer, so the store holds the real tag artifacts and primary\-parent chains, discards the seed, and compares what the store now derives at each entry with what the seed asserted. The price is that history, and the store stays a content\-horizon store at the same C, which holds no seed; a failed transfer puts the structural cone and seed back.
### func \(\*Repo\) WithTx
```go
func (r *Repo) WithTx(fn func(tx *db.Tx) error) error
```
WithTx executes fn within a database transaction.
### func \(\*Repo\) XferHandler
```go
func (r *Repo) XferHandler() http.HandlerFunc
```
XferHandler returns an http.HandlerFunc that decodes Fossil xfer requests, dispatches to HandleSync, and encodes the response. Use this to compose a custom mux alongside operational endpoints \(e.g., /healthz\). It serves with default HandleOpts, so no HandleOpts.MaxStoredBytes budget applies.
## type RequiredContentOpts
RequiredContentOpts authorizes read\-only recovery from an explicit peer. Neither the peer nor credentials are saved. A nil Peer keeps the operation local. Sync accepts authentication, project/server identity, Pull and Private; push, unversioned, extension\-table and link\-sharing options are refused. Existing context\-free operations never discover or contact a peer.
```go
type RequiredContentOpts struct {
Peer Transport
Sync SyncOpts
}
```
## type RevertOpts
RevertOpts configures reverting file changes.
```go
type RevertOpts struct {
Files []string // empty = revert all
}
```
## type RoundStats
RoundStats describes the outcome of a single sync round.
```go
type RoundStats struct {
FilesSent, FilesRecvd, BytesSent, BytesRecvd, Gimmes, IGots int
}
```
## type ScanEnd
ScanEnd describes the completion of a working\-tree scan.
```go
type ScanEnd struct {
FilesScanned int
}
```
## type SeedMismatch
SeedMismatch is one tag on which a seed and the derivation disagree at a check\-in. Seeded and Derived spell the tag as a T card does, sign then value \("\*red", "\+"\), and are "" where it is absent.
```go
type SeedMismatch struct {
Checkin, Tag, Seeded, Derived string
}
```
## type SeedVerification
SeedVerification reports VerifySeed: how many of the seed's T cards were checked, and each tag at an entry of the held set where the seed and the derivation over real artifacts disagree.
```go
type SeedVerification struct {
Checked int
Mismatches []SeedMismatch
}
```
## type SessionEnd
SessionEnd describes the completion of a sync session.
```go
type SessionEnd struct {
Rounds, FilesSent, FilesRecvd int
}
```
## type SessionStart
SessionStart describes the beginning of a sync session.
```go
type SessionStart struct {
ProjectCode string
Push, Pull, UV bool
}
```
## type StashEntry
StashEntry describes a saved stash.
```go
type StashEntry struct {
ID int64
Comment string
Time string
}
```
## type StatusEntry
StatusEntry describes a changed file in the working tree.
```go
type StatusEntry struct {
Name string
Change string
}
```
## type SyncObserver
SyncObserver receives lifecycle callbacks during sync operations. Use NopSyncObserver\(\) for a silent no\-op, or StdoutSyncObserver\(\) for stderr logging.
```go
type SyncObserver interface {
Started(info SessionStart)
RoundStarted(round int)
RoundCompleted(round int, stats RoundStats)
Completed(info SessionEnd)
Error(err error)
HandleStarted(info HandleStart)
HandleCompleted(info HandleEnd)
TableSyncStarted(info TableSyncStart)
TableSyncCompleted(info TableSyncEnd)
}
```
### func NopSyncObserver
```go
func NopSyncObserver() SyncObserver
```
NopSyncObserver returns a SyncObserver that silently discards all events.
### func StdoutSyncObserver
```go
func StdoutSyncObserver() SyncObserver
```
StdoutSyncObserver returns a SyncObserver that logs events to stderr.
## type SyncOpts
SyncOpts configures a sync operation.
```go
type SyncOpts struct {
Push bool
Pull bool
UV bool
ProjectCode string
ServerCode string
User string
Password string
PeerID string // identifies this leaf agent instance
MaxSend int
XTableSync bool // enable extension table sync (peer_registry, etc.)
Private bool // enable private artifact sync
// ShareLinks is fossil's --share-links mode (§9.5): a pulling session
// asks the server for the URLs of other copies of the repository and
// keeps each valid one as config link:URL. Off by default.
ShareLinks bool
Observer SyncObserver
Buggify BuggifyChecker
}
```
## type SyncResult
SyncResult describes the outcome of a sync session.
```go
type SyncResult struct {
Rounds int
FilesSent int
FilesRecvd int
UVFilesSent int
UVFilesRecvd int
UVGimmesSent int
BytesSent int64
BytesRecvd int64
Errors []string
// Elided counts the artifacts left absent under the store cone, and
// ConeServed the replies whose peer acknowledged applying it (pragma
// cone-served, draft-fossil-sparse-cone-00 §6.3); the rest of a sparse
// store's rounds were served densely and filtered locally.
Elided, ConeServed int
// HorizonServed counts the replies whose peer served the store's
// horizon (pragma horizon-served, draft-fossil-horizon-00 §6.2.5); the
// other rounds were classified locally. StructuralServed counts those
// served at the structural level, and HorizonFallback reports a
// structural store that fell back to the content horizon for good (§6.3).
HorizonServed int
StructuralServed int
HorizonFallback bool
// HorizonHistoryURL is the interface outside the sync protocol at which
// the origin says history below a horizon can be queried (pragma
// horizon-served URL, draft-fossil-horizon-00 §6.2.5), the last a reply
// named; "" when none did. An origin names it with
// SettingHorizonHistoryURL.
HorizonHistoryURL string
}
```
## type TableSyncEnd
TableSyncEnd describes the completion of a config table sync.
```go
type TableSyncEnd struct {
Table string
RowsSent, RowsRecvd int
}
```
## type TableSyncStart
TableSyncStart describes the beginning of a config table sync.
```go
type TableSyncStart struct {
Table string
}
```
## type TagOpts
TagOpts configures a tag operation.
```go
type TagOpts struct {
Name string
TargetID int64
Value string
User string
Time time.Time
}
```
## type TagSpec
TagSpec describes a tag to attach to an artifact.
```go
type TagSpec struct {
Name string
Value string
}
```
## type Ticket
Ticket is one ticket as the ticket table holds it after every change.
```go
type Ticket struct {
UUID string `json:"uuid"`
Status string `json:"status"`
// Changed is the time of the ticket's latest change.
Changed time.Time `json:"changed"`
// Fields holds every ticket-table field that has a value.
Fields map[string]string `json:"fields"`
}
```
## type TicketChange
TicketChange is one change artifact of a ticket.
```go
type TicketChange struct {
// Artifact is the change artifact's hash.
Artifact string `json:"artifact"`
User string `json:"user"`
Time time.Time `json:"time"`
// Comment is the remark the change added, or "".
Comment string `json:"comment"`
// Fields holds the change's fields exactly as written, the remark's
// included: "+name" for an append.
Fields map[string]string `json:"fields"`
}
```
## type TicketOpts
TicketOpts is one change to a ticket.
```go
type TicketOpts struct {
// Fields maps a ticket field (a column of the repository's ticket
// schema: title, status, type, resolution, comment, ...) to its new
// value. A "+name" key appends the value to the field instead. A field
// with an empty value is left out, as `fossil ticket` leaves it out.
Fields map[string]string
// User is the change's author, its U card.
User string
// Comment, when not empty, is a remark added with the change, stored as
// the stock ticket forms store one: the icomment field, with login and
// username set to User and mimetype text/plain.
Comment string
// Time is the change's D card; zero means now.
Time time.Time
}
```
## type TimelineEntry
TimelineEntry represents a single event as returned by Timeline. It extends LogEntry with Cursor, the pagination token for this row: pass it back as the next call's TimelineOpts.After to resume enumeration immediately after this entry. Cursor is always valid \(Cursor.Valid\(\) == true\) on every TimelineEntry Timeline produces.
TimelineEntry values should come from Timeline, not be hand\-assembled. A TimelineEntry built by embedding a LogEntry from Ancestry carries a zero\-value Cursor, which Timeline treats as "start from newest" rather than as an error.
```go
type TimelineEntry struct {
LogEntry
Cursor Cursor
}
```
## type TimelineOpts
TimelineOpts configures a Timeline query: a repository\-wide enumeration of the event table.
```go
type TimelineOpts struct {
// Type restricts the enumeration to a single event kind. The zero
// value means "all kinds" — the canonical `fossil timeline` default.
Type EventKind
// After, when valid, resumes enumeration immediately following this
// cursor — the next page after whatever TimelineEntry produced it. The
// zero Cursor means "start from the newest event". See Repo.Timeline's
// doc comment for the pagination contract this forms, and Cursor's doc
// comment for why it must come from a TimelineEntry rather than be
// built by hand.
After Cursor
// Limit caps the number of entries returned. Zero or negative means
// unbounded.
Limit int
}
```
## type Transport
Transport delivers sync payloads between peers. Implementations handle the network layer \(HTTP, NATS, etc.\). Payloads are opaque zlib\-compressed xfer card streams.
```go
type Transport interface {
RoundTrip(ctx context.Context, payload []byte) ([]byte, error)
}
```
### func NewHTTPTransport
```go
func NewHTTPTransport(url string, opts ...HTTPOption) Transport
```
NewHTTPTransport creates a Transport that speaks Fossil's HTTP /xfer protocol.
## type TransportFunc
TransportFunc adapts a plain function to the Transport interface. This is the Transport equivalent of http.HandlerFunc.
```go
type TransportFunc func(ctx context.Context, payload []byte) ([]byte, error)
```
### func \(TransportFunc\) RoundTrip
```go
func (f TransportFunc) RoundTrip(ctx context.Context, payload []byte) ([]byte, error)
```
RoundTrip calls the function.
## type UVEntry
UVEntry describes an unversioned file.
```go
type UVEntry struct {
Name string
Size int64
Mtime time.Time
Hash string
}
```
## type UndoResult
UndoResult reports the working files Undo or Redo changed, each list in the order they were changed, fossil's order. Fossil's undo prints them as UNDO \(or REDO\), NEW and DELETE lines; a caller shows them as it likes.
```go
type UndoResult struct {
FilesRestored []string // existing files given back their saved content
FilesCreated []string // files written that were not on disk
FilesRemoved []string // files deleted from the working tree
}
```
## type UpdateChange
UpdateChange classifies how a file changed.
```go
type UpdateChange string
```
```go
const (
ChangeAdded UpdateChange = "added"
ChangeModified UpdateChange = "modified"
ChangeDeleted UpdateChange = "deleted"
)
```
## type UpdateOpts
UpdateOpts configures updating to a new version with merge.
```go
type UpdateOpts struct {
// TargetRID is the check-in to update to. 0 picks it as fossil's update
// does with no version named: the newest open leaf descended from the
// checkout on its branch. Update fails when several such leaves remain.
TargetRID int64
}
```
## type UpdateResult
UpdateResult reports what Update actually did. Paths, not counts: a caller needs to know which files to show a user, not merely how many changed. All three slices are sorted \(sort.Strings\) before Update returns, so the order is deterministic — safe for golden\-file tests or reflect.DeepEqual — rather than depending on Go's randomized map iteration order.
Outcome mapping \(Update's error return is the other half of this\):
```
clean - err == nil, Conflicted empty
conflicted - err == nil, Conflicted non-empty
failed - err != nil, UpdateResult is the zero value
```
A non\-empty Conflicted means three\-way merge could not resolve those files cleanly and wrote "\<\<\<\<\<\<\<"\-style conflict markers directly into them on disk. That is NOT an error — Update still returns a nil error, because the update itself succeeded and the working tree is usable; it just isn't clean. A caller that only checks the returned error gets today's pre\-conflict\-detection behavior. A caller that needs to warn a user about marker text in their files must check Conflicted. Every path in Conflicted also appears in FilesWritten: the marker text is what was written there.
Unmerged lists files left for the caller to reconcile that hold no markers: a file edited locally that the target deletes \(kept on disk, no longer tracked\), a file added both locally and by the target \(the local file kept, so it shows as edited\), a binary file both sides changed \(the target's content written, the working copy saved as NAME\-original\), and a file edited locally that the target renamed without changing it \(left under its old name, untracked; the new name shows as missing, as in fossil\). Like Conflicted, it is not an error.
```go
type UpdateResult struct {
FilesWritten []string // paths written (added, updated, or merged, clean or not); includes every Conflicted path
FilesRemoved []string // paths deleted from the working tree
Conflicted []string // paths that now contain conflict markers — not an error; also present in FilesWritten
Unmerged []string // paths left to reconcile without markers — not an error
// OutOfCone counts the changes a sparse checkout's update applied to files
// outside its cone: recorded for the next checkin, never written to disk.
OutOfCone int
}
```
## type User
User describes a Fossil user.
```go
type User struct {
Login string
Caps string
}
```
## type UserOpts
UserOpts configures a user creation or update.
```go
type UserOpts struct {
Login string
Password string
Caps string
}
```
## type WorkingVersion
WorkingVersion is the answer of a checkout\-inclusive request: a check\-in, or the checkout's working files.
```go
type WorkingVersion struct {
// RID is the check-in the name resolves to. When Working is set it is
// the checkout's version, the check-in the working files started from.
RID int64
// Working reports that the name was "ckout": the request asks for the
// files on disk, edits included, rather than for any stored check-in.
Working bool
}
```
Generated by [gomarkdoc]()
---
# observer/otel
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/sdk/observer/otel/api/
# otel
```go
import "github.com/danmestas/go-libfossil/observer/otel"
```
Package otel provides OpenTelemetry implementations of the libfossil SyncObserver and CheckoutObserver interfaces. Import this package only when you want OTel instrumentation — it pulls in the OTel SDK dependency.
## Index
- [type CheckoutObserver](<#CheckoutObserver>)
- [func NewCheckoutObserver\(\) \*CheckoutObserver](<#NewCheckoutObserver>)
- [func \(o \*CheckoutObserver\) CommitCompleted\(info libfossil.CommitEnd\)](<#CheckoutObserver.CommitCompleted>)
- [func \(o \*CheckoutObserver\) CommitStarted\(info libfossil.CommitStart\)](<#CheckoutObserver.CommitStarted>)
- [func \(o \*CheckoutObserver\) Error\(err error\)](<#CheckoutObserver.Error>)
- [func \(o \*CheckoutObserver\) ExtractCompleted\(info libfossil.ExtractEnd\)](<#CheckoutObserver.ExtractCompleted>)
- [func \(o \*CheckoutObserver\) ExtractFileCompleted\(name string, change libfossil.UpdateChange\)](<#CheckoutObserver.ExtractFileCompleted>)
- [func \(o \*CheckoutObserver\) ExtractStarted\(info libfossil.ExtractStart\)](<#CheckoutObserver.ExtractStarted>)
- [func \(o \*CheckoutObserver\) ScanCompleted\(info libfossil.ScanEnd\)](<#CheckoutObserver.ScanCompleted>)
- [func \(o \*CheckoutObserver\) ScanStarted\(dir string\)](<#CheckoutObserver.ScanStarted>)
- [func \(o \*CheckoutObserver\) String\(\) string](<#CheckoutObserver.String>)
- [type SyncObserver](<#SyncObserver>)
- [func NewSyncObserver\(\) \*SyncObserver](<#NewSyncObserver>)
- [func \(o \*SyncObserver\) Completed\(info libfossil.SessionEnd\)](<#SyncObserver.Completed>)
- [func \(o \*SyncObserver\) Error\(err error\)](<#SyncObserver.Error>)
- [func \(o \*SyncObserver\) HandleCompleted\(info libfossil.HandleEnd\)](<#SyncObserver.HandleCompleted>)
- [func \(o \*SyncObserver\) HandleStarted\(info libfossil.HandleStart\)](<#SyncObserver.HandleStarted>)
- [func \(o \*SyncObserver\) RoundCompleted\(round int, stats libfossil.RoundStats\)](<#SyncObserver.RoundCompleted>)
- [func \(o \*SyncObserver\) RoundStarted\(round int\)](<#SyncObserver.RoundStarted>)
- [func \(o \*SyncObserver\) Started\(info libfossil.SessionStart\)](<#SyncObserver.Started>)
- [func \(o \*SyncObserver\) String\(\) string](<#SyncObserver.String>)
- [func \(o \*SyncObserver\) TableSyncCompleted\(info libfossil.TableSyncEnd\)](<#SyncObserver.TableSyncCompleted>)
- [func \(o \*SyncObserver\) TableSyncStarted\(info libfossil.TableSyncStart\)](<#SyncObserver.TableSyncStarted>)
## type CheckoutObserver
CheckoutObserver implements libfossil.CheckoutObserver using OpenTelemetry spans and metrics.
```go
type CheckoutObserver struct {
// contains filtered or unexported fields
}
```
### func NewCheckoutObserver
```go
func NewCheckoutObserver() *CheckoutObserver
```
NewCheckoutObserver returns a CheckoutObserver that records spans and metrics using the OTel global tracer and meter providers.
### func \(\*CheckoutObserver\) CommitCompleted
```go
func (o *CheckoutObserver) CommitCompleted(info libfossil.CommitEnd)
```
### func \(\*CheckoutObserver\) CommitStarted
```go
func (o *CheckoutObserver) CommitStarted(info libfossil.CommitStart)
```
### func \(\*CheckoutObserver\) Error
```go
func (o *CheckoutObserver) Error(err error)
```
### func \(\*CheckoutObserver\) ExtractCompleted
```go
func (o *CheckoutObserver) ExtractCompleted(info libfossil.ExtractEnd)
```
### func \(\*CheckoutObserver\) ExtractFileCompleted
```go
func (o *CheckoutObserver) ExtractFileCompleted(name string, change libfossil.UpdateChange)
```
### func \(\*CheckoutObserver\) ExtractStarted
```go
func (o *CheckoutObserver) ExtractStarted(info libfossil.ExtractStart)
```
### func \(\*CheckoutObserver\) ScanCompleted
```go
func (o *CheckoutObserver) ScanCompleted(info libfossil.ScanEnd)
```
### func \(\*CheckoutObserver\) ScanStarted
```go
func (o *CheckoutObserver) ScanStarted(dir string)
```
### func \(\*CheckoutObserver\) String
```go
func (o *CheckoutObserver) String() string
```
## type SyncObserver
SyncObserver implements libfossil.SyncObserver using OpenTelemetry spans and metrics.
```go
type SyncObserver struct {
// contains filtered or unexported fields
}
```
### func NewSyncObserver
```go
func NewSyncObserver() *SyncObserver
```
NewSyncObserver returns a SyncObserver that records spans and metrics using the OTel global tracer and meter providers.
### func \(\*SyncObserver\) Completed
```go
func (o *SyncObserver) Completed(info libfossil.SessionEnd)
```
### func \(\*SyncObserver\) Error
```go
func (o *SyncObserver) Error(err error)
```
### func \(\*SyncObserver\) HandleCompleted
```go
func (o *SyncObserver) HandleCompleted(info libfossil.HandleEnd)
```
### func \(\*SyncObserver\) HandleStarted
```go
func (o *SyncObserver) HandleStarted(info libfossil.HandleStart)
```
### func \(\*SyncObserver\) RoundCompleted
```go
func (o *SyncObserver) RoundCompleted(round int, stats libfossil.RoundStats)
```
### func \(\*SyncObserver\) RoundStarted
```go
func (o *SyncObserver) RoundStarted(round int)
```
### func \(\*SyncObserver\) Started
```go
func (o *SyncObserver) Started(info libfossil.SessionStart)
```
### func \(\*SyncObserver\) String
```go
func (o *SyncObserver) String() string
```
String returns a human\-readable description.
### func \(\*SyncObserver\) TableSyncCompleted
```go
func (o *SyncObserver) TableSyncCompleted(info libfossil.TableSyncEnd)
```
### func \(\*SyncObserver\) TableSyncStarted
```go
func (o *SyncObserver) TableSyncStarted(info libfossil.TableSyncStart)
```
Generated by [gomarkdoc]()
---
# sshverify
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/sdk/sshverify/api/
# sshverify
```go
import "github.com/danmestas/go-libfossil/sshverify"
```
Package sshverify links in\-process verification of SSH signatures into a program that uses go\-libfossil. Import it for its side effect:
```
import _ "github.com/danmestas/go-libfossil/sshverify"
```
Without it, the library links no SSH cryptography, and a signature it would have to check against an SSH public key, such as a structural horizon seed under an SSH libfossil.SettingHorizonSeedKey, is refused with libfossil.ErrSSHVerifierUnavailable. Signing needs no import: it runs ssh\-keygen. PGP signatures are verified by gpg either way.
## Index
Generated by [gomarkdoc]()
---
# CLI
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/
The `libfossil` command, one page per command group. Every page is the output of the binary's own `--help`.
```text
Usage: libfossil [flags]
Fossil-compatible repository tool (pure Go)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo new [] [flags]
Create a new repository
repo clone [ []] [flags]
Clone a remote repository
repo ci --message=STRING [ ...] [flags]
Checkin file changes
repo co [] [flags]
Checkout a version
repo ls [] [flags]
List files in a version
repo timeline [flags]
Show repository history
repo snapshot --message=STRING --path=STRING [flags]
Commit a file's bytes onto trunk, no checkout needed
repo log --path=STRING [flags]
Show the check-ins that changed a path
repo cat [flags]
Output artifact content
repo info
Repository statistics
repo hash ... [flags]
Hash files (SHA1 or SHA3)
repo delta create [flags]
Create a delta between two files
repo delta apply [flags]
Apply a delta to a source file
repo config ls
List all config entries
repo config get
Get a config value
repo config set
Set a config value
repo query
Execute SQL against repository
repo verify [flags]
Verify repository integrity
repo resolve
Resolve symbolic name to UUID
repo extract [ ...] [flags]
Extract files from a version
repo wiki ls
List wiki pages
repo wiki export [flags]
Export a wiki page
repo tag ls []
List tags on an artifact
repo tag add [] [flags]
Add a tag to an artifact
repo open [] [flags]
Open a checkout in a directory
repo status [flags]
Show working directory changes
repo add ... [flags]
Stage files for addition
repo rm ... [flags]
Stage files for removal
repo rename [flags]
Rename a tracked file
repo revert [ ...] [flags]
Undo staging changes
repo diff [ [ []]] [flags]
Show changes vs a version
repo merge [flags]
Merge a divergent version
repo conflicts ls [flags]
List all conflicts
repo conflicts show [flags]
Show all versions of a conflicted file
repo conflicts pick [flags]
Resolve by picking one version
repo conflicts merge [flags]
Resolve by re-merging with a different strategy
repo conflicts extract [flags]
Extract all versions to disk for manual editing
repo mark-resolved [flags]
Mark a conflict as resolved
repo undo [flags]
Undo last operation
repo redo [flags]
Redo undone operation
repo stash save [flags]
Stash working changes
repo stash pop [flags]
Apply top stash and drop it
repo stash apply [] [flags]
Apply stash without dropping
repo stash ls
List stash entries
repo stash drop
Remove stash entry
repo stash clear
Remove all stash entries
repo bisect good [] [flags]
Mark version as good
repo bisect bad [] [flags]
Mark version as bad
repo bisect next [flags]
Check out midpoint version
repo bisect skip [] [flags]
Skip current version
repo bisect reset [flags]
Clear bisect state
repo bisect ls [flags]
Show bisect path
repo bisect status [flags]
Show bisect state
repo annotate [flags]
Annotate file lines with version history
repo blame [flags]
Alias for annotate
repo branch ls [flags]
List branches
repo branch new [flags]
Create new branch
repo branch close [flags]
Close a branch
repo uv ls
List unversioned files
repo uv put
Add or update an unversioned file
repo uv get [flags]
Retrieve an unversioned file
repo uv delete
Delete an unversioned file (creates tombstone)
repo schema add --columns=STRING [flags]
Register a new synced table
repo schema list
List registered synced tables
repo schema show
Show table schema details
repo schema remove
Remove a synced table registration
repo user add --cap=STRING
Create a new user
repo user list
List all users
repo user update --cap=STRING
Update user capabilities
repo user rm
Delete a user
repo user passwd
Reset user password
repo invite --cap=STRING [flags]
Generate invite token for a user
repo serve [flags]
Serve repository over HTTP for sync/clone
repo submanifests enable [flags]
Write new check-ins as sub-manifest trees
repo submanifests retune --k=INT
Re-pin the partition parameter for later commits
repo submanifests disable
Write new check-ins as flat manifests again
repo submanifests status
Show the sub-manifest settings and whether trees exist
repo cone show [flags]
List the cone's members ("dense" when there is none)
repo cone add ... [flags]
Widen the cone, or with --not carve paths out of it
repo cone remove ... [flags]
Remove members of either polarity
repo cone set ... [flags]
Replace the cone
repo cone dense [flags]
Drop the cone and materialize every file
repo cone test ... [flags]
Classify paths against the cone and name the deciding member
repo cone store [ ...] [flags]
Show or replace the repository's store cone, fetching what widening covers
repo cone deepen [flags]
Move the store's horizon down to reach a check-in, fetching the history it
keeps
repo cone compact
Drop held blobs the store cone elides (irreversible locally)
repo cone cost
Report what each horizon rule would hold at a check-in, from this repository
version [flags]
Print version information
Run "libfossil --help" for more information on a command.
```
## Command groups
- [`libfossil repo new`](/docs/reference/cli/repo-new/): Create a new repository
- [`libfossil repo clone`](/docs/reference/cli/repo-clone/): Clone a remote repository
- [`libfossil repo ci`](/docs/reference/cli/repo-ci/): Checkin file changes
- [`libfossil repo co`](/docs/reference/cli/repo-co/): Checkout a version
- [`libfossil repo ls`](/docs/reference/cli/repo-ls/): List files in a version
- [`libfossil repo timeline`](/docs/reference/cli/repo-timeline/): Show repository history
- [`libfossil repo snapshot`](/docs/reference/cli/repo-snapshot/): Commit a file's bytes onto trunk, no checkout needed
- [`libfossil repo log`](/docs/reference/cli/repo-log/): Show the check-ins that changed a path
- [`libfossil repo cat`](/docs/reference/cli/repo-cat/): Output artifact content
- [`libfossil repo info`](/docs/reference/cli/repo-info/): Repository statistics
- [`libfossil repo hash`](/docs/reference/cli/repo-hash/): Hash files (SHA1 or SHA3)
- [`libfossil repo delta`](/docs/reference/cli/repo-delta/): Delta create/apply operations
- [`libfossil repo config`](/docs/reference/cli/repo-config/): Repository configuration
- [`libfossil repo query`](/docs/reference/cli/repo-query/): Execute SQL against repository
- [`libfossil repo verify`](/docs/reference/cli/repo-verify/): Verify repository integrity
- [`libfossil repo resolve`](/docs/reference/cli/repo-resolve/): Resolve symbolic name to UUID
- [`libfossil repo extract`](/docs/reference/cli/repo-extract/): Extract files from a version
- [`libfossil repo wiki`](/docs/reference/cli/repo-wiki/): Wiki page operations
- [`libfossil repo tag`](/docs/reference/cli/repo-tag/): Tag operations
- [`libfossil repo open`](/docs/reference/cli/repo-open/): Open a checkout in a directory
- [`libfossil repo status`](/docs/reference/cli/repo-status/): Show working directory changes
- [`libfossil repo add`](/docs/reference/cli/repo-add/): Stage files for addition
- [`libfossil repo rm`](/docs/reference/cli/repo-rm/): Stage files for removal
- [`libfossil repo rename`](/docs/reference/cli/repo-rename/): Rename a tracked file
- [`libfossil repo revert`](/docs/reference/cli/repo-revert/): Undo staging changes
- [`libfossil repo diff`](/docs/reference/cli/repo-diff/): Show changes vs a version
- [`libfossil repo merge`](/docs/reference/cli/repo-merge/): Merge a divergent version
- [`libfossil repo conflicts`](/docs/reference/cli/repo-conflicts/): List/manage unresolved conflicts
- [`libfossil repo mark-resolved`](/docs/reference/cli/repo-mark-resolved/): Mark a conflict as resolved
- [`libfossil repo undo`](/docs/reference/cli/repo-undo/): Undo last operation
- [`libfossil repo redo`](/docs/reference/cli/repo-redo/): Redo undone operation
- [`libfossil repo stash`](/docs/reference/cli/repo-stash/): Stash working changes
- [`libfossil repo bisect`](/docs/reference/cli/repo-bisect/): Binary search for bugs
- [`libfossil repo annotate`](/docs/reference/cli/repo-annotate/): Annotate file lines with version history
- [`libfossil repo blame`](/docs/reference/cli/repo-blame/): Alias for annotate
- [`libfossil repo branch`](/docs/reference/cli/repo-branch/): Branch operations
- [`libfossil repo uv`](/docs/reference/cli/repo-uv/): Unversioned file operations
- [`libfossil repo schema`](/docs/reference/cli/repo-schema/): Synced table schema operations
- [`libfossil repo user`](/docs/reference/cli/repo-user/): User management
- [`libfossil repo invite`](/docs/reference/cli/repo-invite/): Generate invite token for a user
- [`libfossil repo serve`](/docs/reference/cli/repo-serve/): Serve repository over HTTP for sync/clone
- [`libfossil repo submanifests`](/docs/reference/cli/repo-submanifests/): Sub-manifest tree settings
- [`libfossil repo cone`](/docs/reference/cli/repo-cone/): Show or edit a checkout's sparse cone
- [`libfossil version`](/docs/reference/cli/version/): Print version information
---
# repo new
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-new/
```text
Usage: libfossil repo new [] [flags]
Create a new repository
Arguments:
[] Path for new repository file (default: -R)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--user="" Default user name
```
---
# repo clone
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-clone/
```text
Usage: libfossil repo clone [ []] [flags]
Clone a remote repository
Arguments:
[] Remote Fossil server URL
[] Local path for new repository file
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-u, --user=STRING Username for clone auth
-p, --pass=STRING Password for clone auth
--invite=STRING Invite token (from fossil invite)
--timeout=10m Abort the clone if it runs longer than this;
0 disables the deadline
--horizon=STRING Clone a horizon store: elide blobs below this
check-in, given as tip, a branch or tag,
or its full 40 or 64 digit hash; the origin
resolves a name, or the clone does over what
an origin that cannot sends it; what each rule
would hold needs a local repository ('repo cone
cost') or a peer that exposes it out of band,
as the origin's horizon-served URL may
--depth=INT With --horizon, step the check-in back this
many check-ins along primary parents, stopping
at a root
--leaves With --horizon, also hold every open leaf's
ancestry back to the check-in, for merging
across live branches offline; costly: on a
repository with old unmerged branches that is
nearly the whole history
--structural With --horizon, also elide the manifests below
the check-in, deriving their branch and tag
state from the origin's seed
--cone=CONE,... Clone a sparse store: fetch only this path's
subtree (repeatable)
--cone-not=CONE-NOT,... Carve this path out of a --cone (repeatable)
--seed-key=STRING The origin's public key, an SSH public key line
or an OpenPGP fingerprint: with --structural, a
horizon seed not signed by it is discarded and
the clone falls back to the content horizon;
kept as the horizon-seed-key setting
```
---
# repo ci
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-ci/
```text
Usage: libfossil repo ci --message=STRING [ ...] [flags]
Checkin file changes
Arguments:
[ ...] Files to commit (default: all changes)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--remote=STRING Required-content peer URL (default: saved
last-sync-url)
--sync-user=STRING Required-content authentication user
--sync-pass=STRING Required-content password (never saved)
--sync-private Allow recovery of private required artifacts
--timeout=10m Required-content deadline; 0 disables it
--no-sync Suppress optional autosync, not required-content
recovery
-m, --message=STRING Checkin comment
--user=STRING Checkin user (default: OS username)
--branch=STRING Branch name for this checkin
--allow-conflict Commit files that still hold merge conflict markers
--allow-empty Commit even when nothing has changed
--nosign Do not sign this check-in, even with the clearsign
setting on
-d, --dir="." Checkout directory
```
---
# repo co
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-co/
```text
Usage: libfossil repo co [] [flags]
Checkout a version
Arguments:
[] Version to checkout (default: tip)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Output directory (default: current dir)
--force Overwrite existing files
```
---
# repo ls
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-ls/
```text
Usage: libfossil repo ls [] [flags]
List files in a version
Arguments:
[] Version to list (default: tip)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-l, --long Show sizes and hashes
```
---
# repo timeline
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-timeline/
```text
Usage: libfossil repo timeline [flags]
Show repository history
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-n, --limit=20 Number of entries
```
---
# repo snapshot
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-snapshot/
```text
Usage: libfossil repo snapshot --message=STRING --path=STRING [flags]
Commit a file's bytes onto trunk, no checkout needed
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-m, --message=STRING Checkin comment
--path=STRING File name in the repository (relative,
slash-separated); the bytes come from this path on
disk unless --stdin
--stdin Read the bytes from stdin instead of from --path on
disk
--user=STRING Checkin user (default: OS username)
--json Print {"uuid":...,"changed":...} instead of the bare
uuid
```
---
# repo log
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-log/
```text
Usage: libfossil repo log --path=STRING [flags]
Show the check-ins that changed a path
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--path=STRING File name in the repository
--json Print a JSON array instead of text
-n, --limit=INT Number of entries (default: all)
```
---
# repo cat
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-cat/
```text
Usage: libfossil repo cat [flags]
Output artifact content
Arguments:
Artifact UUID or prefix; with --at, the file name
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-r, --at=STRING Version to read the file from (tip, trunk, a branch,
a uuid or prefix)
--raw Output raw blob (no delta expansion)
```
---
# repo info
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-info/
```text
Usage: libfossil repo info
Repository statistics
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
---
# repo hash
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-hash/
```text
Usage: libfossil repo hash ... [flags]
Hash files (SHA1 or SHA3)
Arguments:
... Files to hash
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--sha3 Use SHA3-256 instead of SHA1
```
---
# repo delta
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-delta/
```text
Usage: libfossil repo delta
Delta create/apply operations
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo delta create [flags]
Create a delta between two files
repo delta apply [flags]
Apply a delta to a source file
```
## repo delta create
```text
Usage: libfossil repo delta create [flags]
Create a delta between two files
Arguments:
Source (original) file
Target (new) file
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-o, --output=STRING Output file (default: stdout)
```
## repo delta apply
```text
Usage: libfossil repo delta apply [flags]
Apply a delta to a source file
Arguments:
Source (original) file
Delta file
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-o, --output=STRING Output file (default: stdout)
```
---
# repo config
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-config/
```text
Usage: libfossil repo config
Repository configuration
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo config ls
List all config entries
repo config get
Get a config value
repo config set
Set a config value
```
## repo config ls
```text
Usage: libfossil repo config ls
List all config entries
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
## repo config get
```text
Usage: libfossil repo config get
Get a config value
Arguments:
Config key to get
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
## repo config set
```text
Usage: libfossil repo config set
Set a config value
Arguments:
Config key
Config value
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
---
# repo query
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-query/
```text
Usage: libfossil repo query
Execute SQL against repository
Arguments:
SQL query to execute
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
---
# repo verify
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-verify/
```text
Usage: libfossil repo verify [flags]
Verify repository integrity
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--remote=STRING Required-content peer URL (default: saved
last-sync-url)
--sync-user=STRING Required-content authentication user
--sync-pass=STRING Required-content password (never saved)
--sync-private Allow recovery of private required artifacts
--timeout=10m Required-content deadline; 0 disables it
--seed Verify the structural horizon's seed by fetching the
history below it (keeps the content horizon)
```
---
# repo resolve
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-resolve/
```text
Usage: libfossil repo resolve
Resolve symbolic name to UUID
Arguments:
Symbolic name, UUID, or prefix to resolve (e.g. trunk, tip, UUID
prefix)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
---
# repo extract
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-extract/
```text
Usage: libfossil repo extract [ ...] [flags]
Extract files from a version
Arguments:
[ ...] Files to extract (default: all)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--version=STRING Version to extract from (default: tip)
-d, --dir="." Output directory
```
---
# repo wiki
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-wiki/
```text
Usage: libfossil repo wiki
Wiki page operations
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo wiki ls
List wiki pages
repo wiki export [flags]
Export a wiki page
```
## repo wiki ls
```text
Usage: libfossil repo wiki ls
List wiki pages
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
## repo wiki export
```text
Usage: libfossil repo wiki export [flags]
Export a wiki page
Arguments:
Wiki page name or artifact UUID
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-o, --output=STRING Output file (default: stdout)
```
---
# repo tag
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-tag/
```text
Usage: libfossil repo tag
Tag operations
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo tag ls []
List tags on an artifact
repo tag add [] [flags]
Add a tag to an artifact
```
## repo tag ls
```text
Usage: libfossil repo tag ls []
List tags on an artifact
Arguments:
[] Version to list tags for (default: tip)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
## repo tag add
```text
Usage: libfossil repo tag add [] [flags]
Add a tag to an artifact
Arguments:
Tag name
[] Tag value (optional)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--version=STRING Version to tag (default: tip)
```
---
# repo open
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-open/
```text
Usage: libfossil repo open [] [flags]
Open a checkout in a directory
Arguments:
[] Checkout directory (default: current dir)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--cone=CONE,... Make a sparse checkout: materialize and track
only this path's subtree (repeatable)
--cone-not=CONE-NOT,... Exclude this path's subtree from an enclosing
--cone (repeatable)
```
---
# repo status
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-status/
```text
Usage: libfossil repo status [flags]
Show working directory changes
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory to scan
```
---
# repo add
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-add/
```text
Usage: libfossil repo add ... [flags]
Stage files for addition
Arguments:
... Files to stage for addition
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
---
# repo rm
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-rm/
```text
Usage: libfossil repo rm ... [flags]
Stage files for removal
Arguments:
... Files to stage for removal
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
---
# repo rename
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-rename/
```text
Usage: libfossil repo rename [flags]
Rename a tracked file
Arguments:
Current file name
New file name
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
---
# repo revert
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-revert/
```text
Usage: libfossil repo revert [ ...] [flags]
Undo staging changes
Arguments:
[ ...] Files to revert (default: all)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
---
# repo diff
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-diff/
```text
Usage: libfossil repo diff [ [ []]] [flags]
Show changes vs a version
Arguments:
[] Version to diff against (default: tip)
[] Second version: diff Version against it instead of the working
directory
[] With two versions, only this file (default: every changed file)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--json With two versions, print a JSON array of {name, unified}
instead of text
-d, --dir="." Working directory to compare
-U, --unified=3 Lines of context
```
---
# repo merge
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-merge/
```text
Usage: libfossil repo merge [flags]
Merge a divergent version
Arguments:
Version to merge into current checkout
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--remote=STRING Required-content peer URL (default: saved
last-sync-url)
--sync-user=STRING Required-content authentication user
--sync-pass=STRING Required-content password (never saved)
--sync-private Allow recovery of private required artifacts
--timeout=10m Required-content deadline; 0 disables it
--strategy=STRING Override merge strategy for all files
--dry-run Show what would be merged without writing
-d, --dir="." Checkout directory
```
---
# repo conflicts
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-conflicts/
```text
Usage: libfossil repo conflicts [flags]
List/manage unresolved conflicts
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
Commands:
repo conflicts ls [flags]
List all conflicts
repo conflicts show [flags]
Show all versions of a conflicted file
repo conflicts pick [flags]
Resolve by picking one version
repo conflicts merge [flags]
Resolve by re-merging with a different strategy
repo conflicts extract [flags]
Extract all versions to disk for manual editing
```
## repo conflicts ls
```text
Usage: libfossil repo conflicts ls [flags]
List all conflicts
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
## repo conflicts show
```text
Usage: libfossil repo conflicts show [flags]
Show all versions of a conflicted file
Arguments:
Conflicted file to show
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
## repo conflicts pick
```text
Usage: libfossil repo conflicts pick [flags]
Resolve by picking one version
Arguments:
Conflicted file to resolve
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
--local Keep local version
--remote Keep remote version
--base Revert to base version
-d, --dir="." Checkout directory
```
## repo conflicts merge
```text
Usage: libfossil repo conflicts merge [flags]
Resolve by re-merging with a different strategy
Arguments:
Conflicted file to re-merge
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
--strategy="three-way" Strategy to use
-d, --dir="." Checkout directory
```
## repo conflicts extract
```text
Usage: libfossil repo conflicts extract [flags]
Extract all versions to disk for manual editing
Arguments:
Conflicted file to extract
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
-d, --dir="." Output directory
```
---
# repo mark-resolved
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-mark-resolved/
```text
Usage: libfossil repo mark-resolved [flags]
Mark a conflict as resolved
Arguments:
File to mark as resolved
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
---
# repo undo
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-undo/
```text
Usage: libfossil repo undo [flags]
Undo last operation
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
---
# repo redo
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-redo/
```text
Usage: libfossil repo redo [flags]
Redo undone operation
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
---
# repo stash
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-stash/
```text
Usage: libfossil repo stash
Stash working changes
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo stash save [flags]
Stash working changes
repo stash pop [flags]
Apply top stash and drop it
repo stash apply [] [flags]
Apply stash without dropping
repo stash ls
List stash entries
repo stash drop
Remove stash entry
repo stash clear
Remove all stash entries
```
## repo stash save
```text
Usage: libfossil repo stash save [flags]
Stash working changes
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-m, --message="" Stash message
-d, --dir="." Checkout directory
```
## repo stash pop
```text
Usage: libfossil repo stash pop [flags]
Apply top stash and drop it
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
## repo stash apply
```text
Usage: libfossil repo stash apply [] [flags]
Apply stash without dropping
Arguments:
[] Stash ID to apply (default: latest)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
## repo stash ls
```text
Usage: libfossil repo stash ls
List stash entries
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
## repo stash drop
```text
Usage: libfossil repo stash drop
Remove stash entry
Arguments:
Stash ID to drop
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
## repo stash clear
```text
Usage: libfossil repo stash clear
Remove all stash entries
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
---
# repo bisect
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-bisect/
```text
Usage: libfossil repo bisect
Binary search for bugs
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo bisect good [] [flags]
Mark version as good
repo bisect bad [] [flags]
Mark version as bad
repo bisect next [flags]
Check out midpoint version
repo bisect skip [] [flags]
Skip current version
repo bisect reset [flags]
Clear bisect state
repo bisect ls [flags]
Show bisect path
repo bisect status [flags]
Show bisect state
```
## repo bisect good
```text
Usage: libfossil repo bisect good [] [flags]
Mark version as good
Arguments:
[] Version to mark (default: current checkout)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
## repo bisect bad
```text
Usage: libfossil repo bisect bad [] [flags]
Mark version as bad
Arguments:
[] Version to mark (default: current checkout)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
## repo bisect next
```text
Usage: libfossil repo bisect next [flags]
Check out midpoint version
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
## repo bisect skip
```text
Usage: libfossil repo bisect skip [] [flags]
Skip current version
Arguments:
[] Version to skip (default: current checkout)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
## repo bisect reset
```text
Usage: libfossil repo bisect reset [flags]
Clear bisect state
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
## repo bisect ls
```text
Usage: libfossil repo bisect ls [flags]
Show bisect path
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
## repo bisect status
```text
Usage: libfossil repo bisect status [flags]
Show bisect state
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-d, --dir="." Checkout directory
```
---
# repo annotate
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-annotate/
```text
Usage: libfossil repo annotate [flags]
Annotate file lines with version history
Arguments:
File to annotate
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--version=STRING Starting version (default: tip)
```
---
# repo blame
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-blame/
```text
Usage: libfossil repo blame [flags]
Alias for annotate
Arguments:
File to annotate
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--version=STRING Starting version (default: tip)
```
---
# repo branch
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-branch/
```text
Usage: libfossil repo branch
Branch operations
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo branch ls [flags]
List branches
repo branch new [flags]
Create new branch
repo branch close [flags]
Close a branch
```
## repo branch ls
```text
Usage: libfossil repo branch ls [flags]
List branches
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--closed Show only closed branches
--all Show all branches (open and closed)
```
## repo branch new
```text
Usage: libfossil repo branch new [flags]
Create new branch
Arguments:
Branch name
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--from=STRING Parent version (default: tip)
-m, --message=STRING Checkin comment
--user=STRING Checkin user (default: OS username)
--nosign Do not sign the branch's check-in, even with the
clearsign setting on
```
## repo branch close
```text
Usage: libfossil repo branch close [flags]
Close a branch
Arguments:
Branch name to close
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--user=STRING User for control artifacts (default: OS username)
```
---
# repo uv
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-uv/
```text
Usage: libfossil repo uv
Unversioned file operations
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo uv ls
List unversioned files
repo uv put
Add or update an unversioned file
repo uv get [flags]
Retrieve an unversioned file
repo uv delete
Delete an unversioned file (creates tombstone)
```
## repo uv ls
```text
Usage: libfossil repo uv ls
List unversioned files
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
## repo uv put
```text
Usage: libfossil repo uv put
Add or update an unversioned file
Arguments:
Name of the unversioned file
Local file to upload
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
## repo uv get
```text
Usage: libfossil repo uv get [flags]
Retrieve an unversioned file
Arguments:
Name of the unversioned file
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
-o, --output=STRING Output file (default: stdout)
```
## repo uv delete
```text
Usage: libfossil repo uv delete
Delete an unversioned file (creates tombstone)
Arguments:
Name of the unversioned file to delete
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
---
# repo schema
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-schema/
```text
Usage: libfossil repo schema
Synced table schema operations
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo schema add --columns=STRING [flags]
Register a new synced table
repo schema list
List registered synced tables
repo schema show
Show table schema details
repo schema remove
Remove a synced table registration
```
## repo schema add
```text
Usage: libfossil repo schema add --columns=STRING [flags]
Register a new synced table
Arguments:
Table name (without x_ prefix)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
--columns=STRING Column defs: name:type[:pk],... (e.g.
peer_id:text:pk,addr:text)
--conflict="mtime-wins" Conflict strategy
```
## repo schema list
```text
Usage: libfossil repo schema list
List registered synced tables
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
## repo schema show
```text
Usage: libfossil repo schema show
Show table schema details
Arguments:
Table name (without x_ prefix)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
## repo schema remove
```text
Usage: libfossil repo schema remove
Remove a synced table registration
Arguments:
Table name (without x_ prefix)
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
```
---
# repo user
URL: https://libfossil-docs.daniel-mestas.workers.dev/docs/reference/cli/repo-user/
```text
Usage: libfossil repo user
User management
Flags:
-h, --help Show context-sensitive help.
-R, --repo=STRING Path to repository file
-v, --verbose Verbose output
Commands:
repo user add --cap=STRING
Create a new user
repo user list
List all users
repo user update --cap=STRING
Update user capabilities
repo user rm
Delete a user
repo user passwd
Reset user password
```
## repo user add
```text
Usage: libfossil repo user add --cap=STRING