# Installing and removing Tambo
Tambo installs and verifies the stack from checksummed release archives. See
[Compatibility](compatibility.md) for retained command and state contracts.
Tambo 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.
Tambo'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 Tambo 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:
```bash
Pin a release by setting the version on the receiving shell:
```bash
The script still verifies the release archive's SHA256 checksum before replacing
the binary. Read the [installer source](https://github.com/intigraph/tambo/blob/main/scripts/install.sh)
when inspecting the bootstrap. After installation, `tambo --version` reads
back the installed release.
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.
`tambo --version` prints the release version and the commit it was built
from, for example `tambo 0.4.0 (1a2b3c4d5e6f)`. Two builds of one release
can then be told apart. The installer warns when a different `tambo` comes
earlier on PATH than the one it just installed, and `tambo 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:
```bash
cargo install --git https://github.com/intigraph/tambo \
--rev <reviewed-commit-sha> --locked
tambo --version
```
## Preflight
`tambo 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`, `go` for Desire Path, `cargo`
for 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:
```bash
tambo init --guided
less caboodle-plan.toml
tambo install
tambo 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](profiles.md): `--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
Tambo 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:
```bash
tambo project-settings --root /path/to/.shanty
# Optional: select one crew member or read identities directly from Quipu.
tambo 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; Tambo
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:
```bash
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 Tambo. 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), Tambo 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 Tambo's own job, and a session that needs the full stack runs
`tambo 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; Tambo 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.
```bash
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:
```bash
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:
```bash
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 Tambo binary and optional local Tambo state. Installed
corpus tools are intentionally left in place because they may be shared with
other workflows:
```bash
rm "${CARGO_HOME:-$HOME/.cargo}/bin/caboodle"
# Optional, from the directory where Tambo was run:
rm -r .caboodle
```
## Reviewed-pin drift
`tambo 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.
`tambo 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.
```bash
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
tambo apply --plan caboodle-plan.toml --state .caboodle/state.json
tambo 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:
```bash
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 Tambo build's reviewed pins to change:
```bash
tambo update-release --tool bobbin --check
tambo update-release --tool bobbin
tambo update-release --tool yupana
tambo 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.
`tambo update-self` applies the same release, checksum, downgrade, backup and
interruption-recovery controls to Tambo 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. Tambo 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/bin` is 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.toml` and `.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 Tambo, delete `~/.cargo/bin/caboodle` and the `.caboodle/`
directory. Installed tools stay, because other workflows may use them. The
[install guide](installing.md) 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; `tambo doctor`
prints the exact source-build command where no reviewed release exists.
| Tambo | 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.16.2 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.** The default local single-user install needs neither
an issued token nor a running server. Explicit `QUIPU_SERVER` or `quipu_mcp`
configuration opts into authenticated remote verification and requires an
accepted issued credential. Sending install records with `flush-episodes`
also requires remote authorization.
## 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:
```bash
tambo 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:
```bash
tambo verify-member seeds
```
This stages the archive pinned by that Tambo 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.