From 7ff3f68b2ccb8d56be1f94acddfcaf12ccca436f Mon Sep 17 00:00:00 2001 From: JP Date: Mon, 10 Aug 2026 01:22:05 +0000 Subject: [PATCH] Add a backlog, consolidating deferred work that was only in commit messages Several decisions had been consciously postponed - the Docker purge date, the unencrypted dumps, the unestablished Docker-in-LXC cause - but they lived in commit messages and chat, which is the same as nowhere. Each entry records what would trigger picking it up, so none of it has to be re-derived. Starts with the MCP tool-consolidation question: the answer is that merging CRUD into one tool would cost the annotations, the scope boundary, the schema clarity and the audit trail, and the useful rule is to merge only when it is the same gesture with a different parameter. Nothing here is urgent; that is the point of writing it down rather than doing it. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01W1Ee4Mc1X1SX8HgYa52zu7 --- BACKLOG.md | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 BACKLOG.md diff --git a/BACKLOG.md b/BACKLOG.md new file mode 100644 index 0000000..5b61f99 --- /dev/null +++ b/BACKLOG.md @@ -0,0 +1,116 @@ +# Backlog + +Things deliberately deferred, with enough context to pick them up cold. Not a +wish list — everything here has been considered and consciously postponed, and +each entry says what would trigger doing it. + +Last reviewed: 2026-08-10 + +--- + +## Consolidating MCP tools + +**Trigger: the model starts picking the wrong tool, or `tools/list` overhead becomes measurable.** + +There are 23 tools. The often-quoted ceiling of ~20 is a rule of thumb, not a +threshold, and mis-selection is caused by tools that resemble each other far more +than by raw count — these are mostly distinct. It has been working well in +practice, so this is not currently a problem. + +The question that prompted this: could one `modify_drink` take an `action` of +create/update/delete and replace three tools? Technically yes, but it is the +wrong merge: + +- **Annotations stop meaning anything.** A tool that *can* delete has to be + marked `destructiveHint`, so a client that confirms destructive actions would + start prompting when you add a drink — or you mark it false and lose the + prompt when it matters. +- **Scope enforcement gets coarser.** Reads need `drinks:read` and writes need + `drinks:write`. A merged tool must demand the union (a read-only token loses + read access) or branch internally, at which point the declared scope no longer + describes the tool and the consent screen misleads. +- **The schema gets harder for the model.** `id` is required for update/delete + and meaningless for create. That is a discriminated union, which converts to + `anyOf` — exactly the shape models fill in wrong. +- **The audit trail gets vaguer.** `McpAuditLog` records the tool name; + `delete_drink` says what happened, `modify_drink` does not. + +The rule worth keeping: **merge when it is the same gesture with a different +parameter; keep separate when the intent or the risk differs.** `upsert_bar_item` +("I bought gin" / "I finished the gin") and `promote_recipe` (drink vs wishlist) +are good merges. CRUD is not. + +Cheaper levers if the count ever needs to come down: + +1. `MCP_CHATGPT_TOOLS=0` drops the `search`/`fetch` aliases: 23 → 21, free if + ChatGPT is unused. +2. Remove low-value tools rather than merging good ones. `update_rating` is the + weakest — a drink can simply be re-rated. +3. Sharpen descriptions. Selection failures are usually a description problem. + +Worth measuring the real `tools/list` payload cost before spending effort here. + +--- + +## Refresh-token grace window + +**Trigger: a client is forced to re-authorize after a dropped network response.** + +Reuse of any refresh generation currently revokes the whole family, with no +grace. That is correct against theft but harsh on a client whose response was +lost in transit — it replays legitimately and destroys its own grant. + +The audit's advice was to build durable lineage first and only then consider +grace. Lineage now exists (`OAuthRefreshToken`, one row per generation, atomic +consume), so this is a safe change to make if it ever bites. Auth0 disables +leeway by default; Okta defaults to 30 seconds, configurable to 60. A 5–10 +second window limited to the immediately preceding generation would be +reasonable — anything older must still revoke the family. + +--- + +## Encrypt backup dumps + +**Trigger: backups leave the LAN, for any reason.** + +`pg_dump` output goes to TrueNAS unencrypted and contains every user's data plus +bcrypt password hashes. Defensible on a NAS you control on your own network; +not defensible in cloud storage, a friend's NAS, or off-site replication. + +`gpg` or `age` before the `scp` in `/usr/local/bin/drinktracker-backup` is a +couple of lines. Much easier to add before there is history to re-encrypt. + +Related: the off-site copy is in the same building, so it survives losing the +LXC or the Proxmox host but not fire or theft. + +--- + +## Purge the retained Docker artifacts + +**Trigger: 2026-09-07.** + +Docker is installed but disabled on the LXC; old containers and volumes were +kept as a rollback path after the move to native systemd. Nothing has needed +them. Remove after that date. + +--- + +## Establish why Docker actually failed in this LXC + +**Trigger: only if Docker is ever wanted on this host again.** + +`deploy/README.md` originally blamed unprivileged-LXC nesting. That is wrong — +`/proc/self/uid_map` reads `0 0 4294967295`, an identity mapping, so the +container is privileged. The AppArmor and `ip_unprivileged_port_start` failures +were real and reproduced, but the cause was never established. The doc now says +so rather than substituting a fresh guess. + +--- + +## Rotate the TrueNAS account password + +**Trigger: do it whenever convenient.** + +`getent passwd` run as root on TrueNAS prints the SHA-512 hash, and one ended up +in a session transcript. Salted and not trivially reversible, but crackable +offline against a weak password.