From c56e83672c41c0fb9eb36975c67f6f4f8e47a985 Mon Sep 17 00:00:00 2001 From: perseus <51974392+tcconnally@users.noreply.github.com> Date: Sun, 28 Jun 2026 18:12:39 +0000 Subject: [PATCH] docs: remove internal HANDOFF-DEV.md/HANDOFF-HERMES-SYNC.md from public repo Internal process/launch notes, not user-facing. Content remains in git history. --- HANDOFF-DEV.md | 54 ----------------- HANDOFF-HERMES-SYNC.md | 132 ----------------------------------------- 2 files changed, 186 deletions(-) delete mode 100644 HANDOFF-DEV.md delete mode 100644 HANDOFF-HERMES-SYNC.md diff --git a/HANDOFF-DEV.md b/HANDOFF-DEV.md deleted file mode 100644 index 09bbd28..0000000 --- a/HANDOFF-DEV.md +++ /dev/null @@ -1,54 +0,0 @@ -# Plutus — Developer Handoff (continuing toward 1.0) - -> For the next Claude Code instance (or human) picking up Plutus development. -> Snapshot: **2026-06-26**, version **0.7.0**. - -## One-line status -The **billing engine** (`plutus_agent`) is live — on PyPI (`pip install plutus-agent`), GHCR, and deployed at **https://plutus.perseus.observer**. The self-serve loop (open signup → API key → `POST /v1/usage` → dashboard → Stripe upgrade) works end-to-end, and we're **dogfooding it on our own Hermes spend**. A deep code review is done; the path to 1.0 is in **[ROADMAP-1.0.md](ROADMAP-1.0.md)**. - -## Where things live -| | | -|---|---| -| Repo | `Perseus-Computing-LLC/plutus` (public, MIT) — `plutus_agent/` is the engine; `plutus.py`/`plutus_route.py` are the original monitor | -| PyPI | `plutus-agent` | -| Image | `ghcr.io/perseus-computing-llc/plutus:latest` (built on `v*` tag via `.github/workflows/release.yml`) | -| Hosted | https://plutus.perseus.observer — container `plutus` on greg, `media-net`, port 8420, data vol `/data` | -| Hosted DB | `/data/plutus.db` in-container (`PLUTUS_HOME=/data`); host: `/mnt/cache/appdata/plutus/data` | -| Docs | `README.md`, `docs/api.md` (ingest API + SDK remote mode), `docs/auth.md`, `docs/claude-code.md` | - -## What's done / live -- v0.4.0 self-serve signup funnel · v0.5.0 ingest API (`POST /v1/usage`) + API keys + SDK remote mode · v0.5.1 Cloudflare-UA fix · v0.6.0 money/concurrency correctness · v0.7.0 security hardening. -- Hermes spend syncs into the dashboard via `examples/hermes_sync.py` on a **host-level cron** (every 15 min) — see `HANDOFF-HERMES-SYNC.md`. -- The test suite passes (`pytest tests/`, stdlib-only/offline). Production monitor untouched. - -## The work queue (toward 1.0) -v0.6 (money) and v0.7 (security) **shipped** in 0.6.0/0.7.0. A 2026-06-26 foundation review verified them against `main`: most fixes landed, but several closed issues were only **partial**, plus new hygiene gaps. Current queue — see **v0.7.1 — Foundation hardening** in `ROADMAP-1.0.md`: -- **Carryover (reopened):** #28 prepaid hard-stop (off-by-default + racy), #30 atomic `balance_after` + quota race (only `busy_timeout` landed), #32 CSRF fails open, #33 no DB-backed per-day org cap, #37 500 error-leak + 404 reflected-XSS. -- **New:** #47 `plutus --db` crash (`os` not imported in `cli.py`); #48 Windows/macOS CI + Python-matrix/classifier mismatch + `release.yml` double-publish + stale packaging. -- **v0.8 — Team tier ($149/mo) + LangChain/CrewAI/MCP integrations** (the revenue/distribution lever; after the v0.7.1 carryover). -- **Do not launch publicly before the v0.7.1 carryover lands** — open signup + live money are exposed. - -## Critical gotchas (these will bite) -1. **Hermes is NOT a separate box — it's containers on greg.** Reach it via `ssh greg` + `docker exec hermes …`. State.db: `/opt/data/webui/minions-hermes-config/state.db` (in-container). -2. **greg uses HOST `docker compose`, not Dockge** (the dockge container isn't running). Redeploy: `docker compose -f /mnt/cache/appdata/stacks/plutus/compose.yaml pull/up -d --force-recreate plutus`. The `greg-deploy` skill's `docker exec dockge` path does **not** work here. -3. **Cloudflare blocks the default `Python-urllib` User-Agent (error 1010).** Anything POSTing to the public URL with urllib must send a real `User-Agent` (fixed in 0.5.1). Internal service-to-service uses `http://plutus:8420` (same `media-net`), which bypasses CF. -4. **Verify the deployed image version before assuming parity.** Check `GET /healthz` (`{"version": …}`) against the latest tag rather than trusting this doc. Client-side SDK fixes don't require a server redeploy, and the greg cron posts to the internal `http://plutus:8420`, so it's unaffected by CF/SDK changes. -5. **Unraid cron persistence:** host cron lives in `/boot/config/plugins/dynamix/*.cron` + `update_cron` → writes `/etc/cron.d/root`. Plain `crontab -l` reads the wrong spool; verify with `crontab -c /etc/cron.d -l`. -6. **The dogfood ingest API key** lives only in the greg cron / dynamix file (and the operator's hands) — never commit it (public repo). Re-mint anytime: `docker exec plutus plutus keys create --name … --org "Perseus Computing"`. -7. **Stripe is LIVE** on the hosted instance. Be careful with billing-path changes. - -## Dev workflow -- **Branch → PR → a human merges.** A safety classifier gates heavy/outward-facing actions — **merging to `main`, tagging/releasing, redeploying greg, and editing `~/.claude/settings.json` all require explicit per-action user authorization.** Don't expect to self-merge; prepare the PR and ask. -- Tests: `pytest tests/` (stdlib `unittest`, fully offline). Keep them green. -- Commit trailer: `Co-Authored-By: Claude Opus 4.8 `. -- Secrets come from env, never config/repo (`config._strip_env_secrets` enforces this on save). - -## Verify it's healthy -```bash -curl -fsS https://plutus.perseus.observer/healthz # {"ok":true,"version":"0.7.x"} -ssh greg 'docker ps --filter name=plutus --format "{{.Names}} {{.Status}}"' -cd && pytest tests/ -q -``` - -## Suggested first move -Close the **v0.7.1 carryover** (see `ROADMAP-1.0.md`). Highest-leverage: finish #30/#28 — wrap the ledger read-modify-write (`db.add_ledger` plus the quota/hard-stop checks in `metering.record_usage`) in one `BEGIN IMMEDIATE` transaction so the balance/quota decision and the insert are serialized, and default the prepaid hard-stop on for prepaid orgs. Then the quick wins: #47 (`import os` in `cli.py`) and #32 (fail closed in `_same_origin` when `base_url` is unset). diff --git a/HANDOFF-HERMES-SYNC.md b/HANDOFF-HERMES-SYNC.md deleted file mode 100644 index 35085da..0000000 --- a/HANDOFF-HERMES-SYNC.md +++ /dev/null @@ -1,132 +0,0 @@ -# Handoff — wire Hermes spend into the hosted Plutus dashboard - -**For:** an agent (Opus 4.8) running on the Hermes box, in this workspace -(`/opt/data/webui/minions/.minions-data/workspace/plutus/`). -**Goal:** every Hermes session's spend shows up live on -`https://plutus.perseus.observer` (we're dogfooding Plutus on our own usage). -**Effort:** ~10 min. One-time deploy + a cron. Read-only against `state.db`. - -> ⚠️ **Secret hygiene:** this repo is **public**. Never commit the API key, and -> never paste it into a file under the repo. It comes from the environment only. - ---- - -## What's already done (no action needed) - -- Hosted Plutus is **live at `https://plutus.perseus.observer`** (v0.5.0, - healthy). The ingest API `POST /v1/usage` is up and authenticates by API key. -- The org **Perseus Computing** (`org_b3471ab33602c4c3`, Pro/unlimited) is seeded. -- A write-only ingest **API key has been minted** for that org. The operator - will provide it to you as `PLUTUS_API_KEY` (see step 1). It can only meter - usage — it cannot read data, charge, or refund. -- The bridge script ships in this repo: **`examples/hermes_sync.py`** - (stdlib-only, no `plutus_agent` install needed). - -## What you're deploying - -`examples/hermes_sync.py` reads **new** rows from Hermes' `sessions` table — the -same `state.db` that `plutus.py` reads — and POSTs them to `/v1/usage`. It tracks -a `sessions.rowid` watermark in a JSON state file and advances it per batch, so -**re-runs never double-count** and a failed run resumes cleanly. - ---- - -## Steps - -### 1. Confirm inputs - -```bash -cd /opt/data/webui/minions/.minions-data/workspace/plutus -git pull # gets examples/hermes_sync.py + this doc - -# Provided by the operator out-of-band — do NOT hard-code or commit: -export PLUTUS_API_KEY='plutus_sk_…' # the Perseus Computing ingest key -export PLUTUS_REMOTE_URL='https://plutus.perseus.observer' -# Optional (these are the defaults; override only if your paths differ): -# export PLUTUS_STATE_DB='/opt/data/webui/minions-hermes-config/state.db' -# export PLUTUS_WORKSPACE='hermes' -``` - -If `PLUTUS_API_KEY` isn't set, **stop** and ask the operator for it (or mint one -on greg: `docker exec plutus plutus keys create --name hermes --org "Perseus Computing"`, -which prints the secret once). - -Sanity-check the endpoint and the DB: - -```bash -curl -fsS "$PLUTUS_REMOTE_URL/healthz" # expect {"ok": true, "version": "0.5.x", ...} -ls -l "${PLUTUS_STATE_DB:-/opt/data/webui/minions-hermes-config/state.db}" -``` - -### 2. Dry-run (no writes, watermark untouched) - -```bash -python3 examples/hermes_sync.py --dry-run -``` - -Expect a count of new sessions and a sample of 2–3 events (provider, model, -tokens, `cost_usd`, `workspace:"hermes"`, `source:"hermes"`). If it says -"nothing new", the table is empty or already synced — fine. - -### 3. Backfill (first real sync) - -```bash -python3 examples/hermes_sync.py -# → "plutus: metered N session(s) → … (watermark rowid=…)" -``` - -### 4. Verify - -- **Idempotency:** run it again immediately — it must print **"nothing new"** - (proves the watermark works and we won't double-count on cron). - ```bash - python3 examples/hermes_sync.py - cat ~/.plutus/hermes_sync.json # {"last_rowid": …, "count": …} - ``` -- **It landed:** ask the operator to open `https://plutus.perseus.observer` - (Google sign-in) and confirm a **`hermes` workspace** with spend now appears. - (The dashboard read APIs are session-gated, so you can't curl them with the - ingest key — rely on the script's printed summary + the operator's eyeball.) - -### 5. Install the cron (every 15 min) - -Mirror the existing `plutus-refresh.sh` cron style. Put the secret in the cron -environment, not in any committed file: - -```cron -*/15 * * * * cd /opt/data/webui/minions/.minions-data/workspace/plutus && \ - PLUTUS_REMOTE_URL=https://plutus.perseus.observer PLUTUS_API_KEY= \ - python3 examples/hermes_sync.py >> /var/log/plutus-hermes-sync.log 2>&1 -``` - -Confirm it's registered (`crontab -l`) and watch one cycle in the log. - ---- - -## Gotchas - -- **Don't disturb the existing `plutus.py` / `plutus_route.py` crons** — this is - a *new, additive* job. The monitor keeps running as-is. -- **`state.db` is opened read-only** (`mode=ro`); the bridge never writes to it. -- **Cost:** the bridge sends Hermes' own `actual_cost_usd` (falls back to - `estimated_cost_usd`), so dashboard dollars match Hermes' ledger. -- **Balance goes negative** on the Perseus Computing org (Pro = unlimited tokens - but no prepaid credit loaded). Harmless for our own org; `plutus topup` on greg - if you want it to read clean. -- **Schema drift:** the bridge auto-detects whether `sessions` has `model` / - `task_type` columns, so it won't break if they're absent. - -## Rollback - -```bash -crontab -e # remove the */15 line -rm -f ~/.plutus/hermes_sync.json # forget the watermark (a later run re-syncs all) -``` -No data on the Hermes side is touched; on the Plutus side, over-sent events are -just extra usage rows on our own org. - -## Done when - -`crontab -l` shows the job, the log shows a clean cycle, a second manual run -says "nothing new", and the operator sees the `hermes` workspace on the -dashboard.