The Quipu stack — shared knowledge with rules and history
The Quipu stack is built around a governed, bitemporal knowledge store: shared facts, decisions and work history your agents can query. Quipu can check writes against your rules, records where each fact came from, and answers what was true when. Bitemporal means keeping both when a fact was recorded and, when supplied, when it was true.
Caboodle installs and configures this stack, then proves the selected tools work. Quipu holds the knowledge. Camayoc prepares knowledge and vocabulary for it. Bobbin searches code and can include knowledge from Quipu; Yupana shows code impact and can read policies from Quipu. Desire Path turns agent mistakes into feature requests and also works on its own. Seeds tracks work items in the Quipu knowledge base. You can use the tools separately or connect them around the same store.
Write refusal is configurable, not automatic in every installation: enable Quipu’s write checks for the rules you want enforced. See Quipu’s configuration reference for the settings and their defaults.
Three ways in
1. Try it in your browser
Open Quipu Explore — no installation. The real store runs as WebAssembly in your browser. Use the SPARQL box, edit a fact, and export a knowledge pack.
2. Start in Claude Code
Add the Camayoc plugin, then run its bootstrap:
/plugin marketplace add scbrown/camayoc
/plugin install camayoc@camayoc
/camayoc:bootstrap
Camayoc brings its own Quipu, loads the ontology, and proves the write gate with probes that must be refused. See the Camayoc setup guide.
3. Install the whole kit
Install Caboodle, then start its guided interview:
caboodle init --guided
Review the generated plan before applying it. The getting-started guide walks through installation, verification, and recovery; profiles let you choose how much of the stack to install.
Around the stack
Explore Shuttle, Creel, Shanty, Desire Path for turning hallucinations into feature requests, and Yupana for policy enforcement and code impact. Seeds tracks work items in the Quipu knowledge base. Agents can run under Shantytown or Creel.
Work-tracking dependencies include the beads_rust fork, based on Dicklesworthstone/beads_rust. See the stack for the component and dependency list.
Getting started
Install Caboodle with the release installer, then try the smallest profile: Quipu and Camayoc. Start in an empty directory.
curl -fsSLO https://raw.githubusercontent.com/scbrown/caboodle/main/examples/caboodle-intent.toml
caboodle plan --profile kg --intent caboodle-intent.toml
caboodle install
The plan command prints plan: caboodle-plan.toml. Read that file before
installing: it selects Quipu and Camayoc, and retains the example intent.
Installation prints progress on stderr and these lines on stdout:
quipu: applied
camayoc: applied
stack configuration: applied
quipu: verified
camayoc: verified
Each verified line means an isolated functional round trip passed, including a control proving the marker was absent before the write. The example intent is a starting contract, not a seeded production graph; see question verification.
Guided interview
Use caboodle init --guided instead of the first two commands to describe
your own use interactively. Run caboodle doctor before installing to see
platform or prerequisite blockers.
| prompt | what to type |
|---|---|
profile | retrieval for a first install. See profiles. |
what should this installation help you do? | One plain sentence. It is recorded in the plan. |
how many themed crew members | 0 unless you run a named team of agents. |
how many ontology questions | Press enter on a first install: caboodle uses a built-in self-test question that checks the store answers a query, and asks nothing else. Or type a number to write your own; each question is a check the finished graph must pass. |
| the question’s five fields | Only if you typed a number. A question in words, its answer shape, the seed fact that answers it, a SPARQL SELECT or ASK query, and a word the answer must contain. |
Recovery
caboodle doctorfirst. It names the blocker and the fix.<tool> install step ... no checksummed CABOODLE release for <platform>: build that tool with thecargo installcommand doctor prints, then reruncaboodle install. Caboodle adopts an installed tool that passes its checks.<tool> functional verification: the tool installed but its round trip failed. The error includes the tool’s own output. Fix it and reruncaboodle install; tools that already passed are not redone.- macOS with Bobbin before v0.18.0 (the pin CABOODLE carried before this release was v0.16.2): bobbin panics with
Failed to load ONNX Runtime dylib: bobbin looks for its bundled runtime next to the~/.cargo/bin/bobbinsymlink rather than next to the real binary. For that release, runexport ORT_DYLIB_PATH="$HOME/.local/share/caboodle/bobbin/v0.16.2/lib/libonnxruntime.dylib"(add it to your shell profile) and reruncaboodle install. - A new build still behaves like the old one: another copy earlier on PATH
wins. Doctor lists every copy in the order your shell finds them, and
caboodle verifyfails withSHADOWEDwhen the copy PATH runs reports a different version from the one caboodle installed. Remove the stale copy or move~/.cargo/binearlier on PATH. no plan at caboodle-plan.toml: you are in a different directory from the one where you rancaboodle init --guided.
Do not delete .caboodle/ to recover. It holds your interview answers and the
last proven state.
Reproduce the README proof
On Linux x86_64, from a checkout, run scripts/test-readme.sh. It extracts the
README’s shell blocks, runs the checksummed release installer and the three
first-success commands under env -i with a fresh home, then diffs stdout
against the README’s own expected-output block. It does not reuse host binaries
for the stack. The Documentation workflow runs this proof on every push and PR.
For local link checks, install mdBook 0.5.4 and lychee 0.24.2, then run
just docs-check. pre-commit install enables the same check before commits.
The check builds the book under its Pages base path and checks files, anchors
and public URLs. The source-build route additionally requires Rust; agent
registration additionally requires the selected client and profile.
Installing and removing Tambo
Tambo is the renamed Caboodle installer. For the transition release, both
tambo and caboodle are available. Existing caboodle-plan.toml and
.caboodle state paths remain compatible; no install-state move is required.
The canonical archive is tambo-vVERSION-TARGET.tar.gz; a compatibility archive
named caboodle-vVERSION-TARGET.tar.gz carries the same two commands for legacy
self-update clients. Both have filename-bound SHA-256 checksum files.
The planned managed Linux and macOS cutover uses apt and Homebrew to update
both commands as a package after the first official Tambo release. The Homebrew
formula rename will migrate caboodle to tambo; the Linux transitional
caboodle package will depend on tambo. The checksummed release
installer remains the standalone fallback. A standalone self-update of a
dual-command installation verifies and updates both commands together, restoring
both prior files and state if either installed read-back fails.
Caboodle publishes a release only when a vVERSION tag exactly matches the
version in Cargo.toml. Each supported archive has a sibling .sha256 file;
the installer verifies that checksum before replacing the binary.
Caboodle’s built-in HTTPS asset downloads make at most three attempts. Server errors (HTTP 5xx) and transient resolution, connection, timeout, partial-transfer, send/receive or HTTP/2 stream errors are retried after one and then two seconds. Each attempt has a 10-second connection timeout and a 120-second transfer limit. Retry logs identify the URL, attempt, curl status, HTTP status and delay; recovery logs name the successful attempt. Failed attempts use disposable files. HTTP 4xx (including missing assets), certificate and local file errors fail immediately. Checksum verification happens after downloading and is never retried or bypassed. This policy covers downloads made by the Caboodle binary, not the bootstrap shell installer above it or external package managers.
Homebrew member updates
For a Mac plan with stack formulas, tambo update refreshes Homebrew metadata
once before checking members. An unpinned formula follows the tap’s current
stable version, even when the installed version is already ahead of Tambo’s
embedded minimum release. A newer installed version is never downgraded.
brew pin scbrown/tap/bobbin holds that formula; the updater reports the hold
and never unpins it. Use brew unpin scbrown/tap/bobbin when you intend to resume
stable updates.
A failed metadata refresh, unreadable channel, failed upgrade, or version
read-back mismatch is an error. After an upgrade, the formula receipt must reach
stable and agree with the executable, and functional verification must pass.
The release fallback still requires the embedded reviewed minimum when no stack
formula is available. check-updates continues to report drift from those
embedded release pins; it does not refresh package metadata.
Release install
Run the checksummed release installer in one line:
curl -fsSL https://raw.githubusercontent.com/scbrown/caboodle/main/scripts/install.sh | bash
Pin a release by setting the version on the receiving shell:
curl -fsSL https://raw.githubusercontent.com/scbrown/caboodle/main/scripts/install.sh | CABOODLE_VERSION=vX.Y.Z bash
The script still verifies the release archive’s SHA256 checksum before replacing
the binary. Read the installer source
when inspecting the bootstrap. After installation, caboodle --version reads
back the installed release.
The compatibility repository URL remains the bootstrap default through the
rename. The installer prefers a tambo- archive with both commands. Only an
HTTP 404 selects the historical caboodle- archive, which may contain just
caboodle; pinned releases such as v0.2.23 therefore remain installable.
Checksum, server and transport failures stop installation without trying an
alternative archive.
Supported release targets are Linux x86_64, macOS x86_64, and macOS arm64.
The default destination is ${CARGO_HOME:-$HOME/.cargo}/bin/caboodle; override
it with CABOODLE_INSTALL_DIR when that directory is managed elsewhere.
caboodle --version prints the release version and the commit it was built
from, for example caboodle 0.2.3 (1a2b3c4d5e6f). Two builds of one release
can then be told apart. The installer warns when a different caboodle comes
earlier on PATH than the one it just installed, and caboodle doctor warns when
PATH runs a different build from the one you invoked. Remove the stale copy or
reorder PATH.
Source install
A Rust toolchain can install the reviewed revision directly:
cargo install --git https://github.com/scbrown/caboodle \
--rev <reviewed-commit-sha> --locked
caboodle --version
Preflight
caboodle doctor reports what would stop an install on this host and changes
nothing. With a plan in the current directory it checks that plan; without one
it checks every tool in the everything profile. It exits nonzero when any line
is a FAIL:
- the install directory (
$CARGO_HOME/bin, default~/.cargo/bin) is not on PATH, so installed tools could not be read back; - a command an install or verification step shells out to is missing (
curl,tar,git,bash,python3,sha256sum,gofor Desire Path,cargofor the lancedb Quipu flavor); - a selected tool is not installed and has no reviewed release for this platform (for example, Yupana on Linux arm64), with the source build to run instead.
It warns, without failing, when a tool differs from the reviewed version, when
several copies of a tool are on PATH (the first one runs), and when a Quipu
server is already live at QUIPU_SERVER. Camayoc verification uses its own
temporary server and never loads the ontology into that existing server.
First proof and resume
The interview writes a plan and changes nothing else. Read the complete file before applying it:
caboodle init --guided
less caboodle-plan.toml
caboodle install
caboodle verify-questions
If input or an install is interrupted, rerun the same command. Interview
answers resume from .caboodle/interview.toml; install state resumes from
.caboodle/state.json. A green result means version read-back and functional
reader-path checks passed, not merely that an installer exited zero.
If a tool cannot install or pass version read-back, apply and install
continue attempting the remaining tools, save each success, and exit nonzero
with all tool failures. Any saved proof for a failed tool is cleared. Stack
configuration, model provisioning, crew setup, share imports and functional
verification wait until every selected tool applies successfully. Fix the
reported gaps, then rerun the same command; already-current tools are reused.
State-write and evidence-queue failures still stop immediately.
Two plan-level choices extend what the box installs, both documented with
their proofs in Profiles: --quipu-flavor lancedb builds the
reviewed Quipu revision with the lancedb feature compiled in (proven by the
server’s GET /version per-feature compile map), and
--embedding-model <spec.toml> provisions checksum-pinned embedding-model
artifacts (a mismatched download is deleted and fails the step; verify
re-hashes the artifacts on disk).
Registering MCP servers
Caboodle owns binary installation, version convergence, and functional proof. Shantytown owns harness registration: it projects the MCP/skill manifest from Quipu into the workspaces named by the rig’s crew cards. Installing binaries alone does not register servers.
After a green install, on a configured Shantytown rig:
caboodle project-settings --root /path/to/.shanty
# Optional: select one crew member or read identities directly from Quipu.
caboodle project-settings --root /path/to/.shanty --agent ada --registry quipu
This delegates to st --root /path/to/.shanty ops provision --json. It reads the
rig’s existing crew, even when the installation plan has no crew profile, and
requires the configured Quipu tooling manifest (SHANTY_TOOLING_MANIFEST).
Server names, transports, and secret references come from that manifest; Caboodle
does not maintain another server list. A rig whose manifest names yupana, bobbin,
forgejo, and homelab registers all four. A missing crew reports
“no crew on this rig yet - run st fleet init / st agent new first”. Missing
manifest, credentials, or failed read-back refuses registration. This needs a
Shantytown version exposing st ops provision; upgrade st if it rejects the command.
No agents are launched or restarted. Existing sessions need their next normal
start to load the new registration.
--policy-only retains the old plan-based policy-summary export to
caboodle-settings/ (--plan and --output customize it). Those summaries do
not register MCP servers. Normal registration does not read the install plan.
For a standalone harness, or until the adapter is available, register explicitly. For Claude Code, substitute your deployment’s reviewed HTTP MCP endpoints:
claude mcp add bobbin -- "$(command -v bobbin)" serve "$PWD"
claude mcp add yupana -- "$(command -v yupana)" serve
claude mcp add --transport http forgejo https://forgejo-mcp.example.com/mcp
claude mcp add --transport http homelab https://homelab-mcp.example.com/mcp
claude mcp list
Homelab MCP exposes the quipu_* tools; it is a separately operated proxy, not
installed by Caboodle. Use your deployment’s authentication procedure without
putting credentials in the plan. Bobbin’s knowledge tools are not a replacement
for Homelab’s Quipu tools. HTTP endpoints above are placeholders, not public
services. For Codex, use the rig adapter to emit its harness-specific settings.
Claude Code cloud environments
scripts/setup-environment.sh is the version-controlled copy of the setup
script for a Claude Code cloud environment. Paste its body into the Setup
script field (claude.ai/code → the cloud icon above the message box →
Add/edit environment) — the field takes a script, not a path, and it runs
before the repo is available, which is why the file exists to be pasted from
rather than executed.
It bootstraps quipu (checksummed prebuilt quipu and quipu-server release
binaries), caboodle itself (prebuilt release binary via scripts/install.sh),
the shared tooling the stack’s quality gates need (just, bd,
pre-commit, mdbook, mdbook-mermaid, cffi), and stages the stack
knowledge packs into ~/.caboodle/packs/, verified with
quipu pack --verify — a pack that fails verification is deleted rather
than trusted. It deliberately does not build the rest of the corpus:
that is caboodle’s own job, and a session that needs the full stack runs
caboodle install against a reviewed plan so every tool is proved rather
than assumed.
Set CABOODLE_ALLOW_SOURCE_FALLBACK=1 only when the Quipu release artifact is
unavailable and the environment has enough time and memory for a Rust build.
The normal setup path never compiles Quipu from source.
Owned Quipu installs provide qp as a relative link to quipu. Both command
names execute the same CLI; quipu remains supported. Apply repairs a missing
alias on a current pair without reinstalling or downgrading that pair. Existing
conflicting qp files in the destination or on PATH cause refusal. Verification
checks the link, executable identity and matching version output without creating
files. --skip-install requires the alias to be present already.
An externally owned Quipu must provide its own command alias; Caboodle verifies
it but does not create it. A paired release update retains the alias across a
CLI replacement or rollback because the link follows the canonical quipu path.
Hosts that only ever run update-self (such as a scheduled installer consumer)
never run apply, so update-self also heals the alias. After a verified swap it
runs the newly installed binary once to repair the owned directory
($CARGO_HOME/bin, default ~/.cargo/bin). That pass creates a missing qp, or
re-points a qp symlink with the wrong or a dangling target. A regular file or
directory named qp is foreign: it is reported and left in place. A failed
repair is reported but never fails the update. Because the old binary performs
the swap, the first release carrying this behaviour repairs on the update after
it.
Stack knowledge packs
packs/ carries quipu knowledge packs for working with the stack:
stack-map/pendant/— what each tool is, where it lives, and how the pieces relate.stack-operations/pendant/— how each repo builds, tests, and proves itself, and the git discipline that binds them.
A pendant is the artifact once called a qpack (named for the khipu’s pendant
cords, each a self-contained record hanging from the primary cord). The old
Each directory contains manifest.json, manifest.ttl, canonical export.nt
and shapes.ttl. SQLite databases are scratch build inputs, not repository
artifacts. The former .pendant.db and .qpack.db paths remain symlinks to
these directories, so directory-based imports can retain their old paths.
They are directory aliases, not binary SQLite packs. Use quipu import,
not quipu unpack or quipu pack --verify, for these new artifacts. Previously
published binary packs retain their original binary consumer commands.
Alias removal is separate from this conversion and is not scheduled before
the release after the announced 0.14 deprecation.
quipu import packs/stack-map/pendant --db scratch.db
quipu import packs/stack-operations/pendant --db scratch.db
Import verifies the bundle’s hashes and stages or quarantines its graph; it
does not promote it automatically. These shares intentionally contain no
domain shapes or stored queries. An empty receiver quarantines stack-map
because https://caboodle.dev/ns#StackComponent is outside its local vocabulary.
Configure reviewed receiver shapes for that class before expecting admission.
Hash integrity alone is not vocabulary, shape or producer certification.
The Turtle sources in packs/src/ remain the review surface.
scripts/build-stack-packs.sh rebuilds and verifies both bundles before
replacing output; a failed build or native verification preserves existing
bundles. Rebuild by hand when sources change: creation time and source identity
can change manifests without changing the semantic graph. Python 3 and Quipu
with text-share support are required. PACKS_OUTPUT_DIR selects an isolated
output root for experiments; python3 scripts/test-stack-pendants.py exercises
native integrity, quarantine, tamper refusal and failure preservation.
Shipping a pack from a repository
Each repository can publish its graph beside its release binaries as a Quipu
share. A .pendant.tar.gz release asset is a deterministic archive of the same
text bundle, not a SQLite database:
quipu share --graph https://example.org/knowledge/repository/example \
--db .bobbin/quipu/quipu.db --out repository-share
tar --sort=name --mtime=@0 --owner=0 --group=0 --numeric-owner \
-czf repository.pendant.tar.gz -C repository-share .
Publish the archive as an immutable release asset. A clone points Quipu at the asset; Quipu fetches bounded bytes, verifies the manifest and payload, and opens a fresh in-memory store without a local download step:
quipu import \
https://github.com/scbrown/quipu/releases/download/quipu-ai-v0.11.1/quipu-quipu-ai-v0.11.1-repository.qpack.tar.gz
Releases still publish the asset as *-repository.qpack.tar.gz (checked on
quipu-ai-v0.11.1). The pendant rename has not shipped yet; when it does, the
asset becomes *-repository.pendant.tar.gz.
For a modified graph, quipu share --since <parent-share> --out <delta-dir>
writes a parent-bound SPARQL 1.1 Update delta. Import rejects a wrong parent,
changed update bytes, unrestricted operations, or a mismatched result digest.
Bobbin’s .bbpack index remains a separate binary release artifact.
Uninstall
Remove only the Caboodle binary and optional local Caboodle state. Installed corpus tools are intentionally left in place because they may be shared with other workflows:
rm "${CARGO_HOME:-$HOME/.cargo}/bin/caboodle"
# Optional, from the directory where Caboodle was run:
rm -r .caboodle
Reviewed-pin drift
caboodle check-updates proposes an update only when the installed version is
older than the reviewed pin. A newer version is reported as ahead of the pin;
an unparseable or same-version identity mismatch is reported as version drift.
These reports remain nonzero because the installation differs from the review.
caboodle update refuses a reviewed-pin downgrade before replacing the tool.
There is no --allow-downgrade escape: a deliberate rollback requires a separately
reviewed installation, rather than a normal stack update.
Replacing a cargo-installed Bobbin
If apply refuses to replace an existing Bobbin executable, it preserves your
installation. To switch a regular-file Cargo install to the reviewed release,
first stop any automatic updates and choose a backup that does not already exist.
The following works on macOS and Linux and uses $CARGO_HOME/bin when configured.
It creates a hard-link backup beside the executable, retaining its bytes and
permissions, before removing the original directory entry. It refuses symlinks,
directories, and an existing backup (including a dangling symlink). It does not
change Cargo’s install metadata or uninstall the crate.
python3 - <<'PYTHON'
import os
from pathlib import Path
import stat
binary = Path(os.environ.get("CARGO_HOME", str(Path.home() / ".cargo"))) / "bin/bobbin"
backup = binary.with_name("bobbin.before-tambo")
if not stat.S_ISREG(binary.lstat().st_mode):
raise SystemExit(f"Expected a regular file; preserved {binary}")
os.link(binary, backup, follow_symlinks=False) # fails if backup already exists
binary.unlink()
print(f"Saved {backup}; rerun apply with your existing plan and state")
PYTHON
caboodle apply --plan caboodle-plan.toml --state .caboodle/state.json
caboodle verify --plan caboodle-plan.toml --state .caboodle/state.json
Keep bobbin.before-tambo until verification succeeds. If either command fails,
stop and inspect the failure. To restore, remove only the managed Bobbin symlink
created by this attempt, if present, then run the following. It refuses an
occupied destination and leaves the backup in place:
python3 - <<'PYTHON'
import os
from pathlib import Path
import stat
binary = Path(os.environ.get("CARGO_HOME", str(Path.home() / ".cargo"))) / "bin/bobbin"
backup = binary.with_name("bobbin.before-tambo")
if not stat.S_ISREG(backup.lstat().st_mode):
raise SystemExit(f"Expected a regular backup; preserved {backup}")
os.link(backup, binary, follow_symlinks=False) # never overwrites a destination
print(f"Restored {binary}; backup retained at {backup}")
PYTHON
This procedure deliberately replaces the selected Bobbin version with the plan’s
reviewed release. Compare bobbin --version with the planned version first; keep
a newer Cargo install if you do not intend to switch. Do not use cargo uninstall
after switching: Cargo still records its previous install and may remove the new
entry. For a symlink or a package-manager-owned executable, use its owner’s repair
procedure instead of the regular-file steps above.
Opt-in published binary updates
After the initial install and plan review, track published stable releases for a selected binary without waiting for this Caboodle build’s reviewed pins to change:
caboodle update-release --tool bobbin --check
caboodle update-release --tool bobbin
caboodle update-release --tool yupana
caboodle update-release --tool desire-path
This path currently supports Linux x86_64. The tool must already be installed on
PATH and selected in the retained plan. --plan and --state select the existing
files; the command never starts a new interview or changes the selected profile.
Each invocation changes one tool, not the whole stack. Quipu server/CLI source
convergence, Camayoc source bundles, and crew runtime updates retain their own
contracts and are not silently included.
The updater resolves GitHub’s latest published stable release, requires the named binary archive and checksum asset, checks the downloaded SHA256 and executable’s version, then atomically replaces the PATH entry. Symlink targets are not written through. It retains the previous bytes, runs the existing adapter’s functional verification, and only then records the new version as verified. Failed proof restores the backup. An interrupted update leaves a journal; the next invocation restores its previous artifact before doing anything else, including during a hold.
A numerically newer installed version is preserved. Equal versions with different
binary bytes are ambiguous (for example a source build ahead of its release) and
are refused. Unreadable versions, missing assets and wrong checksums are errors,
never evidence of a current install. The older reviewed-pin update path also
refuses to replace a newer installed binary or an ambiguous equal-version build.
For initial or missing installations, use apply first.
Create ~/.caboodle/hold to defer new release lookup and installation, or set
CABOODLE_HOLD_FILE to a shared host hold path. The updater uses an OS-held lock
beside the state file to serialize release updates; backups are retained under
release-backups/<binary>/<sha256> in the same directory. A host scheduler must
bound total runtime and retain stdout/stderr. This command does not itself install
a timer or claim that another host or a long-running process has updated.
caboodle update-self applies the same release, checksum, downgrade, backup and
interruption-recovery controls to Caboodle itself. Its post-install contract
requires the new binary’s version and update-release --help to answer correctly.
Self-update supports Linux x86_64, macOS x86_64, and macOS arm64, selecting the
matching published archive and its .sha256 asset. Other platforms refuse before
release lookup. The extracted executable is written as a new file rather than
restoring archive metadata, so archive extended attributes are not installed.
Event consumers can select an exact stable release with update-self --tag v0.2.14.
To bind the download to evidence obtained before installation, also supply
--archive-sha256 and --binary-sha256. Both values must be lowercase SHA256
digests; both flags require the exact tag and each other. CABOODLE compares the
archive before extraction and the executable before running it or replacing the
installed copy. A mismatch leaves the previous installer and state intact.
Use update-self --check to inspect metadata; it does not download artifacts or
prove either supplied hash. Bootstrap from a published release
before scheduling this command; a same-version source build with different bytes
is deliberately refused rather than silently overwritten. It replaces the copy
PATH runs; when you invoke a different copy by its full path, it says that copy
was not changed.
Installed files
- Binaries in
~/.cargo/bin(or$CARGO_HOME/bin). Keep that directory on your PATH, ahead of any other directory that holds an older copy of the same tool (~/.local/binis the usual one). The shell runs the first copy it finds. - Unpacked releases under
~/.local/share/caboodle/. - One setting,
[quipu.owl] reactive_materialize = true, merged into~/.config/bobbin/config.toml. Other settings in that file are kept. - In the directory you ran it from:
caboodle-plan.tomland.caboodle/(interview, state, queued install records).
Every verification check uses a throwaway directory, camayoc’s included. The camayoc
check starts its own quipu-server on a free localhost port with a temporary store,
loads camayoc’s vocabulary and one marker node into it, and stops it afterwards. It
never touches the server at QUIPU_SERVER. To set camayoc up against your real quipu
server, run the bundle’s bootstrap yourself as a separate step:
QUIPU_SERVER=<your server> bash ~/.local/share/caboodle/camayoc/<revision>/scripts/bootstrap.sh.
To remove caboodle, delete ~/.cargo/bin/caboodle and the .caboodle/
directory. Installed tools stay, because other workflows may use them. The
install guide covers release pins, source
installs, and updates.
Platform prerequisites
Platforms. Linux x86_64 needs no Rust toolchain: each tool comes from a
checksummed release or source archive, and Go builds desire-path. On other
platforms, availability depends on the tool as shown below; caboodle doctor
prints the exact source-build command where no reviewed release exists.
| Linux x86_64 | macOS arm64 | macOS x86_64 | Linux arm64 | |
|---|---|---|---|---|
| caboodle | release | release | release | build with cargo install --git |
| quipu | release | release | release | release |
| camayoc | source archive | source archive | source archive | source archive |
| bobbin | release | release | release | release |
| yupana | release | release | release | build with cargo |
| desire-path | built with Go | built with Go | built with Go | built with Go |
Commands on PATH. curl, tar, git, bash, python3, and sha256sum
(brew install coreutils supplies sha256sum on macOS). Yupana verifies its
three pinned v0.12.7 archives in-process and needs only curl and tar for installation. The everything
profile also needs Go 1.22+. Rust is only needed for the source builds above.
Accounts and servers. None. Nothing talks to a private network, and no
token is needed to install. A token (QUIPU_AUTH_TOKEN) matters only when you
send install records to a remote quipu with flush-episodes.
Seeds successful-update rollback
rollback-seeds restores only the prior Seeds artifact retained by the last
successful update-release --tool seeds. It requires the same selected plan,
state file, executable path and exact current/prior SHA256 values:
caboodle rollback-seeds --plan crew.toml --state state.json \
--current-sha256 <installed-sha256> --prior-sha256 <retained-prior-sha256>
The successful update records a Seeds receipt bound to the plan, state bytes, paths, versions and both artifact hashes. Missing receipts, changed state, wrong hashes, corrupt backups, symlinks, held updates and lock contention refuse before replacing Seeds. A hold is any directory entry, including a dangling symlink. Unknown hold-path configuration or metadata errors block execution; only a definitely absent entry permits it. Other tools retain their state. A successful rollback runs the existing isolated functional verifier and consumes the receipt. A failed rollback verifier restores the current artifact and retains the state. An interrupted rollback uses normal release journal recovery, restoring the current artifact and invalidating only Seeds verification; it must earn fresh proof before a later operation. This is not a general downgrade override.
Legacy updates without this receipt do not qualify. A private implementation or test does not authorize publication or adoption; follow the applicable review and deployment contract before using it on an installed tool.
Maintainers can verify an embedded Rust release member before publishing a pin:
caboodle verify-member seeds
This stages the archive pinned by that Caboodle checkout, checks its digest,
identity and version, and runs the manifest’s functional steps in temporary
roots. It does not install a managed program. The caboodle-verify CI job runs
this command against the PR checkout; Seeds pin automation requires that exact
check to complete successfully, alongside the rest of CI.
Using it with agents
Caboodle is a CLI that an agent can run with the Tambo skill. It does not expose its own MCP server. MCP (Model Context Protocol) lets an agent call servers supplied by the installed tools.
Standalone agent
After installing the retrieval profile, register Bobbin with Claude Code. Run it
from the repository to index, and register by absolute path: MCP clients launch
servers without your shell’s PATH, so a bare bobbin fails with ENOENT:
claude mcp add bobbin -- "$(command -v bobbin)" serve "$PWD"
For code-intel or everything, also register Yupana:
claude mcp add yupana -- "$(command -v yupana)" serve
claude mcp list
Index a repository before searching it: run bobbin init and bobbin index
in that repository. Other MCP clients use the same executable and arguments
in their client-specific server configuration.
Managed crew
For a configured Shantytown rig, caboodle project-settings delegates
registration to Shantytown and its Quipu tooling manifest. It does not launch
or restart agents. See registering MCP servers
for prerequisites, selection flags, remote proxies and the policy-only mode.
Agent identity and write access
Agents that write to a shared Quipu store sign each write with their own key. No shared bearer is sent. The operator who runs the store decides which keys may write, and can withdraw that permission for one agent without touching any other.
One identity per agent
Run each agent as its own operating-system user. At minimum, give each agent its
own HOME and its own ~/.config. Never share one login between agents. This
buys you three things:
- Attribution. Every write records the agent that signed it, so the history says who did what.
- Revocation. You can revoke one agent’s key and leave every other agent working.
- Blast radius. A leaked key exposes one agent’s write permission, and only until you revoke it.
Never copy a key file between hosts or between agents. A second machine, or a second agent on the same machine, gets its own key.
1. Create the key on the agent’s machine
As the agent’s user, run sd key init. The introducer is the person or lead who
will register the key on the Quipu host. It is never the agent itself.
sd key init --introducer <operator> --session <agent>-<host> --agent <agent>
sd key init writes one owner-only (mode 0600) private key file under
~/.config/seeds/keys/ and never overwrites an existing key. It prints:
- the public key and its
key_id; - the lines to add to
~/.config/seeds/config.toml; - the one-time
quipu attest registercommand for your operator.
Only the public key leaves the machine. sd key show prints the same details
again later.
Add the printed lines to the agent’s user config, together with the server and project the agent works in:
[quipu]
url = "https://quipu.example.org"
signing_key_file = "/home/<agent>/.config/seeds/keys/<agent>-<host>.key"
signing_session = "<agent>-<host>"
signing_introducer = "<operator>"
[project]
graph = "https://example.org/project/<name>"
prefix = "<prefix>"
Until the key is registered, a write is refused with a message that names the fix:
signed write refused (unbound). The server has no binding for this key and
session: run `sd key show` and have your introducer run the `quipu attest
register` line it prints.
2. Register the key on the Quipu host
The operator runs the printed command once, as the user that owns the store,
and adds --db with the store path:
quipu attest register --agent <agent> --session <agent>-<host> \
--public-key <hex> --introducer <operator> \
--issued-at <epoch> --expires-at <epoch> --allow-write \
--db /path/to/quipu.db
quipu attest list --db /path/to/quipu.db
attest list shows one row per binding, with its key_id, introducer,
allow_write, expiry and revoked state. The running server picks up a new
binding without a restart.
- Share-only vs write. Without
--allow-write, a binding only lets the store trust shares that this key signed. With it, the key may also sign writes to/knot,/updateand/episode. - Expiry. Writes stop at
--expires-at.sd key initproposes about one year; choose what your policy allows. - CLI version. Use the
quipuCLI whose version matches the served server (comparequipu --versionwith the server’s/version). Server packages that ship onlyquipu-serverdo not includeattest.
3. Verify
As the agent, make one write, then read it back as someone else:
sd create "agent write check" # as the agent: prints the new id
sd show <id> --json # as the operator: created_by is the agent
The server log records the accepted write with auth_outcome
authenticated_attested.
4. Lifecycle
| Action | Command (on the Quipu host, with --db) | Agent sees |
|---|---|---|
| Withdraw write, keep share trust | quipu attest deny-write <session> | signed write refused (scope) |
| Restore write | quipu attest allow-write <session> | writes succeed again |
| Revoke the key | quipu attest revoke <session> | signed write refused (revoked) |
| Rotate | the agent runs sd key init with a new session; register it, then revoke the old session | writes succeed with the new key |
Revocation is immediate and permanent for that session. To replace a key, mint a
new one under a new session name: sd key init refuses to overwrite an
existing key file.
Pitfalls
quipu-serverhas noattestsubcommand. It ignores the word and starts a server on its default store. Use thequipuCLI on the store host.- Do not register your own key. The introducer registers it, so another person or lead vouches for each agent.
- A fallback credential can hide a missing key. If an old shared-token file,
or a symlink to one, is still present on the agent’s machine, older clients may
keep authenticating through it. They then never report the missing or rejected
identity. Remove such fallbacks once every client uses its own key, and confirm
with
caboodle doctor, which names the credential source in use.
Worked example: enrolling an agent on a second host
This is a real enrollment, with names and paths replaced. An operator gave the agent on a second host write access to a shared store:
- On the agent’s host, as the agent’s own user:
sd key init --introducer <lead> --session <store>-<host>-<agent> --agent <host>-<agent>. The agent sent its lead the printed public key and register line, and nothing else. - On the Quipu host, the operator checked the served version with
curl -s https://quipu.example.org/version. They then downloaded thequipuCLI of exactly that version from the published GitHub release, and checked its archive against the release’s own.sha256file. Releases publish one checksum file per archive. They unpacked it into a private temporary directory, not ontoPATH, and touched no service. - As the user that owns the store, the operator ran
quipu attest list --db <store>, which showed 4 existing rows. Then they ran the register line once, with--allow-writeand--db <store>. A freshattest listshowed 5 rows: the new binding withallow_write=true revoked=false, and the 4 earlier rows unchanged. They deleted the temporary directory. - The agent wrote one comment. From a third machine, the lead read it back with the agent as its author.
- Rollback, if needed:
quipu attest revoke <session> --db <store>, run with a matching verified CLI.
--allow-write grants write permission to the store as a whole. Per-item or
comment-only limits are not part of the binding. Do not assume them.
Using caboodle
Caboodle ships a resumable interview and the plan/apply/verify engine with adapters for Quipu, Camayoc, Bobbin, Yupana, and Desire Path.
cargo install --git https://github.com/scbrown/caboodle --locked
# Guided: use, crew themes, and anticipated graph questions are checkpointed.
caboodle init --guided
# Read-only preflight: every blocker on this host, before anything installs.
caboodle doctor
# Non-interactive: read the same schema from caboodle-intent.toml.
caboodle plan --profile retrieval --intent caboodle-intent.toml
# Converge the reviewed plan, then prove both tools with isolated round trips.
caboodle apply
caboodle verify
caboodle verify-questions
# Or run both phases together after review.
caboodle install
apply installs a missing released tool and reads its version back — a
successful installer exit alone is never accepted. verify uses temporary
isolated stores: it proves a marker absent, writes it, and requires the reader
path to return it. Progress is written atomically to .caboodle/state.json, so
rerunning converges.
If input ends partway through an interview, Caboodle stops without a plan and
keeps accepted answers in .caboodle/interview.toml. Run caboodle init --guided again to resume at the next unanswered question. Invalid answers are
rejected and never produce a plan.
Use --skip-install when package installation belongs to another system; the
version and functional checks still run.
code-intel adds checksum-pinned Yupana and proves a caller edge in a temporary
repository with isolated HOME/state. everything additionally installs Desire
Path by building the revision its v0.3.2 tag names, reads it back from dp version, and
proves ingest/list against a temporary database. These checks never use the user’s normal evidence stores.
caboodle-intent.toml names the intended use, zero or more themed crew members,
and at least one anticipated ontology question. A question is a contract, not a
wish: it includes an answer shape, fixture/seed intent, executable SELECT or
ASK SPARQL, and an expected result marker. The reviewed install plan embeds
the contract, and verify-questions executes it through Quipu’s reader path.
This makes “what should the graph answer?” an install acceptance test.
A first install usually has no ontology to ask about yet. Pressing enter at the
question-count prompt selects the built-in self-test question instead. It seeds one
caboodle-verify-roundtrip node into a scratch store and queries for it, so it
passes on a fresh machine. verify-questions always runs it against that scratch
store, even when --db points at your own. Replace it with real questions later
by editing [intent] in the plan.
intended_use = "answer dependency and ownership questions about my services"
[[crew_members]]
name = "Harbor"
theme = "patient navigator"
domain = "service operations"
role = "trace dependencies and explain incidents"
[[anticipated_questions]]
question = "which services depend on the message broker?"
answer_shape = "a list of service entities"
seed_intent = "two fixture services and one depends_on edge to the fixture broker"
sparql = "SELECT ?service WHERE { ?service <https://example.org/depends_on> <https://example.org/broker> }"
expected = "fixture-service"
The prose fields steer ontology and seed construction; the SPARQL and expected marker decide whether that construction is retrievable through the path a reader will actually use. Never put credentials in this file or the generated plan.
Review observability before deployment
For every selected tool, CABOODLE emits a versioned contract. Quipu and Bobbin
currently declare /metrics and required metric families; tools without an
exporter remain explicit contracts with an unavailable reason rather than being
silently omitted. Supply generic Prometheus targets without URL schemes:
[targets]
quipu = "quipu.example:8080"
bobbin = "bobbin.example:3030"
Then render and inspect the artifacts. Rendering never claims the targets were scraped or deploys anything:
caboodle render-observability --targets caboodle-observability.toml
caboodle validate-observability
less caboodle-observability/prometheus.yml
less caboodle-observability/alerts.yml
less caboodle-observability/dashboard.json
less caboodle-observability/contracts.json
Validation fails on a missing or malformed selected target, a target for a tool whose pinned contract has no exporter, a missing scrape job, or an alert or dashboard metric absent from the versioned contracts.
Reference
The quickstart covers init, plan, doctor, and install. The rest of the
command surface, for reviewed updates, shared graphs, observability, and install
records:
# Build caboodle from source instead of the release installer.
cargo install --git https://github.com/scbrown/caboodle --locked
# Report what would block an install here. Changes nothing; exits nonzero on a blocker.
caboodle doctor
# Answer the use-shaped interview. It asks who the themed crew members are and
# which questions the finished graph must answer, then only writes a plan.
caboodle init --guided
# Or supply the same intended-use contract non-interactively.
caboodle plan --profile retrieval --intent caboodle-intent.toml
# Optionally add canonical Quipu share directories to the reviewed corpus.
# The target database is explicit; apply stages imports but never promotes them.
caboodle plan --profile retrieval --share ./team-share --quipu-db ./knowledge.db
# Converge the reviewed plan, then prove each tool with a round trip.
caboodle apply
caboodle verify
caboodle verify-questions
# Or run both phases together after review.
caboodle install
# Ask whether the running stack matches this Caboodle build's reviewed pins.
# The command exits nonzero when anything is missing, unreadable, or drifted.
caboodle check-updates
# Converge only drifted tools, read every version back, and functionally verify
# each changed tool before recording it as current.
caboodle update
# A full tool + crew plan is explicit and reviewable.
caboodle plan --profile everything --crew both
apply installs a missing released tool and reads its version back; Bobbin comes
from its checksummed release bundle (including its runtime), not a source build.
A successful installer exit alone is not accepted. verify proves a marker is
absent first, writes/indexes it, and then requires the reader path to return it.
Every check uses a temporary isolated store; camayoc’s starts and stops its own
scratch quipu server (see
installed files). Progress is written atomically to
.caboodle/state.json, so rerunning converges and preserves a still-current
verified result.
check-updates compares the running identities with the release versions and
source revisions pinned in the selected plan’s Caboodle build. It does not use
an unreviewed “latest” endpoint. update is the mutation half: it installs only
drifted selections through their existing checksummed/source-pinned adapters,
requires version read-back to match the reviewed identity, runs the adapter’s
functional proof, and only then records an updated transition. A second run is
current and performs no installation. This keeps release selection in review
while making deployed-versus-released drift executable rather than anecdotal.
If the interview loses input or its caller stops, rerun caboodle init --guided:
accepted answers resume from .caboodle/interview.toml. The session disappears
only after the complete plan has been written successfully.
The interview works backward from expected use. Each crew member has a free-form
theme, domain, and role rather than a closed role enum. Each anticipated ontology
question records its expected answer shape, the seed facts needed to exercise it,
an executable SELECT/ASK query, and a result marker. caboodle verify-questions runs those reader-path checks after installation. Empty,
duplicate, credential-bearing, or non-executable entries are refused before a
plan is written.
Installable profiles today are kg (Quipu + Camayoc), retrieval (plus
Bobbin), code-intel (plus Yupana), everything (plus Desire Path and,
when selected with --crew, Shantytown/Creel), and crew (Shantytown, Creel,
both, or standalone). CABOODLE installs
checksum-pinned Shantytown and Creel distributions. Creel verification remains
browser-owned: it requires explicit machine-readable doctor and admission
documents and refuses missing, unknown, unredacted, or non-admit evidence. See
the crew capability contracts.
Until Camayoc publishes core.pendant, CABOODLE installs its
checksum-pinned bootstrap distribution: ontology, shapes, queries, and the
same fail-closed gate proof. Verification then proves a separate first ingest
with an absent control, reader-path retrieval, and an idempotent replay.
Use --skip-install when package installation belongs to another system; the
version and functional checks still run.
Yupana installs from its checksum-pinned v0.12.7 release and proves callers on
an isolated fixture repository. CABOODLE builds Desire Path from the revision its v0.3.2 tag names (1f457ec), stamps
that identity into dp version, and proves an isolated ingest/list round trip. Neither
verification can write into the user’s normal Yupana state or Desire Path DB.
caboodle render-observability converts the reviewed selection plus explicit
generic targets into Prometheus scrape config, starter alerts, a dashboard, and
versioned contracts. validate-observability fails closed on missing targets or
uncovered metrics; rendering is review-only and never claims a live scrape.
Applied/verified transitions and br JSONL lifecycle snapshots queue as
redacted, content-addressed Quipu episodes. flush-episodes requires an HTTPS
endpoint plus an environment-only token, proves a query marker first, and keeps
the identical bytes pending on ambiguous delivery.
Profiles can also consume one or more directories produced by quipu share.
Caboodle passes each directory unchanged to quipu import, records the returned
share ID, staging graph, eligibility, and blockers in .caboodle/state.json,
and leaves ROOT promotion to the explicit quipu import promote review step.
Non-conforming or off-vocabulary shares remain quarantined and visible rather
than being reshaped into a Caboodle-specific bundle.
Command and option index
Run caboodle <command> --help for that command’s complete option syntax.
The table covers the current source tree; published binaries may lag new commands.
| Command | Purpose and options |
|---|---|
doctor | Read-only preflight; --plan. |
init | Interview; --guided, --output, --session. |
plan | Write selection; --profile, --crew, --intent, --output, --share, --quipu-db, --quipu-flavor, --embedding-model, --quipu-mcp-url, --quipu-mcp-token-file. |
apply | Install; --plan, --state, --skip-install. |
verify | Functional proof; --plan, --state, --creel-doctor, --creel-admission. |
install | Apply then verify; all apply and verify options. |
check-updates | Compare reviewed pins; --plan. |
update | Converge reviewed pins; --plan, --state, --creel-doctor, --creel-admission. |
update-release | Unix release update; --tool, --check, --plan, --state. Quipu tracks a reviewed paired archive pin; Camayoc/Shantytown refuse without checksummed publications. |
update-self | Unix self-update; --check, --state. |
bump-member | Maintainers: re-pin a members/*.toml to its published release; manifest argument, --tag, --probe-wrapper. |
project-settings | Rig registration; --root, --agent, --registry; or --policy-only with --plan, --output. |
verify-questions | Execute intent questions; --plan, --db. |
render-observability | Generate reviewable artifacts; --plan, --targets, --output. |
validate-observability | Check artifact contracts; --output. |
queue-br | Queue lifecycle records; input JSONL argument, --queue. |
flush-episodes | Deliver queued records; --queue, --endpoint. |
packs trust | Pendant autoload trust: own <host/owner>, allow <repo> <sha256>, allow-endpoint <repo> <url>, list, check <remote> <sha256>, check-endpoint <remote> <url> (exit 3 when refused); --file. |
Files and configuration
The configuration reference documents plan, state, intent, environment variables and observability targets.
Words you will see
| word | meaning |
|---|---|
| plan | caboodle-plan.toml, the reviewed list of what will be installed. Nothing installs without one. |
| state | .caboodle/state.json, what is installed and when it last passed its check. |
| negative control | The half of a check that proves the thing is absent first, so a pass could have been a fail. |
| episode | A batch of facts written to quipu in one go. |
| knot | One write to quipu over HTTP (/knot). |
| ontology, shapes | The vocabulary quipu accepts and the rules (SHACL) that reject bad facts. |
| share, pendant | A portable export of a quipu graph that another store can import. “Share” names the protocol; a pendant is the artifact that travels. |
| crew | Several named coding agents that work together, run by shantytown (terminal) or creel (browser). |
bead, br | An issue in beads, a git-friendly issue tracker agents use as memory. |
| MCP | Model Context Protocol, how an agent calls a tool’s server. |
Quipu paired release updates
caboodle update-release --tool quipu --plan caboodle-plan.toml selects the
exact version and per-target SHA256 in release-pins/quipu.toml, rather than
implicitly trusting the latest release or its downloaded checksum. The
publication must provide the expected archive and checksum exactly once; both
the archive bytes and published sum must agree with the reviewed digest.
The plan must select a local release-flavor Quipu. Both quipu and
quipu-server must be regular files selected by PATH in CARGO_HOME/bin
(default $HOME/.cargo/bin), with matching versions. External servers, LanceDB
source builds, shadowed paths, symlinks, downgrades, mixed versions and equal
versions with different bytes are refused. The install does not restart any
service or load data into a running store.
Both executables are backed up before either is replaced. The existing release
lock and pending journal protect the pair; failed installed verification or an
interrupted swap restores both prior binaries and the exact prior state. A
corrupt backup retains the journal and refuses recovery before changing either
file. Backup file renames and every ancestor directory entry are synced before
the pair journal becomes durable; its removal is synced before success. A failed
clear barrier reports uncertain completion and attempts to retain the exact
pending record for later recovery. Failed retention remains an error, never a
claim that completion or restoration is proved. Existing single-binary journals and Seeds rollback remain supported.
This does not extend rollback-seeds to other tools.
Verification performs the existing negative/positive client and vocabulary
round trips in temporary stores and checks the server version/help contract.
It does not prove a running server, service restart, database migration or
host delivery. --check inspects publication metadata without downloading or
replacing executables and explicitly reports that checksum/functional proof
has not run. Source pin updates still require review; no automatic pin writer
or host schedule is introduced by this command.
Camayoc has no published release, and Shantytown’s wheel release lacks a checksum asset. Their explicit refusals preserve these missing trust anchors; source bundles and wheels are not guessed into standalone binaries.
Configuration reference
Caboodle writes a reviewable TOML plan and a JSON progress file. Use the CLI to create the plan; keep credentials in the environment, never in intent text. The plan’s schema rejects unknown keys.
Plan: caboodle-plan.toml
| Key | Meaning |
|---|---|
schema_version | Plan schema version, currently 1. |
profile | kg, retrieval, code-intel, everything or crew. |
tools | Ordered tool selection derived from the profile. |
shares | Canonical Quipu share directories to stage during apply. |
quipu_db | Explicit destination database, required when shares are selected. |
quipu_flavor | release (default) or lancedb; see profiles. |
stack_config.quipu.owl.reactive_materialize | Recommended OWL materialization setting merged into Bobbin’s config. |
crew.mode | shantytown, creel, both or standalone. |
crew.durable_owner | Shantytown for durable crew work, when selected. |
crew.burst_owner | Creel for browser bursts, when selected. |
crew.routing | single-owner or explicit-handoff, derived from the mode. |
crew.policy.identity_source | quipu; crew identity authority. |
crew.policy.model | Optional model selection. |
crew.policy.tools | The crew profile’s tool set. |
intent | Intended-use contract described below. |
embedding_model.destination | Directory for the model artifacts. |
embedding_model.artifacts | Array of entries, each with name, HTTPS url and sha256. |
Crew ownership fields must match the selected mode. Do not hand-edit them into an alternative routing scheme. Profiles explains the contracts.
Intent: caboodle-intent.toml
| Key | Meaning |
|---|---|
intended_use | Nonempty plain-language purpose. |
crew_members | Array with name, theme, domain, role; may be empty. |
anticipated_questions | At least one executable reader-path question. |
anticipated_questions[].question | The question in words. |
anticipated_questions[].answer_shape | Shape of the expected answer. |
anticipated_questions[].seed_intent | Facts required before the query can pass. |
anticipated_questions[].sparql | SELECT or ASK query. |
anticipated_questions[].expected | Marker required in its result. |
anticipated_questions[].self_test | Built-in scratch-store check flag; normally generated by the interview. |
plan embeds the intent in the plan. verify-questions executes those queries;
custom questions require their seed facts in the target database first. The
built-in interview self-test instead seeds its own temporary store. Installing
and verifying tools does not seed a user’s production graph.
Local state and targets
.caboodle/state.json is generated progress, not user configuration. It records
tools, model artifacts and staged shares. Preserve it to resume an interrupted
apply or update. .caboodle/interview.toml checkpoints interview answers;
.caboodle/episodes/ retains queued install records until confirmed delivery.
caboodle-observability.toml contains a [targets] table with quipu and, when
selected, bobbin keys. Values are Prometheus host:port destinations, without
a URL scheme or path. Rendering produces files for review, not a live scrape.
See emission and proofs for those boundaries.
Environment
| Variable | Purpose |
|---|---|
HOME | User configuration, data and default install directories. |
CARGO_HOME | Overrides the binary destination root; binaries go in its bin/. |
PATH | Must find the installed binaries before stale copies. |
CABOODLE_CAMAYOC_ROOT | Override the Camayoc distribution root. |
QUIPU_SERVER | Existing Quipu server for operations that need one; temporary functional checks remain isolated. |
QUIPU_AUTH_TOKEN | Nonempty bearer override for Quipu requests. |
QUIPU_AUTH_TOKEN_FILE | Request-time token file; defaults to ~/.config/quipu/token. |
CABOODLE_HOLD_FILE | Override the release-update hold marker, default ~/.caboodle/hold. |
SHANTY_TOOLING_MANIFEST | Shantytown’s managed rig registration manifest; see agent setup. |
The shell release installer also accepts CABOODLE_VERSION,
CABOODLE_INSTALL_DIR, CABOODLE_TARGET, CABOODLE_REPOSITORY and
CABOODLE_RELEASE_BASE_URL. The last two select the download authority; keep
them unset for official releases. These are installer inputs, not plan keys.
Quipu credentials
Obtain an accepted credential from the target server administrator through your approved secret distribution channel. The administrator activates the shared bearer or registers an issued named credential before distributing it. Locally installing a random token does not grant access.
install -d -m 700 "$HOME/.config/quipu" && install -m 400 /secure/issued-token "$HOME/.config/quipu/token"
caboodle provision-quipu-token --from /secure/issued-token --server https://your-quipu-server.example
caboodle doctor
provision-quipu-token proves the issued file against the authenticated server
before installing it. It refuses to replace a differing existing credential;
rotation remains a separate decision. It creates the canonical file with mode
0400 and its parent with mode 0700. No token is printed or stored in a plan.
A nonempty QUIPU_AUTH_TOKEN after trimming takes precedence over QUIPU_AUTH_TOKEN_FILE,
then the default ~/.config/quipu/token. Files are read on every request so
rotation reaches running sessions. An explicit file does not fall back to the
default if missing. Existing deployments can retain their file override until
provisioning and rotation move together; avoid independent copies of secrets.
Token contents must never enter command arguments, logs or version control.
Doctor and verify name the winning source without printing its value, including
an environment value that shadows a valid canonical file.
Doctor checks the protected /episode route with {}. The server authorizes
before rejecting the incomplete episode, so no graph data is written. It reports
accepted authorization, missing token, rejected token, read-only server, or an
unknown write path (including no HTTP response). This is authorization evidence,
not a successful storage commit. Doctor also requires a nonempty canonical file
with mode 0400 and parent mode 0700, and an authenticated /shapes read
returning 2xx. caboodle verify runs that live credential proof before recording
Quipu as verified, then exercises installed tools in isolated scratch stores.
Set QUIPU_SERVER to the intended server; without it, verify uses the plan’s
Quipu MCP URL or http://localhost:3030. Missing, rejected, or unproven
credentials leave verification incomplete. Public health is not an auth check.
Quipu MCP for Claude Code
A Claude Code MCP entry for Quipu that holds only a URL sends no bearer. Reads
work and every MCP write is refused with missing_or_invalid_bearer_token.
caboodle plan --quipu-mcp-url <server> adds a reviewable [quipu_mcp] step.
That step changes your Claude Code configuration, so it is opt-in.
apply installs ~/.local/bin/quipu-mcp-headers (mode 0755). Claude Code runs
it at connect time. It reads the token with the precedence above and prints the
Authorization header. apply then registers the user-scope entry
{"type":"http","url":"<server>/mcp","headersHelper":"<helper>"} through
claude mcp add-json. The secret is written into neither the helper nor the
Claude configuration. Re-running is a no-op. A differing user-scope quipu
entry, including one without a headersHelper, is replaced, and the output says
so. If registering the new entry fails after the old one was removed, the old
entry is put back. A running Claude Code session keeps its old connection until
/mcp reconnects it.
Claude Code runs the helper in its own environment, which may not carry the
token variables. If this host’s token file is not ~/.config/quipu/token, give
its path with --quipu-mcp-token-file. plan also takes it from
QUIPU_AUTH_TOKEN_FILE when that is set. The path, never the token, is recorded
in the plan and becomes the helper’s default. QUIPU_AUTH_TOKEN and
QUIPU_AUTH_TOKEN_FILE still take precedence when Claude Code has them.
verify fails if the user-scope entry is missing, differs from the plan or has
no headersHelper. It also fails if a local-scope quipu entry without one
shadows it. It then sends an invalid-Turtle quipu_knot call to /mcp, first
without a credential as a control, which must be refused. It sends it again
with the helper’s header, which must reach Quipu’s parser. Nothing is written.
A server that accepts the unauthenticated control reports UNKNOWN, because it
cannot show that the helper works. Verify runs in your shell, not in Claude
Code. If the helper finds a token only through a variable set in that shell, it
warns that Claude Code must see the same variable. Re-plan with
--quipu-mcp-token-file if Claude Code will not see it.
Codex search signposts
The desire-path hook bundle supplies dp signpost-prefetch before Codex Bash
calls and dp signpost after successful calls. Codex uses the same
tool_name: Bash and tool_input.command hook envelope for these events.
Turn-completion ingestion remains in codex_notify with --source codex;
these search hooks do not convert turn records into tool-failure desires.
Failure-event signposting is not claimed by this bundle for Codex.
Set DP_SIGNPOST_BOBBIN_URL, DP_SIGNPOST_LOG, and any deployment-specific
DP_SIGNPOST_SEARCH_MODE in the environment that launches Codex. Values in
Claude’s settings.json environment do not configure Codex. Verify a real
search produces a signpost event under the Codex session ID and that prefetch
warms the cache; a configured hook alone does not prove delivery.
Codex edit policy hook
The Yupana bundle registers yupana hook pre-edit for native apply_patch
PreToolUse events. This requires a Yupana build with native multi-file Codex
patch support; a pre-support binary silently ignores the patch contents.
Activate this entry only after the supporting Yupana release is installed.
Verify a real edit’s session-attributed guard metric and advisory output;
rendered configuration alone does not prove edit coverage. Existing running
agents pick up changed hooks on their next launch.
Externally owned Quipu
Set external_quipu = true in a reviewed plan when another mechanism owns
Quipu installation. The default is false. Caboodle resolves quipu and
quipu-server from PATH, reads both versions, and records their canonical paths
with the identity. It never installs, converges, or release-updates these files.
Missing executables and incompatible versions fail; external ownership does not
waive the isolated write/read round trip or hook registration checks. Other tools
retain their managed-install and shadow checks. Build flavors are incompatible
with external ownership because Caboodle does not build that installation.
Verify uses the resolved client for both the entity and vocabulary round trips.
An external source build has to support those contracts; a version banner alone
is insufficient. check-updates reports external ownership rather than claiming
release convergence. Apply records identity, while verify records functional
proof. Changing executable paths invalidates retained verification even if the
version strings match.
Architecture
Caboodle’s engine is a plan → apply → verify loop with a state file recording what is installed and last proven working. Idempotent and resumable: re-running converges, and a failed step names itself.
The wizard is an interview, not a form over install switches. caboodle init --guided first elicits intended use, themed crew-member identities/domains/roles,
and the questions the installed graph must answer. Those answers become typed
plan data and executable reader-path contracts. The engine therefore builds
backward from expected retrieval rather than forward from whatever data happens
to be available.
In agent hands the same flow is a skill; non-interactive callers provide the
same schema in caboodle-intent.toml. Output is always the plan file —
two-phase, so nothing installs on the strength of a conversation alone.
Three proofs, not one
Three claims separate caboodle from an install script, and each is a proof:
- Installed — proven by version read-back, never by exit code.
- Working — a per-tool functional round-trip: quipu accepts an episode and a control-gated query finds it; bobbin indexes a fixture and search returns it. Every check pairs with a negative control — the selftest that corrupts a byte and goes red — so a pass is capable of failing.
- Observable — Prometheus actually scrapes each tool’s declared exporter; caboodle generates the scrape config, a starter alert pack, and one consolidated dashboard.
This encodes the operating culture the stack was built under: verify by mechanism, read back what you wrote, control every zero.
Convention over manifest
There is no per-repo manifest file to parse or rot. Each tool is caboodle-ready by exposing the pattern the repos already follow:
- conventional CLI verbs:
<tool> selftest,<tool> health,<tool> init - and/or
justrecipes:just install,just verify
Caboodle discovers and drives these directly. Metrics contracts live as camayoc catalogue entries, not per-repo config. The readiness contract every corpus repo adopts:
- The conventional CLI surface above.
- A
selftestwith a negative control. - A metrics story, catalogued in camayoc.
- Versioned release artifacts — install never means build-from-source.
initwrites a starter config; file + env with documented precedence.- A health probe distinct from the selftest.
- No environment-specific names in defaults or docs.
Profiles
kg— quipu + camayocretrieval— + bobbincode-intel— retrieval + Yupana; isolatedanalyze/callersproofcrew— a choice: shantytown, creel, both, or standalone (no crew layer — the tools serve a single agent or a human directly). A tmux-resident crew on a host, parallel agent bursts in the browser, the pair, or neither.
Both harnesses are first-class: Claude Code and codex can each drive the
install, and when a crew is chosen the wizard asks which harness each role runs
on, emitting the right per-harness configuration. The crew profile also
installs seeds (sd), the crew’s work tracker, and shuttle, its workflow engine.
everything— code-intel + Desire Path + seeds + shuttle; isolated failure-ingest/read proof, a seeds marker round trip, and a shuttle run round trip
When a plan selects both seeds and Desire Path, apply also sets the Desire
Path command alias bd -> sd, so an agent that types bd from habit reaches
seeds. verify checks that the alias is there. If the host already has a bd
alias that points somewhere else, caboodle leaves it alone: dp alias would
silently replace it, so caboodle reads the existing aliases first. apply and
verify then report the existing target as a deliberate local choice, not a
failure. If dp is not on PATH, apply prints a note instead of failing.
Any profile can extend its corpus with repeatable --share selections because
all profiles include Quipu. Shares require an explicit --quipu-db, are staged
through Quipu’s canonical import command during apply, and are never promoted
to ROOT automatically. The share choice is part of the reviewable plan rather
than a hidden installer input.
The crew choice is emitted today:
caboodle plan --profile crew --crew shantytown
caboodle plan --profile crew --crew creel
caboodle plan --profile crew --crew both
caboodle plan --profile crew --crew standalone
both is not fan-out. Its plan names Shantytown as durable_owner, Creel as
burst_owner, and explicit-handoff as the only routing mode. This keeps one
owner per task until the cross-harness handoff contract lands.
The reviewed plan can export policy summaries for review. These summaries do not register MCP servers or override the rig crew and its Quipu tooling manifest:
caboodle project-settings --policy-only
In both mode this writes shantytown.settings.json and
creel.settings.json. Their shared object is byte-equivalent. Their security
surfaces are intentionally not: Shantytown owns hooks and host-workspace access;
Creel owns browser-BYO credentials (write-only to agents) and operator-granted
browser permissions. Neither adapter is allowed to emit the other’s fields.
The engine applies and verifies Quipu + Camayoc for kg, adding Bobbin for
retrieval, Yupana for code-intel, and Desire Path for everything. A crew
plan then applies only the selected runtime: the released
Shantytown wheel, the static Creel browser bundle, both in their declared owner
roles, or neither for standalone. Both distributions are checksum-pinned.
Shantytown verification reads back st 0.4.0. Creel’s browser security and
admission facts cannot honestly be inferred by the host-side installer, so its
adapter consumes the versioned crew capability contracts.
It refuses when either contract is absent, when a required doctor result is not
pass, or when the governor does not return a measured admit.
Camayoc currently distributes its bootstrap ontology, shapes, queries, and gate
proof as a repository bundle; CABOODLE pins and checksums that bundle until the
designed core.pendant artifact is published.
Quipu install flavor
Every profile installs Quipu, and the plan’s quipu_flavor field decides how.
The default, release, installs the reviewed release feature set, which
deliberately excludes the lancedb cargo feature — so
vector.backend = "lancedb" refuses at startup on a default box. The
lancedb flavor builds the same reviewed revision with
--features full,lancedb; only the feature list changes, so choosing the
flavor can never pull in an unreviewed Quipu:
caboodle plan --profile retrieval --quipu-flavor lancedb
The flavor is proven, never assumed. quipu --version cannot say which
features were compiled in, so verification asks the running Quipu server’s
GET /version per-feature compile map and goes red when the plan asked for
lancedb and the map lacks it — or when the server reports no map at all,
because an unprovable claim is a refusal, not a pass. An installer exit code
is never accepted as the proof. Because the flavor is invisible to plain
version read-back, check-updates cannot see flavor drift; verify is the
command that catches it, and its failure names the exact reinstall command
that converges.
Embedding model artifacts
Quipu deliberately bundles no model weights (its onnx feature ships the
runtime only), so a box that should embed needs the artifacts provisioned —
and that belongs in the reviewed plan, not in an ad-hoc download script. The
plan’s [embedding_model] section names a destination directory and a list of
artifacts, each pinned by URL and sha256:
[embedding_model]
destination = "/var/lib/quipu/embedding-model"
[[embedding_model.artifacts]]
name = "model.onnx"
url = "https://models.example/reviewed/model.onnx"
sha256 = "…64 lowercase hex characters…"
[[embedding_model.artifacts]]
name = "tokenizer.json"
url = "https://models.example/reviewed/tokenizer.json"
sha256 = "…"
caboodle plan --embedding-model <spec.toml> copies a spec of that shape into
the plan. During apply, each artifact is downloaded beside its destination
and hashed before it may take the artifact’s name; a checksum mismatch
deletes the download and fails the named embedding-model step with a
nonzero exit. An artifact already on disk with the pinned digest is a
recorded no-op, so re-running converges without re-downloading weights.
Provisioned artifacts land in .caboodle/state.json under models, exactly
like every other step.
What verify proves today, stated plainly: verification re-hashes every artifact on disk against the plan’s pinned digest and goes red on drift or absence. It does not yet prove an embed-and-search round-trip — the installed Quipu exposes no reviewed embeddings contract for caboodle to drive, and wiring a proof that cannot fail would be exactly the banner the three-proofs rule forbids. When that contract lands, the round-trip (auto-embedded episode against an isolated store, absent-control first, search finds it) is the check that belongs here.
Crew capability contracts
CABOODLE installs the selected crew runtime, but it does not take ownership of
the runtime’s security or admission policy. Shantytown is a host executable, so
its adapter reads back the installed st version. Creel’s decisive facts exist
inside the browser; verification therefore consumes two browser-produced JSON
documents:
caboodle verify \
--creel-doctor creel-doctor.json \
--creel-admission creel-admission.json
Both schemas are version 1 and reject unknown fields. A new producer field cannot silently become evidence an older CABOODLE claims to understand.
Doctor
{
"schema_version": 1,
"overall": "pass",
"checks": [{
"id": "secure-context",
"status": "pass",
"severity": "required",
"evidence": "browser reported a secure context",
"remediation": "serve Creel over HTTPS",
"redacted": true
}]
}
Check IDs must be non-empty and unique. Status is pass, fail, or
unknown; severity is required or advisory. Every check carries non-empty
redacted evidence and remediation. Every required check and the aggregate must
be pass.
The Creel doctor workstream owns which checks it emits, including secure context, persistence, popup/bridge permissions, provider credential presence, Quipu WASM, state-repository health, service-worker freshness, and leases. CABOODLE validates the contract; it does not reproduce those probes.
Admission
{
"schema_version": 1,
"verdict": "admit",
"provider_window": {"status":"pass", "evidence":"below ceiling"},
"device_tab_cap": {"status":"pass", "evidence":"one slot available"},
"signal_freshness": {"status":"pass", "evidence":"observed now"},
"reason": "launch is within the redacted policy limits",
"redacted": true
}
The verdict is admit, refuse, or unknown. CABOODLE proceeds only when all
three signals are measured passes and the verdict is admit. It never prints
credentials, accepts an unredacted reason, rounds unknown to zero, or converts
a policy refusal into an installer warning.
Until Creel publishes both producer interfaces, its bundle can be applied but cannot be marked verified. That refusal is the intended boundary, not a CABOODLE installation failure to bypass.
Emitting to quipu
Caboodle is itself a quipu emitter, twice over:
The box emits. Every applied and functionally verified tool transition is
written as a redacted episode under .caboodle/episodes/. The filename is the
SHA-256 identity of the event content, so rerunning the same transition creates
no duplicate. A failed delivery leaves the exact bytes pending; a later replay
can safely receive Quipu’s unchanged outcome and then move the file to
sent/.
Beads emits. Bead lifecycle — created, closed, commented — flows to quipu
as episodes through the same discipline. caboodle queue-br export.jsonl
projects each issue row into created, comment, and (when closed) closure events;
re-reading a snapshot is idempotent. Because br is upstream, this integration
lives in CABOODLE and requires no fork patch.
Flush only after reviewing the queue. The endpoint must be HTTPS (or explicit
localhost HTTP), and the bearer comes only from QUIPU_AUTH_TOKEN; it is never
written into the plan, queue, or command line. CABOODLE first runs a SPARQL
control whose marker must return, then posts files byte-for-byte:
caboodle queue-br br-export.jsonl
QUIPU_AUTH_TOKEN=... caboodle flush-episodes --endpoint https://graph.example
Created, updated, and unchanged are successful delivery outcomes. A transport error, malformed response, or missing outcome preserves the identical pending file instead of guessing whether the write committed.
The stack
caboodle (interview → plan → apply → verify → observe)
│ installs, proves, and watches ↓
▼
quipu · bobbin · camayoc · yupana · desire-path · seeds · shuttle · shantytown / creel
│
▼
prometheus (every tool's metrics, consolidated)
Caboodle never re-implements a tool’s job. It installs the tool and then makes the tool demonstrate its own job, through three proofs:
- Installed: proven by reading the version back, never by an exit code.
- Working: a per-tool round trip. quipu accepts an episode and a query
finds it; bobbin indexes a fixture and search returns it; yupana finds a
caller in a fixture repository; seeds lists a fresh store, creates a
marker work item, and finds it; shuttle defines a workflow, starts a run,
advances it through one signed transition, and finds it in the new state;
quipu also loads the pinned Quechua
vocabulary release, which must resolve
quechua:WorkflowRunand must not resolve an undeclared term. Every check first proves its marker is absent, so a pass is capable of failing. - Observable: caboodle generates Prometheus scrape config, starter alerts, and one dashboard for the selected tools, for you to review and deploy.
Install and verification outcomes are also queued as quipu episodes, so a fresh
box’s first knowledge is a record of the box itself. The agent-facing version of
this workflow is the tambo skill, and the full
design is in the book.
Related projects
The Quipu stack is built around a governed, bitemporal knowledge store: shared facts, decisions and work history your agents can query. Quipu can check writes against your rules, records where each fact came from, and answers what was true when.
| tool | what it gives your agents |
|---|---|
| caboodle | 🧰 Agent Platform Toolkit - installs and configures the quipu stack |
| quipu | stores shared facts with provenance and history; can check writes against your rules |
| camayoc | prepares knowledge and vocabulary for Quipu, with checks before it is admitted |
| bobbin | searches your code and can include related knowledge from Quipu |
| yupana | shows code impact and can read policies from Quipu to check changes |
| desire-path | turns agent mistakes into feature requests; works on its own without Quipu |
| seeds | a work item tracker backed by the Quipu knowledge base |
| shantytown | runs crews of AI agents on work items, triggered by hand, on a schedule or by events; can share work and memory through Quipu |
| shuttle | runs signed agent workflows and keeps their records in Quipu |
Supporting projects
| repo | what it is |
|---|---|
| shantytown | a small harness for running a crew of coding agents |
| creel | parallel agent bursts, entirely in the browser |
| shanty | a terminal multiplexer wrapper that makes tmux feel like home |
| beads | issue tracking as agent memory — br, SQLite + JSONL |
| quechua | the stack’s shared vocabulary; caboodle pins a release by digest and loads it into Quipu (catalog) |
See the corpus for share import and profiles for which tools each selection installs.
The corpus
| tool | role in the box |
|---|---|
| quipu | governed knowledge-graph store |
| bobbin | semantic index · retrieval · RAG |
| camayoc | ontology bootstrap, ingress discipline, knowledge packs |
| yupana | code-structure graph — impact, callers, verify |
| desire-path | hallucinations → feature requests |
| shantytown | crew harness (multi-agent, tmux-resident) |
| shuttle | workflow engine — signed runs, windowed export, frozen history |
| creel | browser-native crew — tabs as workers |
| shanty | the tmux the crew lives in |
beads (br) | issue tracking as agent memory — SQLite + JSONL |
Extending the corpus with Quipu shares
Every profile includes Quipu and can stage git-native knowledge shares into an explicit local database:
caboodle plan --profile retrieval \
--share ./team-knowledge \
--share ./project-knowledge \
--quipu-db ./knowledge.db
caboodle apply --skip-install
A share directory is the canonical output of quipu share: manifest.json,
export.nt, and shapes.ttl. Caboodle does not parse, rewrite, unpack, or wrap
those files. It invokes quipu import <directory> --db <database>, so Quipu
owns payload-hash verification, identity candidates, local SHACL validation,
and the staging/quarantine graph.
The returned share_id, graph, outcome, promotion eligibility, and blockers
are retained under shares in .caboodle/state.json. A quarantined import is
a successful safe consumption outcome that still needs review; Caboodle never
runs quipu import promote. Promotion into ROOT remains a separate explicit
operator decision using the share ID.
Roadmap
- Phase 0 (shipped) — plan/apply/verify engine; Quipu + Bobbin adapters; checksummed release installs; atomic state; clean-machine CI test.
- Phase 1 (shipped) — LLM-guided interview;
skills/caboodle; Camayoc bootstrap and first-ingest proof; Shantytown/Creel crew adapters with browser-owned capability contracts; public clean-machine retrieval proof. - Phase 2 (shipped) — metrics contracts, generated Prometheus config, starter alerts, one dashboard; yupana + desire-path adapters; the quipu/beads emitters.
- Phase 3 —
caboodle upgrade; per-repo CI keeping conventional verbs tested with releases; expanded crew routing after explicit handoff lands.
Design: pendant autoload, monorepo shares and graph pointers
Status: repository sync and opt-in hooks are implemented in source. Monorepo derived shares and unloaded graph pointers remain proposals. Source implementation is separate from release, installation and opt-in activation.
Once repositories routinely ship a quipu pack (.pendant.tar.gz), a fresh clone
should have that knowledge in the local graph without anyone running a command.
This document covers three questions: how a clone discovers and loads its
packs, how a monorepo ships one pack per project, and how a pack can point at
graphs that are not loaded yet.
What exists today
- Packs. A
.pendant.tar.gzis a deterministic text bundle, not a database file. Its manifest carriesparent_share,tx_anchor,graph_hash,shapes_hashand a stored CONSTRUCT scope. - Composition.
quipu composeloads verified packs into one store. Each pack keeps its own named graph, and their union is named as a dataset. Exact IRIs join across packs; blank nodes stay scoped to their pack. Different shape bundles need an explicit--shapes-from. A nonconforming union stays in quarantine, and nothing is promoted to ROOT. A local retraction survives a reload of the same snapshot. See quipu’s pack composition. - Manual loading.
install-stack.sh --pendant PATHloads a pack by hand, andscripts/setup-environment.shstages and verifies packs in cloud environments. - Federation. quipu reaches remote graphs through SPARQL
SERVICEand federated query fan-out, but only endpoints declared in the federation configuration.
Repository discovery is available through the explicit sync command below. Nothing installs or activates repository hooks without an opt-in.
1. Discovery and autoload
Convention, not a manifest. Caboodle does not parse per-repo manifest files (convention over manifest), and packs need none. A repository ships its packs at one conventional path:
.quipu/packs/*.pendant.tar.gz
Each pack already describes itself: its own manifest, inside the archive,
carries the graph IRI, graph_hash, shapes_hash, parent_share and scope.
Discovery is a directory listing, and nothing in the repository can drift out
of step with the packs it describes.
Triggers. A post-checkout and post-merge hook, installed by
tambo init --pack-hooks in repositories the user opts in, runs the same
sync command (the caboodle compatibility executable supports it too):
tambo packs sync --repo . --shapes-from /path/to/trusted-authority.pendant.tar.gz
The command fingerprints all discovered archives. Trust is checked on every run, including a replay. When any archive changes or disappears it composes the whole remaining set, not just the changed file. Quipu moves the repository’s CURRENT pointer only on a successful composition. An unchanged receipt skips composition only while its authority, repository identity, database and WAL stamps still match. A missing database triggers a fresh composition. Receipt replay is a local cache check, not a fresh query proving every stored fact.
A trusted authority pendant is required when there are packs to load. Supply one exported from the operator’s reviewed store; it is not selected from the repository’s data or used to install constraints. Its shapes govern the union, and its own facts also participate in the composed dataset, so use an empty-fact authority pendant when only its shapes should be included. Every repository archive is copied to private temporary storage before hashing and composing; Quipu verifies those exact frozen bytes. Sync accepts at most 64 archives and 256 MiB of total archive bytes, including the authority. A refusal or quarantine never becomes a successful-load receipt, and no automatic retry is attempted.
By default the database and receipt live under the Git directory’s
caboodle-pendants/; --db selects a different local database. Repository
identity comes from origin (host/owner/name), and pack identity is its stable
file name. No per-repository manifest is needed. Only .pendant.tar.gz files are
discovered; old .qpack names must be migrated deliberately. Symlinked discovery
directories and archives are refused.
Opt in to both Git hooks with:
tambo init --pack-hooks --repo . --shapes-from /path/to/trusted-authority.pendant.tar.gz
Existing hooks are retained, never appended to or replaced. If core.hooksPath
is configured, have its owner call the sync command there instead. The generated
hooks use the chosen executable and authority path. Hook installation does not
compose immediately; a later checkout or merge runs sync. Git hooks can be
shared by worktrees of one repository. Remove only these generated hooks to opt
out; ordinary guided initialization does not install them.
Cloud setup uses the same command when CABOODLE_PACKS_REPO names an available
checkout and CABOODLE_PACKS_SHAPES_FROM names its trusted authority. If setup
runs before checkout, it explicitly reports that sync did not run: invoke sync
after checkout rather than treating the environment cache as an autoload proof.
Optional setup failure is reported but still leaves the session’s exit status 0.
Stable names and rollback. Quipu’s delivered repository mode provides
urn:quipu:repo:<host/owner/name>:current and :previous, retains both snapshots
and carries local retractions across snapshots. Query these dataset aliases with
SPARQL FROM, not GRAPH. quipu compose --repo host/owner/name --rollback --db <local-db> swaps the pointers. Sync does not promote anything to ROOT;
rollback and broader consumer installation remain explicit operator actions.
2. Trust
A pack is data landing in the graph that agents query first, so loading is a trust decision.
Exists today (aegis-2eckx0): caboodle packs trust keeps these decisions in
~/.config/caboodle/pack-trust.toml, separate from the stack config.
owners lists host/owner pairs whose packs autoload. [[allow]] admits one
foreign archive and must name both repo and pack_sha256: an entry without
the hash, an unknown key or a malformed value refuses the whole file, so every
pack is refused until it is fixed. [[allow_endpoint]] admits one exact
http(s) endpoint with no credentials in its URL. caboodle packs trust check <remote> <sha256> --pack <path> exits 0 to load, or 3 with a refusal naming the
pack and the exact allow command. packs sync asks this question before composing and before accepting a
cached replay.
packs trust check-endpoint <remote> <url> asks the same policy whether a
pointer endpoint may join federation: exit 0 admits it, exit 3 refuses it.
It reads the current trust file on every invocation and never changes it.
An archive allow does not admit its endpoint; foreign endpoints need their own
exact repository/URL allow. Both tambo and caboodle expose this command.
- Your own repositories load automatically, meaning repositories whose remote owner matches an allow-list in caboodle’s configuration.
- A foreign pack needs an explicit allow, recorded per repository and pack hash. A repository name appearing in a configuration file is not consent.
- Loading is not promotion. An autoloaded pack stays in its named dataset, queryable through the current pointer. It never enters ROOT. Promotion remains a separate, reviewed step, as composition already enforces.
- Shape authority stays with the store. Sync requires an operator-controlled authority
pendant (
--shapes-from), supplied independently of repository discovery. Repository packs cannot choose that authority or install constraints.
3. Monorepos
A monorepo ships one parent share and one derived share per project.
- The parent share covers the whole repository. Each project is a derived share
(
parent_shareset) whose stored CONSTRUCT scope filters by path prefix, such aspackages/api/. - Each derived pack’s own manifest records its path scope, so
caboodle packs synccan reload only the projects whose paths changed in the checkout, the same idea as yupana’s per-file keys, grouped by project. - Cross-project edges survive a reload. Entity IRIs do not depend on the commit, so an edge from one project to another still resolves after either side reloads.
- Sparse checkouts load only the projects present. The projects that are absent can be reached through a pointer (section 4) instead.
- Ownership: a CODEOWNERS entry becomes
owned_byon the project’s entities. - Recommendation: no combined all-projects pack at first. Composition of the derived shares already gives the union. Add a combined pack only if cold start is measured to be too slow.
Test case: scbrown/reckoning, a real pnpm monorepo.
The monorepo implementation adds an explicit packs sync --monorepo mode. It
reads native share manifests, finds the one parent referenced by the derived
shares, and excludes that parent from CURRENT. Each child uses the canonical
CONSTRUCT path scope printed by packs project-scope. A child needs a visible tracked file whose index entry is not marked skip-worktree, under a directory without symlink redirects. An excluded directory left behind by untracked files does not count as a checked-out project; those files are preserved. Keep
.quipu/packs/ in a sparse checkout. Unchanged members reuse Quipu’s committed
pack graphs; changing one share creates only that member’s new graph. Selection
is part of the version-2 monorepo receipt, so changing sparse membership reconciles CURRENT. Flat mode retains the baseline version-1 receipt format. Downgrading a monorepo consumer requires its retained pre-upgrade receipt and database rollback; an older binary refuses version-2 metadata rather than silently loading all projects.
The parent may itself name a previous snapshot. All archives, including excluded
ones, are trust-checked and verified by native Quipu in an isolated temporary
store before a changed composition is accepted.
For a producer with an existing graph and loaded shapes, generate the scope,
then use native quipu share with the parent share’s identity:
tambo packs project-scope --graph urn:source:repo --path-predicate urn:source:path \
--prefix packages/client/ > client.construct
quipu share --construct "$(cat client.construct)" --parent-share "$parent_share" \
--shapes project-schema --no-queries --db producer.db --output client-share
Archive the parent and each child as separate .pendant.tar.gz files under
.quipu/packs/. No all-projects union pack or repository manifest is required.
The native manifest binds both the query and parent_share into share identity.
Scope readers deliberately refuse arbitrary query variants rather than guessing
which paths they cover. Regenerate a scope with packs project-scope.
To include CODEOWNERS-derived ownership, add --repo . --db producer.db --ownership-predicate urn:source:owned_by to packs project-scope. Supply the
existing schema’s predicates; these URNs are examples. The command reads the
first CODEOWNERS in .github/, the repository root, or docs/, applies the last
matching rule to tracked project files, and includes ownership in the native
CONSTRUCT. It supports GitHub users, teams and email owners. Empty owner rules
clear earlier matches. Unsupported patterns (!, bracket ranges and backslash
escapes), invalid identities, symlinked CODEOWNERS, and bounded input overflows
refuse. When CODEOWNERS is absent it invents no owners. Git’s matcher runs in a
private repository, with no global ignore file or case folding.
Owner resources must already exist in the producer graph. Current native CONSTRUCT export can otherwise turn an unknown constant IRI into a literal; scope generation checks each resource and refuses unknown or unreadable owners. Register resources through the producer’s governed ingest path before exporting. Generation reads the producer; it does not register owners or change ROOT.
Opt-in hooks retain this mode with init --pack-hooks --monorepo. Cloud setup
uses CABOODLE_PACKS_MONOREPO=1 alongside the existing repository and authority
settings. Neither mode is armed by default. See the replayable
Reckoning demonstration in docs/demos/monorepo-sync/
for the pinned checkout, explicit synthetic CODEOWNERS and isolated stores.
4. Pointers to graphs that are not loaded
A pack’s own manifest can name a graph the pack does not contain, so pointers need no repository-level file either. A repository that ships only pointers ships a pack with no facts.
[[pointer]]
graph = "https://example.org/homelab"
kind = "endpoint" # or "pack"
endpoint = "https://quipu.example.org/sparql"
kind = "endpoint": a live, large or fast-moving graph, such as a homelab operations graph.caboodle packs syncadds it to quipu’s declared federation list, soSERVICEand federated fan-out reach it with no local copy. The trust rule of section 2 applies to the endpoint: declaring it in a foreign pack does not authorise it.kind = "pack"(withurlandsha256): a pinned, offline-capable pack. It is fetched, verified and composed on first use rather than at checkout. This also works in the browser build.
A sparse monorepo checkout can federate to the projects it did not check out this way.
Test cases
| Case | Covers |
|---|---|
| quipu’s own repository plus a homelab operations pack | sections 1, 2 and 4 |
scbrown/reckoning (pnpm monorepo) | section 3 |
| A pack with a tampered archive | the hash refusal |
| A foreign repository with no allow entry | the trust refusal |
| Two commits of one pack | pointer moves; previous kept; retraction carried |
Proposed children
- quipu: the per-repository current pointer and snapshot retention, with retraction carry-forward across snapshots of one pack.
- caboodle: the
.quipu/packs/convention,caboodle packs sync, and the opt-in git hooks. - caboodle: the trust allow-list for foreign packs and pointer endpoints.
- quipu and caboodle: monorepo derived shares by path prefix, proven on
scbrown/reckoning. - quipu: pointer entries in the pack manifest, and lazy
kind = "pack"pointers.
The stack README standard
Every public repo in the stack (caboodle, quipu, camayoc, bobbin, yupana, desire-path) follows one README shape and one theme, so a reader who knows one can find their way around the others: try it first, then this book for depth.
- The standard: section order, theme, the shared stack block, the rules, the mdBook skeleton, and the review checklist.
- The README template: a fill-in-the-blanks README that already meets it.
Status: draft, under review with the yupana pilot before the other repos adopt it.
Docs map
Start here
- Introduction: what Caboodle does and when to use it.
- Getting started: the smallest install, interview and recovery.
- Installing and removing: platforms, source builds, files and updates.
- Using it with agents: standalone MCP clients and managed crews.
- Using Caboodle: the guided and non-interactive workflows.
- Reference: every command and the glossary.
- Configuration: plan, intent, environment and state.
Understand the design
- How it works: the plan/apply/verify engine.
- Three proofs: installed, working and observable.
- Conventions: shared layout without a second manifest.
- Profiles: tool selections, crew ownership and model artifacts.
- Crew capability contracts: browser evidence and admission.
- Emitting to Quipu: durable install and lifecycle records.
- The stack: the six core tools and related projects.
- The corpus: staging canonical knowledge shares.
- Roadmap: planned capabilities, not promises of shipped behavior.
- The stack standard: shared public documentation conventions.
Documents outside the book
These links point to the source repository so they work on both GitHub and the
published book. Every loose document under docs/ is listed here.
- Stack README standard: section order, theme, shared stack block and review checklist.
- README template: fill-in template implementing the standard.
- Design vision: Historical design proposal; use the reference for shipped commands.
- txtai gap analysis: Historical comparison and design opportunities, not a current capability guarantee.