How-to
Each section is one task. Every command finds the writer on
127.0.0.1:8761 with nothing set; PACKSET_URL points it elsewhere and
packset ensure prints the address it started.
Name the workspace once#
A workspace is the git remote of the directory you stand in, or default.
Set PACKSET_WORKSPACE to pin one for a shell:
$ export PACKSET_WORKSPACE=seat
$ packset status
Every verb also takes the workspace as its last argument, and the writers
take --workspace WS.
Pin a set#
A set is a named slice of a workspace with its own cards. Pinning one scopes reads and deduplication to it.
$ packset pin review
{"instructions":"","set":"review","workspace":"demo"}
$ packset pin
{"instructions":"","set":"review","workspace":"demo"}
The binary prints a JSON object with workspace, set, and instructions.
Ask what was live on a date#
A closed claim keeps its window. packset atoms --as-of TS lists every
claim live at TS; packset search --as-of TS QUERY ranks those same
claims. Live-now search drops the closed window.
$ packset atoms --as-of 2026-08-01T00:00:00Z seat
$ packset search --as-of 2026-08-01T00:00:00Z --workspace seat fusion
GET /v1/search with as_of is the same window. A stamp the pack will
not parse is refused.
Retire a claim#
The pack tombstones; it does not erase. The record stays readable and stops being recalled.
$ curl -s $PACKSET_URL/v1/atoms/delete -d '{"workspace":"seat","id":"3f9c...","why":"deed-review-2026-09"}'
why names the deed that showed the claim wrong. The seat’s ljos forget
does this with the same field.
Follow the citations#
A claim that names a deed accession (deed-... or sha256:...) is joined
to the deed store by that id and nothing else.
$ packset accessions seat | deedar evidence - # the bytes are intact
$ packset accessions seat | deedar current - # still the tip
$ packset citers deed-file-note seat # the claims standing on one deed
Choose the panel#
The writer fuses its ballots with a named panel. Set the three slots in the writer’s environment, not in a client:
Variable |
Values |
Default |
|---|---|---|
|
|
|
|
|
|
|
|
|
fsrs scales a score by the claim’s retrievability from its review clock;
on is a fourteen-day half-life on age. Explanation
says what each was measured against.
See what a task involves#
Islands are the natural clusters of the link graph; a cue activates one. On a seat’s own pack of thirty claims:
$ packset islands
9 The org to rst exporter fails with stringp nil on a src block with no language. Every begin_sr
7 Trust rows live in the pack as trust atoms and reach the settle through ljos consensus. One le
1 ljos pins packset-client to one git commit in its lock file. A client change needs cargo updat
$ packset island settle a vote with learned trust
1.000 seed 1bd8ee9e370dc5d1b6eb86aca804b15a Trust rows live in the pack as trust atoms and reach
0.888 seed e7574b841812abff43b3c83e6a704d5e A trust graph with rows into only some voters makes
0.810 seed a34d89130f030ea528b5d70a3d0a22c9 The tracker verb takes --trust JSON rows laid over the con
0.732 seed 5a779f5aee06da68e95edc90c1bf3cf6 Trust rows over static trust config for the seat's c
The first column is activation relative to the strongest; seed marks a
claim search found itself, the rest were reached along links. Add --fire
when the seat goes on to use the island: the strongest eight fire together
and their links gain weight, so the next such cue walks a heavier path.
$ packset island --fire settle a vote with learned trust
$ packset fire 1bd8ee9e... e7574b84... # or name the claims yourself
Turn on the dense scorer#
The dense ballot needs the packset-embed binary beside packsetd or on
PATH, and a model it can load. The default build links Open Neural
Network Exchange (ONNX) Runtime that you compiled, or a distro
libonnxruntime of 1.24 or newer that
pkg-config can see. The runtime this ort publishes as a prebuilt is
1.28.0, and that is the version to compile. The prebuilt download from
cdn.pyke.io is --features download-binaries, off unless named.
ORT_SKIP_DOWNLOAD=1, ORT_OFFLINE=1 or CARGO_NET_OFFLINE=1 refuse that
download even with the feature on.
$ crates/packset-embed/build-onnxruntime.sh "$HOME/onnxruntime"
$ ORT_LIB_PATH=$HOME/onnxruntime/lib ORT_PREFER_DYNAMIC_LINK=1 cargo build --release -p packset-embed
$ export LD_LIBRARY_PATH=$HOME/onnxruntime/lib
pkg-config is tried first. A libonnxruntime it reports at 1.24 or
newer is the one that is linked. ORT_LIB_PATH is the directory used when
that probe does not succeed: libonnxruntime.so for a shared build, or
libonnxruntime.a for a static one. ORT_LIB_LOCATION is the same
variable. ORT_PREFER_DYNAMIC_LINK=1 selects the shared library; leave it
unset to link the static archive. Point PKG_CONFIG_PATH at a prefix that
provides libonnxruntime.pc when that is the library you want.
A build with none of these, and neither feature, stops within the first
minute with this recipe, instead of at the linker once everything else
has compiled. PACKSET_EMBED_NO_ORT_CHECK=1 skips that check.
A runtime load does not link at build time:
$ cargo build --release -p packset-embed --features load-dynamic
$ ORT_DYLIB_PATH=$HOME/onnxruntime/lib/libonnxruntime.so packset-embed
The default model is bge-small-en-v1.5. A hub checkout of it, with
onnx/model.onnx, tokenizer.json, config.json,
special_tokens_map.json and tokenizer_config.json, is the model. The
first run does not fetch when those files are present.
$ PACKSET_EMBED_MODEL_PATH=$HOME/models/bge-small-en-v1.5 packset-embed
Without that variable the same files under
$PACKSET_EMBED_CACHE/user/bge-small/ are used. The cache is
$XDG_CACHE_HOME/packset/embed unless PACKSET_EMBED_CACHE names another
directory. A directory that is missing a file is an error. An absent
directory is the Hugging Face fetch for a model the runtime knows.
HF_HUB_OFFLINE=1 refuses that fetch. e5-large-v2 has no fetch; it is
files only, under user/e5-large-v2/.
The pack stays on the lexical ballots without the model files or the
binary. /v1/status then reads embedder.available false. A sitting
still opens: doctor reads that status, and recall, island and search run
on the lexical ballots.
$ packset stop && packset ensure
$ packset status seat | jq .embedder
The published binary remains cargo binstall packset-embed for a seat
that does not compile the runtime.
Run the second stage on one question#
The cross-encoder rerank is measured and off by default. Ask for it per
query with packset search --rerank, GET /v1/search?rerank=1, or set
PACKSET_RERANK=1 on the writer. The stage is a forward pass over the
top 20. An absent or broken packset-embed leaves the first-stage
order and reports rerank: absent. A lone hit reports rerank:
settled: one candidate cannot be reordered, so the stage answers
without a model call.
$ packset search --rerank --workspace seat fusion
$ curl -s "$PACKSET_URL/v1/search?workspace=seat&q=fusion&rerank=1" | jq '.rerank'
Export for a handover, import from one#
$ packset export --into bag/data/atoms seat | deedar export --into bag/data/deeds -
Import is one POST per line of the .jsonl; the seat’s ljos receive
with --import does that after checking the bag. Trust rows travel the same way.
Serve many agents#
One writer serves every client on the seat. The worker pool is the balance:
PACKSET_WORKERS sets it (default 4, at most 8), and one logical write runs
at a time so two identical claims arriving together are stored once. A burst
waits in a queue of PACKSET_QUEUE connections (default 128) rather than
becoming threads. Past that a request is answered 503 with Retry-After: 1.
Searches and writes that a worker has read but cannot yet run are bounded
the same way. packset-client asks a busy writer again after 50, 150 and
450 ms. Health, status, the workspace list and the listing without vectors
that a status line reads keep one worker, so a search does not hold the last
one. /health still answers. Reads share one parsed snapshot per write. The
encoder runs before the write lock and is warmed at start.
$ PACKSET_WORKERS=8 packsetd --port 8761
$ PACKSET_URL=http://127.0.0.1:8761 cargo run --release -p packset-daemon --example hammer -- 32 100
The example prints requests per second and latency percentiles for remember and search under 32 clients; the README carries the measured table. A second host is not a second writer: it is a client over the network, or its own pack.
Run the retrieval benchmark#
$ curl -sSLO https://raw.githubusercontent.com/snap-research/locomo/main/data/locomo10.json
$ cargo run --release -p packset-daemon --example locomo -- locomo10.json
PACKSET_LOCOMO_CONVERSATIONS=3 scores three conversations for a quick
run; the numbers are then comparable to each other and not to a full run.