Quick Start
Ten minutes from nothing to a value that explains itself. Use the xkv command first to see the idea, then the Go package to build it into a program.
1. Get the Command
Download the archive for your system from the download page, unpack it, and put xkv on your PATH. Check it runs:
xkv version
2. Write a Chain
Three services each write one value. Each names what it acted on with --cause.
xkv put limits.xkv chargeback:cb_991 "card dispute filed" \
--reason "bank notice" --ref BANK-7731 --actor billing-worker
xkv put limits.xkv risk:42:score 0.82 \
--reason "new chargeback" --cause chargeback:cb_991 --actor risk-model
xkv put limits.xkv account:42:limit 50 \
--reason "risk policy v3" --cause risk:42:score --actor limits-service
--cause KEY points at the key’s current revision. --cause KEY@REV points at one revision by number. Leave out --reason and the write is refused: that’s strict mode, on by default.
3. Ask Why
xkv why limits.xkv account:42:limit
account:42:limit = 50 r3 · 2026-10-19 09:20:41
│ by limits-service · risk policy v3
└─ risk:42:score = 0.82 r2 · 2026-10-19 09:20:41
│ by risk-model · new chargeback
└─ chargeback:cb_991 = card dispute filed r1 · 2026-10-19 09:20:41
by billing-worker · bank notice · ref BANK-7731
Add --json for the same tree as JSON. xkv log limits.xkv risk:42:score lists a key’s revisions, newest first, and xkv get limits.xkv account:42:limit --at 2 shows a value as it stood at revision 2.
4. Use It From Go
go get github.com/explainable-kv/xkv
package main
import (
"context"
"log"
"os"
"github.com/explainable-kv/xkv"
)
func main() {
db, err := xkv.Open("limits.xkv", nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Name the actor once; every write made with ctx records it.
ctx := xkv.WithActor(context.Background(), "limits-service")
_, err = db.Put(ctx, "account:42:limit", []byte("40"),
xkv.Reason("second chargeback this quarter"),
xkv.CauseLatest("risk:42:score"))
if err != nil {
log.Fatal(err)
}
tree, err := db.Why(ctx, "account:42:limit", 0)
if err != nil {
log.Fatal(err)
}
tree.WriteText(os.Stdout)
}
Close the command-line session’s file first, or open it read-only from Go with &xkv.Options{ReadOnly: true}: one handle writes to a file at a time.
5. Write Several Keys at Once
Update runs a transaction. Every write inside commits, or none does, and the options given to Update apply to each write.
err := db.Update(ctx, func(tx *xkv.Tx) error {
if _, err := tx.Put("price:basic", []byte("9")); err != nil {
return err
}
_, err := tx.Put("price:pro", []byte("33"))
return err
}, xkv.Reason("Q4 price list"), xkv.Ref("PRICE-12"))
6. From Other Languages
Run the store as a small HTTP service on localhost:
xkv serve limits.xkv
curl -X PUT localhost:7070/v1/keys/account:42:limit \
-d '{"value": "45", "reason": "manual review", "causes": [{"key": "risk:42:score"}]}'
curl localhost:7070/v1/why/account:42:limit
Where Next
- The reference lists every call, command and endpoint.
- The case studies show the chains XKV is for: a credit limit, a forgotten timeout, an AI agent’s change and a price list.
- The live demo runs the store in your browser.