---
title: Upgrading Pinchy
description: How to update Pinchy to a new version safely.
---

import { Aside, Steps } from "@astrojs/starlight/components";

Pinchy handles most upgrade steps automatically — database migrations run on startup, and the OpenClaw configuration is regenerated from your current settings. A typical upgrade takes under a minute.

## Standard upgrade

<Aside type="note">
  This is the upgrade flow **between v0.5.0+ releases**. If you're still on
  v0.4.x, complete the **Upgrading from v0.4.4 to v0.5.0** section below first —
  that one-time migration adds the `PINCHY_VERSION` pin you'll bump from here
  on. After it, return to this section and bump `PINCHY_VERSION` straight to the
  latest tag.
</Aside>

<Steps>

1. **Back up your database**

   Before any upgrade, create a database backup. This lets you roll back if anything goes wrong.

   ```bash
   mkdir -p backups
   docker compose exec -T db pg_dump -U pinchy pinchy > backups/pinchy-$(date +%Y%m%d-%H%M%S).sql
   ```

   Keeping backups under `backups/` instead of loose in `/opt/pinchy/` means they don't sit next to your `docker-compose.yml` / `.env`, and the directory is easy to target with your off-host backup rotation.

2. **Bump the version pin**

   Edit `/opt/pinchy/.env` and update `PINCHY_VERSION` to the new release tag (e.g. `PINCHY_VERSION=%%PINCHY_VERSION%%`), then pull the matching images:

   ```bash
   cd /opt/pinchy
   docker compose pull
   ```

   <Aside type="tip">
     Release notes will tell you if `docker-compose.yml` itself needs to be
     re-fetched — that only happens when a release adds new services or volumes,
     which is rare.
   </Aside>

3. **Restart and prune the old image layer**

   ```bash
   docker compose up -d && docker image prune -f
   ```

   `up -d` applies any pending database migrations, regenerates the OpenClaw configuration, and restarts all services. `docker image prune -f` then removes the previous image layer for each Pinchy service that `pull` left dangling — without this, every upgrade keeps the prior `pinchy` and `pinchy-openclaw` layers on disk and a long-running deployment eventually fills its root volume.

   <Aside type="tip">
     `prune -f` removes layers that no tag and no container references —
     typically the prior digest of the tag you just replaced, plus any older
     dangling layers left over from previous upgrades. It's safe to run on a
     live deployment because the running container still references the new
     image. On a host that runs other Docker workloads alongside Pinchy, this
     also clears their dangling layers — that's almost always what you want, but
     worth knowing if you share the daemon. If you've been upgrading for a while
     and want to reclaim space from **older pinned versions** as well (e.g.
     `v0.5.0` you no longer plan to roll back to), run `docker image prune -a
     -f` instead. This removes every image not currently associated with a
     container, so you lose the ability to instantly roll back without
     re-pulling — schedule it between upgrades, not as part of one.
   </Aside>

4. **Verify**

   Confirm the new version is running by hitting `/api/version` — this endpoint is public and ignores Domain Lock, so it works from inside the server even when external access is locked to a specific domain:

   ```bash
   curl -s http://localhost:7777/api/version
   # {"pinchyVersion":"0.5.4","openclawVersion":"2026.5.7","build":"<git sha>","nodeEnv":"production"}
   ```

   Then open Pinchy in your browser at the URL you normally use (e.g. `https://your-domain.example`) and check the logs for any warnings:

   ```bash
   docker compose logs pinchy --tail 50
   ```

   <Aside type="tip">
     If you have Domain Lock configured, `http://localhost:7777` (the legacy
     verification URL) will return **403 Forbidden** for any page other than
     `/api/health` and `/api/version` — use your locked domain instead.
   </Aside>

</Steps>

## What happens automatically

You don't need to worry about these — Pinchy handles them on every startup:

- **Database migrations** — Drizzle ORM tracks which migrations have been applied and only runs new ones. This is idempotent and safe to run repeatedly.
- **OpenClaw configuration** — Rebuilt from your database state (agents, providers, permissions) every time Pinchy starts. Config format changes between versions are handled transparently.
- **Plugins** — Copied from the source code into the OpenClaw extensions volume during the Docker build.

## What you might need to check

- **New environment variables** — Check the release notes for any new variables. Pinchy won't crash if they're missing (features may just be disabled), but review them in your `.env` file.
- **Docker Compose changes** — Most releases reuse the existing `docker-compose.yml` and only require bumping `PINCHY_VERSION` in `.env`. When a release does need a new compose file (new services or volumes), the release notes spell out the extra `curl` step. New volumes and services are then created automatically on first start.

## Restoring from backup

If you need to roll back after an upgrade:

<Steps>

1. **Stop Pinchy**

   ```bash
   docker compose down
   ```

2. **Restore the database**

   Pick the backup you want from `backups/` (the most recent one is usually what you want):

   ```bash
   docker compose up db -d
   ls -lt backups/             # newest first
   docker compose exec -T db psql -U pinchy pinchy < backups/pinchy-YYYYMMDD-HHMMSS.sql
   ```

3. **Pull the previous version**

   Replace `vX.Y.Z` with the version you want to roll back to (check the [Releases page](https://github.com/heypinchy/pinchy/releases)):

   ```bash
   curl -fsSL https://raw.githubusercontent.com/heypinchy/pinchy/vX.Y.Z/docker-compose.yml -o docker-compose.yml
   docker compose pull
   ```

4. **Restart**

   ```bash
   docker compose up -d
   ```

</Steps>

<Aside type="caution">
  Database migrations can't be reversed automatically. If you upgrade and then
  need to go back, restore from the backup you made in step 1. This is why the
  backup step matters.
</Aside>

## Upgrading from v0.5.3 to %%PINCHY_VERSION%%

### Breaking changes

- **`odoo_schema` tool replaced** — see [Bookkeeper / Odoo schema discovery](#bookkeeper--odoo-schema-discovery-auto-migrated) below. Existing agents are auto-migrated on startup; no manual action is required.

### Upgrade notes

%%PINCHY_VERSION%% extends the Odoo template library with read-write operator agents, hardens how those agents reference Odoo records, and irons out several chat-reliability edges. It also picks up Next.js security patches and OpenClaw 2026.5.7.

Upgrade with the standard flow:

```bash
cd /opt/pinchy
sed -i 's/PINCHY_VERSION=v0.5.3/PINCHY_VERSION=%%PINCHY_VERSION%%/' .env
docker compose pull && docker compose up -d && docker image prune -f
```

No database migration, no `docker-compose.yml` change, and no env-var changes are required.

<Aside type="tip">
  **Odoo users: re-sync your schema after upgrade.** %%PINCHY_VERSION%% adds six
  new agent templates (Bookkeeper, Project Manager, HR Operator, Warehouse
  Operator, Production Operator, Approval Manager) whose `requiredModels`
  reference Odoo models that previous Pinchy versions didn't probe (e.g.
  `account.account`, `account.tax`, `res.users`, `res.currency`,
  `hr.expense.sheet`, `project.project`, `mrp.production`, `ir.attachment`).
  Existing connections still hold the old narrow schema cache, so the new
  templates' **Create** buttons stay disabled even though your Odoo instance
  exposes the models. Open **Settings → Integrations → Odoo → ⋯ → Sync now**
  once after the upgrade to refresh the cache.

This is also required for existing Bookkeeper-style agents created before
the FK-lookup fix: without `account.account` in the cache, the agent
cannot select the right ledger account when posting a vendor bill.

</Aside>

#### Bookkeeper / Odoo schema discovery (auto-migrated)

The `odoo_schema` tool has been replaced by two narrower tools: `odoo_list_models` and `odoo_describe_model`. The new `odoo_describe_model` returns a compact field map (default ~40 most-common fields per model) and accepts `fields: ["…"]`, `limit`, and `verbose: true` arguments. This cuts typical schema-discovery context cost from ~18 kB per model to ~1 kB, which unblocks vision-capable models like `ollama-cloud/gemini-3-flash-preview` for invoice-processing flows.

**No action required.** A startup migration rewrites every agent's `allowed_tools` to use the new names, and the read-write Odoo operator templates (Bookkeeper, Warehouse Operator, HR Operator, etc.) ship with updated guidance for the agent.

For existing agents created before %%PINCHY_VERSION%%, the old `odoo_schema` tool name is kept as a deprecated alias. The `AGENTS.md` files those agents store in their workspace still contain literal `odoo_schema` references — the alias keeps them working without any manual file editing, and routes the call through the new compact code path. You can safely ignore the alias; it will be removed in v0.6.x.

#### Six new Odoo agent templates

The agent picker now includes a Bookkeeper template and five read-write Odoo operator counterparts to the existing read-only Analysts:

| Analyst (read-only)   | Operator (read + create + write) |
| --------------------- | -------------------------------- |
| Project Tracker       | Project Manager                  |
| HR Analyst            | HR Operator                      |
| Inventory Scout       | Warehouse Operator               |
| Manufacturing Planner | Production Operator              |
| —                     | Approval Manager (cross-module)  |

Each operator template follows a safety pattern: mandatory user confirmation before any side-effecting write, duplicate-check on every create, no delete on any model (cancellations are write transitions, never hard deletes), and read-only access to records that belong to a different role. The new Bookkeeper template covers the "book this receipt" workflow with a draft-first, dedupe-first, one-shot-create flow.

The Approval Manager works on Odoo Community as well as Enterprise — the `approval.request` and `approval.category` models are flagged optional, so the Create button stays enabled on Community installs and the agent discovers availability at runtime via `odoo_schema`.

#### Vision capability is now standard for read-write Odoo templates

Every read-write Odoo operator template now declares the `vision` capability in its model hint. Previously this had to be set manually after agent creation, which often landed users on unstable Vision+Tools models. A new drift invariant in the test suite enforces this for any future write-capable Odoo template.

#### Hardened Odoo relation references

When an Odoo tool returns a record, related-record references are now emitted as opaque, encrypted tokens (`pinchy_ref:v1:…`) rather than raw `(id, label)` tuples. The pinchy-odoo plugin transparently resolves these tokens on subsequent calls, so an agent can no longer fabricate or substitute a numeric ID it never saw. Existing agents pick up the new behavior automatically; no configuration change is required.

#### Self-refs on `odoo_create` and `odoo_read` (`_pinchy_ref`)

Every record the plugin returns now carries a `_pinchy_ref` field — an opaque, encrypted token that identifies the record itself (alongside the raw integer `id`). `odoo_create` returns `{id, _pinchy_ref}` instead of just `{id}`, and every record returned by `odoo_read` carries one.

This closes a release-blocker gap surfaced on staging in late v0.5.3 testing: the `odoo_attach_file` tool only accepts opaque references for `targetRef`, but `odoo_create`'s old `{id}`-only response left agents no path to a valid token for a record they had just created. They would fabricate strings like `"account.move,37"`, which the plugin correctly rejected with `"Invalid integration reference"` — making `odoo_create` → `odoo_attach_file` chains (the entire Bookkeeper "book this bill and attach the PDF" flow) effectively unreachable.

The field is named `_pinchy_ref` rather than `ref` to avoid shadowing Odoo's own `ref` field, which exists on `account.move`, `account.payment`, and others as a string holding payment-reference identifiers (`"INV/2026/0001"` etc.). The underscore prefix signals "Pinchy-added, not Odoo data" so the LLM's view of actual Odoo records stays lossless.

Tools that already accept raw integer IDs (`odoo_write`, `odoo_delete`) are unchanged. Existing agents pick up the new behavior automatically; the operator-template `AGENTS.md` files have been updated to teach agents about the `_pinchy_ref` field, so old agents continue to work via the shared instruction shipped in their template prompt.

#### Odoo file attachment (new `odoo_attach_file` tool)

Read-write Odoo operator templates can now attach uploaded files to Odoo records as `ir.attachment`. The Bookkeeper template uses this to link receipt images to the corresponding vendor bill; the Warehouse Operator can attach delivery notes to pickings; the HR Operator can attach medical certificates to leave requests; and so on for the Project Manager, Production Operator, and Approval Manager.

The new `odoo_attach_file` tool is automatically included in the `read-write` permission tier alongside `odoo_create` and `odoo_write`. When you create any read-write Odoo agent, the tool is pre-enabled — no manual permission change is required.

**After upgrading**: existing Odoo connections need a schema re-sync to pick up `ir.attachment`. Open **Settings → Integrations → Odoo → ⋯ → Sync now**. Until you sync, the **Create** buttons for operator templates that reference `ir.attachment` will remain disabled.

The tool rejects filenames containing path separators or leading dots, and caps attachments at 25 MB (matching Odoo's default `web.max_file_upload_size`). These guardrails prevent a prompt-injection-influenced agent from referencing files outside the agent's uploads directory.

#### Background chats

Open chats keep running in the background while you navigate within Pinchy — agents no longer reset when you switch to another part of the app. A pulse dot in the sidebar marks each agent with an active run; if the last turn errored, the dot turns red so you see at a glance which chats need attention. ([#199](https://github.com/heypinchy/pinchy/issues/199))

#### Chat reliability fixes

- **Browser sleep recovery.** Chats now reattach cleanly when a tab returns from background sleep instead of getting stuck on a stale stream.
- **Dead-key composer freeze.** Typing characters that involve dead keys (e.g. `^` + `e` → `ê`) no longer desyncs the composer input.
- **Payload rejection visibility.** When OpenClaw rejects a payload mid-stream, the chat now surfaces a clear error status instead of leaving the spinner running.
- **Silent stream-end retry.** A run that finishes without producing any assistant text — typically a cold-path tool call that timed out inside OpenClaw's embedded layer — now shows the standard error bubble with a **Retry** button instead of an indistinguishable "successful empty reply". Each occurrence writes a throttled `chat.silent_stream` audit entry so admins get an operational signal.
- **Gemini 3 `thought_signature` errors are now named and explained.** When the upstream provider drops Gemini 3's required `thought_signature` field on a tool-call replay turn (upstream `openclaw/openclaw#72879`), Pinchy now shows a dedicated **transient upstream issue bubble** that names the cause and tells the user that **Retry usually clears it on the next try**. The bubble deliberately offers no "Switch model" link — the model is fine. Each occurrence writes a throttled `agent.upstream_format_error` audit entry so admins can track frequency from the audit page rather than grepping gateway logs. The upstream fix for the native Google path lands in OpenClaw 2026.5.18; %%PINCHY_VERSION%% is still pinned at 2026.5.7 and ships with the transient-issue bubble as the user-facing mitigation. The OpenClaw bump is scheduled for the next Pinchy release. ([#338](https://github.com/heypinchy/pinchy/issues/338))

#### Permissions tab no longer reports false-positive dirty state

If an Odoo or Email connection was configured on an agent, the **Settings → Agent → Permissions** tab kept showing "Unsaved changes" and a **Save & Restart** button after a successful save. The dirty-state check now re-syncs against the persisted state once the post-save refetch lands, so a clean save shows as clean.

#### New `/api/version` endpoint + OCI image labels

You can now confirm which Pinchy and OpenClaw versions are actually running without opening the UI. `GET /api/version` returns `{ pinchyVersion, openclawVersion, build, nodeEnv }` and is exempt from Domain Lock so it works from monitoring tools and the host shell. Docker images now also carry standard OCI labels (`org.opencontainers.image.version`, `org.opencontainers.image.revision`, …), so `docker inspect ghcr.io/heypinchy/pinchy:%%PINCHY_VERSION%%` reports the version and git SHA the image was built from.

The upgrading guide's "Verify" step is updated to use `/api/version` instead of opening `http://localhost:7777` directly — the old instruction returned 403 on Domain-Lock-enabled deployments.

#### Startup warning when Domain Lock is set but `BETTER_AUTH_URL` is unset

Better Auth's own `baseURL` autodetection does not read Pinchy's Domain Lock value (verified on a staging v0.5.4 install where Better Auth still logged `Base URL could not be determined`). On a Domain-Locked deployment, that gap silently sends password-reset and email-verification links pointing at the wrong host.

Pinchy now logs a clear `⚠ Domain Lock is configured (<your-domain>) but BETTER_AUTH_URL is unset…` warning on every startup of such a deployment, with the exact env-var assignment to copy-paste into your `.env`. The existing `BETTER_AUTH_URL is set` line is retained for the positive case. If you are running with Domain Lock today, check your pinchy container logs after this upgrade and add `BETTER_AUTH_URL=https://<your-domain>` to `/opt/pinchy/.env` if the warning appears. ([#352](https://github.com/heypinchy/pinchy/issues/352))

#### Runtime and dependency updates

- **OpenClaw 2026.5.7** (was 2026.5.3) — picked up via `Dockerfile.openclaw` and the bundled `openclaw-node 0.9.0`.
- **Next.js 16.2.6** (was 16.2.4) — addresses [GHSA-wfc6-r584-vfw7](https://github.com/advisories/GHSA-wfc6-r584-vfw7) and [GHSA-26hh-7cqf-hhc6](https://github.com/advisories/GHSA-26hh-7cqf-hhc6).
- **`ws` 8.20.1** (was 8.20.0) — addresses [GHSA-58qx-3vcg-4xpx](https://github.com/advisories/GHSA-58qx-3vcg-4xpx) (uninitialized memory disclosure).

## Upgrading from v0.5.2 to v0.5.3

### Breaking changes

None.

### Upgrade notes

v0.5.3 is a maintenance release focused on chat reliability: the recommended Ollama Cloud reasoning model was swapped after the previous default soft-deprecated upstream, model errors now surface as actionable in-chat bubbles instead of raw HTTP 500s, and the chat layout was restructured so per-agent runtimes survive in-app navigation. File attachments also got a proper preview surface.

Upgrade with the standard flow:

```bash
cd /opt/pinchy
sed -i 's/PINCHY_VERSION=v0.5.2/PINCHY_VERSION=v0.5.3/' .env
docker compose pull && docker compose up -d
```

No database migration, no `docker-compose.yml` change, and no env-var changes are required.

#### Default Ollama Cloud reasoning model swapped

`ollama-cloud/kimi-k2-thinking` is removed from the curated model list. The model began returning silent HTTP 500s from Ollama Cloud and is no longer a safe default. Agents that resolve via `tier=reasoning` now fall through to `deepseek-v4-pro`, which is already configured and tested.

Existing agents that pinned `kimi-k2-thinking` explicitly keep loading from the database — but the next request will fail upstream. Open **Settings → Agent → Model** for each affected agent and pick a working model. The new in-chat error surface (below) makes those agents easy to spot.

#### Actionable model-error bubbles in chat

When an agent's model call returns a 5xx from the upstream provider, the chat now shows a structured error bubble (`<Agent> couldn't respond.` + provider/model + a deep-link to the agent's model picker) instead of the raw `HTTP 500: "Internal Server Error (ref: …)"` string. The original error stays available under a collapsible **Technical details** section so support requests can still cite the upstream `ref` ID. Every flip into this error state writes an `agent.model_unavailable` audit entry.

#### Per-agent chat runtimes survive in-app navigation

Switching between agents in the sidebar no longer tears down and recreates the chat runtime: a new `ChatSessionProvider` mounts one runtime per agent at the `(app)` layout level, so streams in-flight on agent A keep running while you read agent B's history. The sidebar shows a subtle pulse on agents with an active turn and a red dot on agents that ended in an error, so background activity is visible without opening every chat.

Existing behavior is preserved on full page reloads: history still rehydrates from OpenClaw, the bundled history cap is now 200 messages, and a transient disconnect bubble shows when the WebSocket drops mid-stream.

#### Attachment preview and composer chips

Non-image attachments (PDFs and other files) now render as a filename chip in the composer and on sent messages, with the full filename available on hover. Clicking an attachment opens an in-app **AttachmentPreview** modal with PDF and image preview, instead of forcing a download. The uploads route is now authenticated for both `POST` and `GET`, and PDFs/images are routed through OpenClaw's built-in tools rather than inlined.

#### Background-run audit telemetry

Chat runs that complete while the user is not actively viewing the agent now emit a `chat.background_run_completed` audit event with the agent identity, run duration, and turn outcome. This makes background activity auditable without leaking conversation content into the audit log.

## Upgrading from v0.5.1 to v0.5.2

### Breaking changes

None.

### Upgrade notes

v0.5.2 is a maintenance release: a security fix for admin access to personal agents, the long-standing Ollama local-provider setup bug, and several robustness improvements in **Settings → Groups**.

Upgrade with the standard flow:

```bash
cd /opt/pinchy
sed -i 's/PINCHY_VERSION=v0.5.1/PINCHY_VERSION=v0.5.2/' .env
docker compose pull && docker compose up -d
```

No database migration, no `docker-compose.yml` change, and no env-var changes are required.

#### Security: admin access scope to personal agents

Before v0.5.2, an admin could read or modify another user's personal agent by calling `GET`/`PATCH`/`DELETE /api/agents/:agentId` directly, even though the UI did not expose those agents. The admin fast-path in `assertAgentAccess` now correctly enforces the personal-agent boundary: personal agents are only accessible to their owner, regardless of role. To share an agent with the team, set its visibility to **All users** (or use a group), which is unaffected by this change.

No action is required on upgrade. If you relied on this behavior in scripts or integrations, switch them to use a shared agent instead.

#### Ollama local provider works on fresh installs

Setting up a local Ollama provider in the onboarding wizard or **Settings → Providers** now works without manual `.env` edits. Pinchy detects local Ollama base URLs (`localhost`, `127.0.0.1`, `host.docker.internal`) and rewrites them to a Docker-routable hostname (`ollama.local`, mapped to the host gateway in `docker-compose.yml`) so the OpenClaw container can reach the model server on the host. The provider config also uses `openai-completions` with the `/v1` suffix, which is what OpenClaw expects for self-hosted OpenAI-compatible endpoints. Existing deployments that already work are unaffected.

#### Settings → Groups: inline validation, clearer errors

Group create and edit dialogs now render field-level validation errors inline (matching Pinchy's error-display policy) and surface API failures via toasts instead of leaving the dialog in an ambiguous state. Two-step partial failures — for example, the group is created but the initial member assignment fails — are reported explicitly so you know what to retry.

## Upgrading from v0.5.0 to v0.5.1

### Breaking changes

None.

### Upgrade notes

v0.5.1 is a hotfix release. It fixes a startup loop in OpenClaw caused by a missing `baseUrl` field in the generated `openclaw.json` for the Anthropic, OpenAI, and Google providers. Deployments of v0.5.0 that configured any of these providers without setting `ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL`, or `GOOGLE_BASE_URL` env-vars hit a `models.providers.<name>.baseUrl: expected string, received undefined` error and OpenClaw never came up.

Upgrade with the standard flow:

```bash
cd /opt/pinchy
sed -i 's/PINCHY_VERSION=v0.5.0/PINCHY_VERSION=v0.5.1/' .env
docker compose pull && docker compose up -d
```

No database migration, no config-file change, and no env-var changes are required. Pinchy regenerates `openclaw.json` on first start with the correct default base URLs, and OpenClaw boots cleanly.

If you previously set `ANTHROPIC_BASE_URL` (or the OpenAI/Google equivalents) for a proxy deployment, those env-vars continue to override the defaults — no action needed.

## Upgrading from v0.4.4 to v0.5.0

### Breaking changes

None for the standard upgrade path — the new `docker-compose.yml` you download below already contains the `openclaw-secrets` tmpfs block needed by the new SecretRef architecture.

If you maintain a **custom `docker-compose.yml`** (e.g. for a non-standard deployment, a different orchestrator profile, or a downstream fork), you must add a tmpfs-backed named volume for `/openclaw-secrets` and mount it into **both** the `pinchy` and `openclaw` services before starting v0.5.0:

```yaml
services:
  pinchy:
    volumes:
      - openclaw-secrets:/openclaw-secrets
      # …rest of the pinchy volumes

  openclaw:
    volumes:
      - openclaw-secrets:/openclaw-secrets
      # …rest of the openclaw volumes

volumes:
  openclaw-secrets:
    driver: local
    driver_opts:
      type: tmpfs
      device: tmpfs
      o: "mode=0770,uid=999,gid=999"
```

The volume must be mounted into both services: Pinchy writes and atomically replaces `secrets.json` (requires directory write permission — `uid=999` grants this as the `pinchy` system user); OpenClaw reads it. Without this, Pinchy cannot write the secrets file and every agent request fails with an authentication error.

### Upgrade notes

v0.5.0 moves image version pinning from `docker-compose.yml` into `.env`. This is a one-time migration — after this, upgrading only means editing `PINCHY_VERSION` in `.env` and running `docker compose pull && docker compose up -d`.

<Steps>

1. **Back up your database** (as always):

   ```bash
   docker compose exec db pg_dump -U pinchy pinchy > backup-$(date +%Y%m%d).sql
   ```

2. **Fetch the new `docker-compose.yml`**:

   ```bash
   cd /opt/pinchy
   curl -fsSL https://raw.githubusercontent.com/heypinchy/pinchy/v0.5.0/docker-compose.yml -o docker-compose.yml
   ```

3. **Add `PINCHY_VERSION` to your `.env`**:

   ```bash
   echo "PINCHY_VERSION=v0.5.0" >> /opt/pinchy/.env
   ```

4. **Pull and restart**:

   ```bash
   docker compose pull && docker compose up -d
   ```

</Steps>

If you skip step 3, `docker compose up` fails loudly with a clear message telling you exactly what to add — this is by design, so a forgotten pin can't silently pull the wrong version.

#### Recommended: set BETTER_AUTH_URL on HTTPS deployments

If Pinchy is available on a public HTTPS domain, add the canonical URL to `/opt/pinchy/.env` before restarting:

```bash
BETTER_AUTH_URL=https://pinchy.example.com
```

The new `docker-compose.yml` passes this through to the Pinchy container. Better Auth uses it to generate absolute links for password resets, OAuth callbacks, and Telegram pairing redirects. Use the same hostname you lock in **Settings → Security**, without a trailing slash.

#### Telegram: brief downtime on first start

OpenClaw detects the new SecretRef config format and hot-reloads. Telegram channels may be unresponsive for roughly 30 seconds during this reload. They recover automatically — no restart or re-configuration needed.

#### Recommended: rotate your API keys

Before v0.5.0, decrypted API keys were written into `openclaw.json` on disk. Docker volumes persist across container restarts, which means previous versions of `openclaw.json` (with decrypted keys inside) may still exist in volume history or backup snapshots taken before v0.5.0. After upgrading, rotate the keys at each provider:

- **Anthropic** — Settings → API Keys → revoke old, generate new
- **OpenAI** — API Keys → revoke old, generate new
- **Google (Gemini)** — Google Cloud Console → API Credentials
- **Ollama Cloud** — ollama.com/settings → API Keys
- **Telegram bot** — message @BotFather → `/revoke` → `/newtoken` (only if you suspect exposure; revoking invalidates the previous webhook setup)

Then in Pinchy, go to **Settings → Providers** and re-enter each new key. Pinchy validates each one against the provider's API before saving. Optionally, delete old volume backups that predate v0.5.0 if you no longer need them.

This is a "nice to do" for most deployments, but if your server was ever compromised or you share volume backups with third parties, treat it as required.

#### Optional: scan audit logs for historical plaintext

If your compliance requirements demand evidence that no API keys were logged in plain text in your audit trail, run the bundled scan script:

```bash
docker compose exec pinchy npx tsx packages/web/scripts/scan-audit-logs-for-plaintext.ts
```

It checks audit log entries for patterns that look like API keys (Anthropic `sk-ant-…`, OpenAI `sk-…`, Google `AIza…`, Ollama Cloud, Telegram bot tokens). A clean result means your audit trail doesn't contain any key material.

#### Future upgrades

From v0.5.0 onwards, upgrading is just:

```bash
# Edit /opt/pinchy/.env → change PINCHY_VERSION to the new tag
docker compose pull && docker compose up -d
```

The `docker-compose.yml` only changes when we add new services or volumes, which is rare. Release notes will tell you when a fetch is needed.

### What's new in v0.5.0

- **SecretRef architecture (RAM-only secrets)** — API keys no longer touch disk. The `openclaw.json` config file holds opaque `SecretRef` pointers; the actual key material lives in a tmpfs volume (`/openclaw-secrets/secrets.json`) that exists only in RAM and is never persisted. Compliance-friendly default for regulated industries. See [Secrets & API Key Storage](/security/secrets/).
- **Web Search integration** — connect Pinchy to the Brave Search API and grant agents per-tool access to search and page fetch. Per-agent filters for domain allow/deny, freshness, language, and region let you scope what each agent can reach. See [Set Up Web Search](/guides/web-search-setup/).
- **Gmail integration** — connect Google accounts via OAuth and give agents scoped email access: read, create drafts, or send. A guided wizard walks admins through the Google Cloud Console setup. See [Connect Email](/guides/connect-email/).
- **Per-template model recommendations** — agent templates now declare preferred model tiers (fast, balanced, reasoning) and required capabilities (vision, long-context, tools). When creating an agent from a template, Pinchy picks the best match from your installed models automatically.
- **Message delivery status** — the chat interface now tracks whether each message reached the agent. A message dims briefly while the network round-trip completes; once the agent acknowledges it, the bubble returns to normal. If delivery fails, a "Couldn't deliver" notice appears.
- **Retry button** — when a message fails to deliver, the agent doesn't respond, or a response stream drops mid-way, a Retry button appears directly below the failed message. Pinchy handles the right recovery action automatically — resend, re-trigger, or continue the partial response — depending on what went wrong. See [Message Delivery Status & Retry](/guides/retry-messages/).
- **Chat connection states with auto-recovery** — the chat indicator now shows four distinct states (Starting, Ready, Responding, Unavailable) with a 2-second hysteresis that absorbs brief network hiccups. When the agent runtime becomes reachable again after a drop or settings change, Pinchy automatically refreshes the conversation history. See [Chat Connection States](/explanation/chat-states/).
- **Seat-cap enforcement for licensed deployments** — Settings → Users now shows current seat usage (active users + pending invites) against the licensed `maxUsers` cap. The Invite button is disabled when at the cap, and the invite endpoint refuses to issue new invitations with a `user.invite_blocked` audit entry. License expiry banner refreshes without a page reload.
- **Integration delete with preflight** — deleting an integration now shows a dialog with the agent usage count up front. If agents are connected, you can detach-and-delete in one step instead of hunting through every agent. The state machine handles in-flight deletions cleanly so a failed delete doesn't leave orphaned references.
- **Customizing your deployment** — new guide documents the supported customization surface: `docker-compose.override.yml`. Pinchy's `docker-compose.yml` is owned by the project and replaced on upgrade — overrides go into the override file, which Docker Compose merges automatically and upgrades never touch. See [Customizing Your Deployment](/guides/customizing-deployment/).

- **OpenClaw runtime updated to 2026.4.27** — the underlying agent runtime gets a significant bump. Notably: spurious gateway restarts on agent-config changes are eliminated (Pinchy now supplements the OpenClaw-managed `channels.telegram` fields so the runtime sees no diff for Telegram-unrelated changes). The usage poller no longer runs immediately on connect, avoiding a CPU-bound `sessions.list` scan that was blocking the event loop during startup. No action required on upgrade.

#### Heads-up: Google template-model realignment

If you create new agents from a template with the Google provider, the `balanced` tier now picks `gemini-2.5-flash` instead of `gemini-2.5-pro`, matching Google's own tier descriptions (Flash is "best price-performance"; Pro is the reasoning tier). The `fast` tier picks `gemini-2.5-flash-lite`, and `reasoning` picks `gemini-2.5-pro`. **Existing agents are unaffected** — model IDs are only resolved at agent-creation time and never re-resolved on existing agents.

## Upgrading from v0.4.3 to v0.4.4

Standard upgrade — no breaking changes, no new required environment variables.

```bash
curl -fsSL https://raw.githubusercontent.com/heypinchy/pinchy/v0.4.4/docker-compose.yml -o docker-compose.yml
docker compose pull && docker compose up -d
```

### What's fixed in v0.4.4

- **Integrations list no longer blanks out when one row can't be decrypted** — if the `ENCRYPTION_KEY` ever changed (e.g. accidentally overridden via `.env`), one un-decryptable row in `integration_connections` silently caused `GET /api/integrations` to return 500, and the UI fell back to "No integrations configured yet" for every integration — including freshly-added ones that would decrypt fine. Each row is now decrypted in isolation; unreadable rows surface in Settings → Integrations as a warning card with a Delete action so admins can recover without DB access. `regenerateOpenClawConfig()` got the same per-row isolation, so one broken connection no longer takes down every agent's config.

## Upgrading from v0.4.2 to v0.4.3

```bash
curl -fsSL https://raw.githubusercontent.com/heypinchy/pinchy/v0.4.3/docker-compose.yml -o docker-compose.yml
docker compose pull && docker compose up -d
```

### Port binding change — action required if you use direct port access

The default port binding changed from `0.0.0.0:7777` to `127.0.0.1:7777`. After upgrading, Pinchy will only accept connections from localhost.

**If you have a reverse proxy (Caddy, nginx):** No action needed — this is how it should be.

**If you access Pinchy directly on port 7777** (no reverse proxy): Add this line to `/opt/pinchy/.env` to restore the previous behavior:

```bash
PINCHY_PORT=0.0.0.0:7777
```

We strongly recommend [setting up a reverse proxy](/guides/vps-deployment/#set-up-https-with-caddy) instead. Docker bypasses host firewall rules (UFW), so `0.0.0.0:7777` exposes the port to the internet regardless of your firewall configuration.

## Upgrading from v0.4.1 to v0.4.2

Standard upgrade — no breaking changes, no new required environment variables.

```bash
curl -fsSL https://raw.githubusercontent.com/heypinchy/pinchy/v0.4.2/docker-compose.yml -o docker-compose.yml
docker compose pull && docker compose up -d
```

### What's fixed in v0.4.2

- **Pinchy reconnects cleanly after the OpenClaw container is recreated** — on upgrades that replaced the OpenClaw container, Pinchy would repeatedly log `token_mismatch` / `token_missing` and refuse to connect to the gateway. Root cause: once a device is paired, OpenClaw accepts only the per-device token it issued at pairing, not the gateway bootstrap token from `openclaw.json`. Pinchy's client library now persists that per-device token alongside the device identity and sends it on reconnect, and discards it automatically if OpenClaw ever rejects it so re-pairing just works. (`openclaw-node` bumped from 0.3.1 to 0.4.0.)

## Upgrading from v0.4.0 to v0.4.1

Standard upgrade — no breaking changes, no new required environment variables.

```bash
curl -fsSL https://raw.githubusercontent.com/heypinchy/pinchy/v0.4.1/docker-compose.yml -o docker-compose.yml
docker compose pull && docker compose up -d
```

### What's fixed in v0.4.1

- **Smithers can read the platform docs again** — in every clean v0.4.0 install, Smithers' `docs_list` and `docs_read` tools pointed at an empty directory (relative bind-mount in `docker-compose.yml` resolved to nothing without a co-located source tree). The docs are now baked into the OpenClaw image itself, so this works in every deploy shape.
- **Security patches** — transitive-dependency vulnerabilities flagged by osv-scanner were patched.
- **Docs improvements** — the Hetzner deploy guide now requires an SSH key at server creation (the old "skip SSH keys" instruction left servers with no login path), and the upgrade guide gained a recovery path for legacy v0.3.0 deployments affected by that gap.

## Upgrading from v0.3.0 to v0.4.0

Standard upgrade — no breaking changes, no new required environment variables.

```bash
curl -fsSL https://raw.githubusercontent.com/heypinchy/pinchy/v0.4.0/docker-compose.yml -o docker-compose.yml
docker compose pull && docker compose up -d
```

<Aside type="note">
  v0.4.0 switches from local Docker builds to pre-built images from GitHub
  Container Registry (see "What's new" below). The `docker compose pull` above
  fetches the new images automatically — no extra steps. The previously-built
  local images stay on disk until you run `docker image prune` (safe to do after
  the upgrade).
</Aside>

### If you deployed v0.3.0 without an SSH key

A small number of early Hetzner deployments followed an older version of our guide that said "skip the SSH key." Those servers have no SSH access at all — the upgrade above can't run. Recover access like this:

<Steps>

1. **Reset the root password in Hetzner Cloud**

   In the Hetzner Cloud Console, open your server's detail page. In the actions menu (top right, "···"), click **Reset Root Password**. Hetzner shows a one-time password — copy it.

2. **Log in via the web console**

   On the same detail page, click the `>_` icon to open a browser terminal. At the `login:` prompt enter `root`, then the password from step 1.

3. **Install an SSH key**

   If your GitHub account has an SSH key attached (Settings → SSH keys), this is the shortest path:

   ```bash
   ssh-import-id-gh YOUR_GITHUB_USERNAME
   ```

   It fetches every public key on your GitHub profile and writes them to `~/.ssh/authorized_keys` with the right permissions.

   No GitHub keys? Create one locally (`ssh-keygen -t ed25519`) first and upload it to your GitHub account — the command above then works. Manual fallback: `mkdir -p ~/.ssh && curl -L github.com/YOUR_USER.keys >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys`.

4. **Verify SSH works from your local machine**

   ```bash
   ssh root@YOUR_SERVER_IP
   ```

   Once you're in via SSH, proceed with the standard upgrade above.

</Steps>

<Aside type="tip">
  The [Deploy on Hetzner](/guides/deploy-hetzner/) guide now requires an SSH key
  at server creation, so this recovery is a one-time fix for legacy deployments.
</Aside>

### What's new in v0.4.0

- **Telegram channels** — connect any agent to a Telegram bot. Users link their accounts with a pairing code and chat with agents from their phone. See [Set Up Telegram](/guides/telegram-setup/).
- **Odoo ERP integration** — give agents scoped, permission-aware access to Odoo with 16 pre-built agent templates. See [Connect Odoo](/guides/connect-odoo/).
- **Ollama (local and cloud)** — run agents fully air-gapped on your own hardware, or use Ollama Cloud's hosted open-source models. See [Set Up Local Ollama](/guides/ollama-setup/) and [Manage LLM Providers](/guides/llm-providers/).
- **Usage dashboard improvements** — cache-token tracking, source breakdown (chat vs. system vs. plugin), and a background poller that captures OpenClaw usage even for work that doesn't flow through the web UI.
- **Domain lock and insecure-mode banner** — lock Pinchy to a canonical HTTPS hostname; a warning banner appears when running without HTTPS on a bound IP/domain. See [Lock Pinchy to a Domain](/guides/domain-lock/).
- **Audit log event status** — every audit entry (not just tool calls) now shows success/failure with filterable status. Entries from earlier versions show a neutral placeholder.
- **Disconnect recovery during chat** — if the OpenClaw gateway disconnects mid-conversation, Pinchy detects it, tells the user, and recovers automatically when the gateway returns.
- **LLM provider errors surfaced in chat** — provider failures (invalid key, rate limit, no credits) now appear as a clear error card in the chat with an admin hint, instead of failing silently.
- **Pre-built Docker images on GHCR** — `docker compose pull` fetches images directly from GHCR (no local build step), making upgrades faster.
- **OpenClaw runtime updated** to 2026.4.12.

## Upgrading from v0.2.1 to v0.3.0

Standard upgrade — no breaking changes, no new required environment variables.

```bash
curl -fsSL https://raw.githubusercontent.com/heypinchy/pinchy/v0.3.0/docker-compose.yml -o docker-compose.yml
docker compose pull && docker compose up -d
```

### What's new in v0.3.0

- **Usage & Cost Stats dashboard** — admins can view token usage and estimated costs per agent and per user at `/usage`.
- **Smart PDF reading** — Knowledge Base agents can now extract text from PDFs automatically.
- **Dynamic model selection** — the default model is selected from your provider's live model list rather than being hardcoded.
- **Rate limiting on WebSocket connections** — protects the gateway from connection floods.
- **Setup password recovery** — admins can recover access via the `PINCHY_ADMIN_EMAIL` environment variable if locked out.
- **Security hardening** — timing-safe token validation, fail-closed audit logging.

### New optional environment variable: PINCHY_ADMIN_EMAIL

If you want to be able to recover admin access via the setup wizard, add this to your `.env`:

```bash
PINCHY_ADMIN_EMAIL=your-admin@example.com
```

This is optional — Pinchy works fine without it.

## Upgrading from v0.2.0 to v0.2.1

This is a docs-only release — no code changes, no database migrations, no breaking changes. Just pull the new tag and rebuild:

```bash
curl -fsSL https://raw.githubusercontent.com/heypinchy/pinchy/v0.2.1/docker-compose.yml -o docker-compose.yml
docker compose pull && docker compose up -d
```

## Upgrading from v0.1.0 to v0.2.0

This section covers what changed and what to expect.

### All users must log in again

The authentication system was upgraded to Better Auth. Existing login sessions are invalidated during the migration. Passwords are preserved — users just need to log in again.

### New environment variable: PINCHY_ENTERPRISE_KEY

v0.2.0 introduces enterprise features (Groups, RBAC, agent access control). To enable them, set the `PINCHY_ENTERPRISE_KEY` environment variable in your `.env` file or `docker-compose.yml`. This is optional — Pinchy works fine without it, and the key can also be entered via Settings in the UI.

### OpenClaw runtime updated

The bundled OpenClaw runtime was updated from 2026.3.7 to 2026.3.13. This happens automatically when you pull the new images with `docker compose pull`.

## Version history

Check the [Releases page](https://github.com/heypinchy/pinchy/releases) for detailed changelogs and any version-specific upgrade notes.
