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

117 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.