Skip to content

v0.2.1

Choose a tag to compare

@torfeu torfeu released this 29 Aug 06:40
· 77 commits to main since this release

New

  • Settings in tabs — eleven sections in one modal had outgrown the dialog. They are now grouped into Security (password, edit mode, MCP endpoint auth, read token, agent token), Identity, System (shared port, venvs, server) and Maintenance (usage tracking, update check). The panels stay in the DOM, so loading and Save Settings still reach every field no matter which tab is open — saving remains one action for the whole dialog. The chosen tab is remembered in localStorage (in a try/catch: in a private window the access itself throws)
  • Login lockout — three rejected credentials from one address cost 60 seconds, the next three 120, then 240, doubling without a ceiling; the API answers 429 with Retry-After and the login dialog shows a countdown instead of "wrong password". There is no login route to protect — the browser sends the password as a Bearer token on every request — so the counter sits in app/lockout.py where that decision is made, keyed by the connection's address and never by X-Forwarded-For, which a caller could write to dodge their own counter or run someone else's address into a block. A request carrying no credential is not counted (the UI asks before anyone has logged in, and a reload must not cost a strike), and neither is a configured token that merely lacks the scope for a route — that is a client at the wrong door, not a guess. Counters live in memory only: a manager restart clears every block, which is also how you get back in if you lock yourself out
  • Permissions dialog at scale — a search over all tools (with a "10 of 152" counter, blocks holding a hit open themselves), a search over the people list, and instance blocks collapsed from eight instances up, each header carrying its status (no access · all tools · 7 of 86). Filtering and collapsing only show and hide the rendered blocks, so a checkbox already ticked cannot get lost on the way

Fixed

  • A new venv installed an mcp the runner cannot use — the base packages were unpinned, so any venv created after the release of mcp 2.0 got the 2.x line, which rebuilt the low-level Server API; every instance in that venv died at startup with 'Server' object has no attribute 'list_tools'. Existing venvs keep the 1.x they installed long ago and show nothing, which is why this stayed invisible on a running installation — but a fresh install of v0.2.0 was broken out of the box, as was any newly created venv. mcp is now pinned below 2 in venv_manager.BASE_PACKAGES and in pyproject.toml, with a test that fails if the ceiling is dropped without porting the runner first
  • The permissions dialog painted over its own buttons — its grid items kept their automatic minimum height, so the columns grew with their content instead of scrolling, and the overflow was drawn across the Save/Close bar (which has no background of its own). Measured at 900×500 with 86 tools: 199 px of overlap, now a 20 px gap and a scrollbar where it belongs
  • A leftover directory took the venv list down — an interpreter migration parks the old venv next to the new one under a name like default.py312-migrated-20260816-001110. The listing asked venv_dir() about every directory it found, and the dot in that name raised out of the listing and took /api/venvs with it. Unnoticed since 16 Aug because the UI has a fallback and quietly showed only default. The listing now skips names this module could never have produced; venv_dir() stays strict, so using such a name still raises
  • A three-line instance name in the permissions header — the name stacked next to the dropdown in the narrow column; the dropdown has a fixed width now and the name min-width: 0