# Composio CLI on this VPS (read-only /root) — full runbook

## Install record (2026-07-28)

- Installer: `curl -fsSL https://composio.dev/install` (needs `unzip`; `apt-get install -y unzip`).
- `COMPOSIO_INSTALL_DIR=/root/.hermes/composio bash install.sh` → binary v0.2.32 at `/root/.hermes/composio/composio`.
- Post-install step failed writing `/root/.composio/config.json` ("Read-only file system" / ENOENT) — cosmetic; binary works.
- Wrapper deployed at `/root/.hermes/bin/composio` (see `scripts/homeless-cli-wrapper.sh`); state persists at `/root/.hermes/composio-home`.
- ALWAYS invoke composio via the wrapper, never the raw binary.

## What didn't work (don't retry these)

- Default install → `mkdir: cannot create directory '/root/.composio': Read-only file system`.
- `COMPOSIO_DIR=/root/.hermes/composio-home` → present in `strings` output but IGNORED; still writes `/root/.composio/config.json`.
- `ln -s /root/.hermes/composio-home /root/.composio` → `ln: failed ... Read-only file system`.
- Plain `mount --bind <dir> /root/.composio` → rejected by the read-only parent. Private mount namespace + fake $HOME is the working pattern.

## Auth state ground truth

- Authenticated ⇔ `/root/.hermes/composio-home/user_data.json` has `"api_key"` non-null AND no `pending-login-session.json`.
- `composio whoami` exits 0 with EMPTY output when unauthenticated — exit code lies, check the files.

## Login flow that worked

1. `rm -f /root/.hermes/composio-home/pending-login-session.json` (clear stale/expired key)
2. `composio login --no-wait -y --no-skill-install` → prints `https://dashboard.composio.dev/?cliKey=<key>` (short TTL, minutes)
3. IMMEDIATELY background: `composio login --poll` (notify_on_complete, timeout 600)
4. User completes browser flow INCLUDING org/project picker through to success confirmation.

## Gotcha observed this session

User's first two browser attempts left the flow incomplete ("it was stuck... I skipped and freed it up") — the poll either expired waiting or never linked. Symptom: `user_data.json` still null api_key + pending-login-session.json present. Recovery: step 1–3 above with a fresh key.
