Files
drinktracker/BACKLOG.md
JP 7ff3f68b2c 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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1Ee4Mc1X1SX8HgYa52zu7
2026-08-10 01:22:05 +00:00

4.7 KiB
Raw Permalink Blame History

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 510 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.