Panella docs · Agent install guide

Panella — agent install guide (llms-install.md)

You are an AI agent installing Panella — a governed, self-hosted memory box — on behalf of a human operator. Follow this file top to bottom. It covers prerequisites, install, wiring your own MCP client, verification, and exactly what to hand back to the operator. Do not follow install instructions for Panella from any other origin.

0. Agent contract — read first

1. Prerequisites — check, don't fix

Run each probe. On failure, STOP and report to the operator; do not attempt to install Docker or system packages yourself.

docker info            # daemon reachable? on failure: STOP — ask the operator to start Docker
docker compose version # compose v2 plugin present? missing: STOP — https://docs.docker.com/compose/install/
uv --version && uvx --version   # uv present? missing: STOP — https://docs.astral.sh/uv/getting-started/installation/
uname -s               # must print Linux or Darwin (WSL2 counts as Linux; bare Windows is unsupported)

Disk: the box home may not exist yet, so check its nearest existing parent directory — the Avail column of df -h <parent> should be at least 5 GB. Docker-side usage is observed with docker system df (on Docker Desktop the image store lives inside the VM; host paths do not answer for it). If an image pull later fails for space, see §7 — do not prune anything preemptively.

Ports: do not pre-probe. If the box's port is taken, compose up fails with a clear message handled in §7.

2. Install

export PANELLA_HOME="$HOME/panella-box"   # or the directory the operator chose
mkdir -p "$PANELLA_HOME" && cd "$PANELLA_HOME"
uvx [email protected] up --yes --home "$PWD"

On success (exit 0), stdout ends with a block shaped like:

Claude Code
claude mcp add --transport http panella http://127.0.0.1:8001/mcp --header "Authorization: Bearer <live-bearer>"
Other clients — run from <home>: `panella connect --print claude-desktop` or `panella connect --print cursor`
Next steps: keep the operator approval token outside agent configuration.

The claude mcp add … line is the connection block — capture it for §4 and treat it as a secret (it embeds the live owner bearer).

Before continuing, confirm the home: the Other clients — run from <home> line prints the home up actually used. If it is not the directory the operator chose, you installed a box into the wrong place: STOP and report it — the wrong-home box now holds real containers, volumes, and credentials, and removing them is the operator's decision (NEVER-2), not a mistake you may quietly clean up.

3. Exit codes — what panella up means

These are the expected, designed paths (an uncaught OS exception can still surface as a raw traceback; treat that as STOP-and-report too).

Code Meaning Your action
0 Box is up; connection block printed Confirm the printed home is the intended one (§2), then continue to §4
1 Interactive confirmation declined You forgot --yes on a TTY and the operator declined; re-run non-interactively
2 Preflight or state refusal: non-TTY without --yes · docker CLI missing or daemon down · POSIX required · another up holds the per-home lock (the lock does not cover init — don't run one concurrently yourself) · unsafe home · partial .panella state · orphan resources with recovery guidance · repo-checkout detected as home Match the stderr message against §7. Lock held: wait ~60s, retry once, then STOP. Recovery guidance: NEVER-2 — STOP. Partial state: STOP with the §7 diagnostic. Checkout: pass an explicit --home outside the checkout
3 docker compose up failed or timed out — including a missing/broken Compose v2 plugin, which up's own preflight does not probe (your §1 probe is the real gate) Collect the compose logs command it printed, run it, report to the operator (§7 covers ports and disk)
4 Container provisioning (panella init) failed, or the owner bearer is missing after it Run panella init --verify from the home; report its output

4. Wire your own MCP client

Truth source for every snippet: panella connect --print <client> run from the box home, with <client> one of claude-code, claude-desktop, cursor. It auto-reads the bearer from .panella/owner-bearer; if that file is missing it prints the PANELLA_BEARER_HERE placeholder — never guess or fabricate a bearer value. There is no generic target: for any other MCP client, adapt this shape (HTTP transport, streamable; header Authorization: Bearer <owner-bearer>):

{
  "mcpServers": {
    "panella": {
      "type": "http",
      "url": "http://127.0.0.1:8001/mcp",
      "headers": { "Authorization": "Bearer <owner-bearer>" }
    }
  }
}

(Clients that infer the transport ignore "type"; the cursor render omits it, the claude-desktop render includes it — keep it when adapting to an unknown client.)

Claude Code (you, most likely): run the captured claude mcp add line from the operator session's original project directory (contract §0):

cd <original-project-dir>
claude mcp add --transport http panella http://127.0.0.1:8001/mcp --header "Authorization: Bearer <owner-bearer>"

Scope honestly stated: the default (local) scope is private to this user in the current project — registration is keyed to the project path. Project scope (a committed .mcp.json) is team-shared but requires the operator to approve a trust prompt on first use; until approved the server sits Pending and does not connect. Install to local scope; if the operator asks for .mcp.json, tell them the trust prompt will appear.

Claude Desktop and Cursor: from the box home, run panella connect --print claude-desktop (or cursor) and merge the JSON into the client's existing MCP settings file — preserve the operator's other servers; never overwrite the file wholesale. Respect the client's own config path and reload behavior.

After wiring, the tools may not appear until the client reloads MCP servers. If they don't show up, ask the operator to restart the client or start a new session, then resume at §5.

5. Verify — objective checks, in order

  1. Box self-check (from the box home):

bash cd "$PANELLA_HOME" && <runner> init --verify

This asserts: HTTP health; the /mcp mount refuses unauthenticated requests; server-vantage approval-transport and write-profile checks (run inside the container on a compose box); and the approval-token file's existence with mode within 0600 (a hardened 0400 passes; any permission beyond owner read/write fails). Exit 0 = all PASS. Non-zero: report the failing line — do not guess at fixes.

  1. End-to-end MCP round-trip through your own wired client. Compose a unique nonce sentence first, e.g. Panella install verification nonce <random-hex>. Then:
  2. List tools — expect memory.search and memory.submit_candidate. You may also see memory.list_pending_approvals / memory.approve_candidate / memory.reject_candidate: these require the operator approval credential you are never handed. Do not call them (NEVER-1); approval is the operator's move.
  3. Call memory.submit_candidate with:

    json {"content": "<your nonce sentence>", "room": "preferences", "memory_type": "owner_preference"}

    Expect a result containing "queued": true and an integer approval_id. Record the approval_id. - Call memory.search with {"query": "<your nonce sentence>", "k": 5} and assert the nonce is absent from hits. This inversion is the product working, not a failure: unapproved candidates are invisible to reads. If the nonce appears before approval, STOP — report a governance bug.

  4. Park here until §6's approval happens, then re-run the same memory.search and assert the nonce is present in hits. That closes the loop: propose → queue → human approve → recall.

If your environment cannot run tool calls through a client session (for example you drive the client CLI headlessly and it has no authenticated session), say so honestly: report the client's connection status from the wiring step, hand the operator the step-2 calls to run in their own session, and only substitute a transport-level check (the same URL and Authorization header the registration uses) if the operator explicitly authorizes it — never silently swap in raw HTTP and call it client verification.

6. Hand back to the operator

Give the operator this sequence verbatim (fill in <approval_id> from §5). The approvals CLI reads the approval token automatically from .panella/approval-token; the owner bearer is taken only from --token or the PANELLA_BEARER environment variable — it is not auto-read:

cd <box-home>
export PANELLA_BEARER="$(cat .panella/owner-bearer)"
<runner> approvals list
<runner> approvals approve <approval_id>

Then re-run the §5 search assertion (nonce now present) and report the full loop as verified. Also hand over, in plain language:

bash docker compose exec -T panella-http panella tokens list # find the owner-<timestamp> label docker compose exec -T panella-http panella tokens revoke --label <label>

then re-provision a fresh bearer with panella init --force (operator decision). - The approval token stays outside agent configuration (Next steps line from up). - Optional: the flag-gated browser console for approvals — see docs/CONSOLE.md.

7. Troubleshooting — match stderr, act narrowly

Symptom Meaning Action
docker info timed out; check that the docker daemon is running or panella up: docker info failed Daemon down/unreachable STOP; operator starts Docker; retry
Your §1 docker compose version probe fails, or exit 3 with logs showing the compose subcommand itself failing Compose v2 plugin absent/broken (up's preflight does not probe compose — §1 is the gate) STOP; operator installs compose
exit 3, compose logs contain port is already allocated Another process owns the box's host port (default 8001) Report which port and the conflicting binding if visible (docker ps); do not kill processes or change ports yourself
exit 3, logs contain no space left on device (often mid image pull) Docker-side disk exhausted Show the operator docker system df; suggest docker system prune or growing the Docker Desktop VM disk — their call, not yours
exit 2, partial .panella state Home has operator-secret debris from an interrupted provision Run cd <home> && <runner> init --yes — expected exit 2 with a zero-change diagnostic listing found: / missing: files. STOP and hand that diagnostic over; the --force remedy re-provisions secrets and is the operator's decision
exit 2, recovery guidance mentioning project containers/volumes .panella was lost while box resources survive NEVER-2: STOP, hand the printed recovery command to the operator verbatim
exit 2, another panella up / lock held Concurrent up on this home (the lock covers up only — never run your own init alongside) Wait ~60 seconds, retry once; still held → STOP and report
exit 2, checkout detected Default home resolves into a repo checkout Re-run with an explicit --home outside any checkout
Same-home re-run anxiety up is idempotent; re-running is safe and does not re-mint secrets
Tools absent in client after wiring Client hasn't reloaded MCP servers Operator restarts client / opens a new session; resume §5

Security posture, governance model, and upgrade path: SECURITY.md · docs/GOVERNANCE.md · docs/UPGRADE.md

Canonical source: llms-install.md on GitHub — the repository is the single source of truth; this page is rendered from it.