Skip to content

Releases: john-broadway/proximo

v0.31.0 — surfaces scope planes, not the doorway

Choose a tag to compare

@john-broadway john-broadway released this 02 Aug 03:05

Scoping your planes no longer opts you out of the door

PROXIMO_SURFACES chose the doorway as well as the planes. Setting it kept the pre-0.30
catalog door, so an install that scoped itself never got the 0.30.0 default it thought it had:
on a PVE+PBS box, 569 resident tools where the facade serves 5. Removing a
PROXIMO_TOOLSETS=catalog pin to "adopt the new default" bought two tools (571 → 569).

It failed silently. A working server, the old bill, no warning.

Surfaces now scope the searchable catalog and leave the door at the default facade.
Nothing becomes unreachable — a 800-combination matrix against 0.30.0 confirms zero
reachability regressions — and the old doors are one named variable away:
PROXIMO_TOOLSETS=catalog (full schemas, auto-scoped) or =all (everything).

Found by an external security and behaviour re-vet that probed the running server over stdio
JSON-RPC instead of trusting the changelog. Our own suite could not have caught it: the test
guarding the exact README sentence that was wrong validates the scoping filter, not what a
client is actually served.

Also fixed

  • proximo doctor no longer names a tool the box does not serve. It derived the facade
    size and "memory-first" from an environment variable while scoping had pruned
    proximo_recall, and pointed a model at it. Both now read from the live registry. It also
    credited PROXIMO_SURFACES for narrowing that autoscope actually did, including reporting
    "NOT plane-narrowed" on a box where hundreds of tools had been pruned.
  • A utility-only surface is served plainly. PROXIMO_SURFACES=memory built a search facade
    over a five-tool world while telling the model ~900 were searchable. That description now
    counts this server.
  • A second scoping pass no longer collapses the searchable catalog (measured 314 → 4).
    Pre-existing in 0.30.0, embedder-facing only; every shipped entry point scopes once.
  • Estate memory announces its file on every start, with the path, the opt-out
    (PROXIMO_MEMORY=0) and the relocation (PROXIMO_MEMORY_PATH) on the same line. It stays on
    by default; the objection worth honoring was that a plaintext inventory of your guests, nodes
    and targets should not appear unannounced.

Corrections to what we published

SECURITY.md said PROXIMO_SURFACES=all "forces the full surface" — it does not.
docs/SETUP.md priced SURFACES=pve at ~97,432 tokens for "a whole plane (311 tools)"; it is
the facade at a measured ~868 tokens with 312 searchable, and the 311 was wrong before this
release too. The README briefly claimed 316 tools "reachable, and only those", which was false
in the other direction: scoping changes what is advertised, never what is reachable by name.

Upgrading

If you set PROXIMO_SURFACES, your advertised tool count will drop sharply and everything
stays callable. To keep the memory-first estate answer resident, name it:
PROXIMO_SURFACES=pve,pbs,memory. To keep the old door, PROXIMO_TOOLSETS=catalog.

Full detail, including the claims corrected rather than rewritten away, in
CHANGELOG.md.

v0.30.0 — the default door fits a local model

Choose a tag to compare

@john-broadway john-broadway released this 01 Aug 20:50

[0.30.0] — 2026-08-01

Changed

  • The default door is now the dynamic facade, and estate memory is on by default. With
    nothing configured, tools/list serves six resident tools at ~1,449 tokens
    proximo_find_tools / proximo_tool_schema / proximo_call / proximo_recall plus the
    audit pair — with everything the box serves still searchable and callable through them.
    The measured reason: the previous default served the full plane catalog (~97k tokens on a
    single-PVE box, ~277k unscoped), 12x over the 8,192-token default context of a stock
    ollama install — dead on connect for a local model, and a silent tax on every other
    client that does not defer schemas. Rollbacks, by name: PROXIMO_TOOLSETS=catalog
    restores the pre-0.30 default (full schemas, auto-scoped to configured planes),
    PROXIMO_TOOLSETS=all the full surface, and PROXIMO_MEMORY=0 opts out of the estate
    map (removing proximo_recall from the facade rather than leaving a call that could only
    fail). The map stays local, derived and rebuildable, beside the audit ledger the install
    already keeps; scoping remains context hygiene, not an authorization control — the token
    ACL is still the boundary. CLI verbs (badge, mint, arm, disarm, reap, hello)
    no longer run registry scoping at all, so their errors are not prefixed with scoping
    noise.

Added

  • Search now finds the right tool with NOTHING configured — a vocabulary tier and lexical
    vectors, in the wheel.
    Keyword search needs the operator's words to appear in a tool's
    text; "how much space is left for backups" shares no surface form with "storage usage —
    disk used and available", so it matched nothing. Two mechanisms, pure stdlib, no
    dependency, no model, no network, no download:
    lexical.VOCABULARY maps operator language onto Proxmox's own terms (memory→mem/ram,
    container→lxc/ct/guest, who/changed→audit/ledger) as curated, auditable data — one
    readable line per mapping
    , and it is now the single source keyword search draws its
    synonyms from, so the two can never drift. Behind that, hashed char-n-gram TF-IDF vectors
    rank whatever keyword left unanswered, marked "match": "lexical".

    Measured on the real 905-tool catalog: the first search builds the index (259 ms,
    cached per catalog for the process), every search after it is ~7 ms, and the probes
    that used to miss now land (who changed this vm's configaudit_verify,
    space left for backups → the backup tools). Off-domain queries return nothing:
    admission requires a real word in common — a character-n-gram score alone once offered
    pve_node_disk_wipe ("MUTATION: wipe ALL data… NO UNDO") for "recipe for banana bread",
    because "recipe" and "wipe" share the fragment "ipe". Default-on because a search that
    needs configuration to work defeats the purpose; PROXIMO_LEXICAL=off disables the
    lexical tier.

    The two vocabularies are deliberately separate. VOCABULARY (wide, concept-level)
    serves ranking only; KEYWORD_VOCABULARY (narrow, near-exact renames) serves the keyword
    tier. Sharing one table was tried and reverted the same day after measurement: with the
    wide table feeding keyword AND-matching, "show" matched 824 of 905 tools and
    "check cluster health" 187, because concept jumps (health→status) land on words nearly
    every description carries — the OR-blowup lean mode exists to prevent. A blast-radius
    test against the tracked manifest now holds that line.

    Search is now a stack, each tier filling only what the one above left empty and marking
    its rows: keyword (exact) → semantic (opt-in, PROXIMO_EMBED_URL) → lexical
    (in-wheel). An unreachable embedder now degrades to lexical rather than all the way back
    to bare keyword.

  • Opt-in vector search over the estate sqlite — PROXIMO_EMBED_URL. Point it at an
    OpenAI-compatible /v1/embeddings server you run (ollama, llama.cpp and vLLM all serve one)
    and two seams gain semantic recall, zero new dependencies (struct-packed float32 in sqlite,
    pure-python dot product): proximo_find_tools keeps its exact-keyword hits FIRST and
    unchanged, and vector matches only fill remaining room, each marked "match": "semantic";

v0.29.0 — who asked, and two defaults moved toward safety

Choose a tag to compare

@john-broadway john-broadway released this 31 Jul 23:24

0.29.0 — who asked, and two defaults moved toward safety

Principal in the ledger. The PROVE chain now records who, not just what. A declared name tag
(PROXIMO_PRINCIPAL) stamps every entry; on the network faces, signed ES256 caller badges pinned by
the operator (PROXIMO_CALLER_KEYS_DIR) refuse an unverifiable caller fail-closed at the one shared
perimeter. Identity, never authority — the Proxmox token ACL stays the only authorization boundary.
Opt-in and inert until configured: an unconfigured deployment's ledger bytes are unchanged.

Command-body redaction is now ON by default. It was the only default in the box on the wrong
side of zero trust: ct_exec / ct_psql / pve_agent_exec bodies routinely carry a password on an
argv, and the PROVE ledger is a durable file. Full-body recording is now the deliberate act, and an
unrecognised value keeps redaction ON so a typo cannot silently disable it. Breaking if you
relied on full bodies for forensics — set PROXIMO_LEDGER_REDACT=0 explicitly.

A ~16% cheaper doorway, with no loss of surface. Full surface 276,000 → 231,700 tokens; one
plane 97,000 → 81,900; one domain 8,900 → 7,750. A nullable-anyOf collapse that is the identical
JSON Schema, and proximo_target no longer advertised on a box with no target registry to name it.
Routing is unchanged — it has always run off the injected kwarg, never the advertised schema.

Five independent review passes ran before this shipped, and they found more in our own work than
in the feature: a crash-safety ledger entry that dropped attribution, a network face tagging its own
remote requests as the local channel, a pin store that followed a symlinked directory and accepted a
world-writable key file, and a revocation guarantee in SECURITY.md that was false against a running
process. Each was fixed in the code rather than softened in the prose.

v0.28.0 — two honesty fixes

Choose a tag to compare

@john-broadway john-broadway released this 30 Jul 21:24

Two honesty fixes: one in what the software says, one in what it leaves behind.

The RRD tools stopped calling a rolling window "today"

A reviewer asked an agent for "utilization charts for today" and got the last hour, then the last 24 hours, presented as today. The obvious read is model error. It wasn't.

pve_node_rrddata offers hour|day|week|month|year and described that only as "over the specified timeframe." Proxmox's RRD endpoint accepts no start/end at all. So the model picked a window ending at now and called it today — because nothing in the schema said the windows roll, and nothing said a calendar day was unavailable. The description authored the claim; the model only spoke it.

Every RRD tool now states, in the text a model actually reads, that the window rolls and ends at now, that day means the last ~24 hours and not the calendar day, that no date can be requested, and what to report instead: the real span, from the timestamps. Fixed across the whole class rather than the one reported site — pve_node_rrddata, pmg_node_rrddata, pbs_node_rrd, pbs_datastore_rrd, proximo_baseline — with the disclosure pinned by tests at all five.

Reported by meyergru on the Proxmox forum, whose earlier report shaped 0.26.0's context work.

proximo reap can clean up after dead sessions (opt-in)

Restoring the read-only key was always half the job. Nothing removed a dead session's token and lock files, so a session directory accretes one pair per session forever — a credential store nobody audits. The deployment that surfaced this had 167 files for zero live arms.

Set PROXIMO_REAP_UNLINK_DAYS=N and reap also unlinks session files that are proven read-only, unheld (kernel flock, the same liveness oracle reaping already uses), and idle past N days. The removal happens under the file's own exclusive lock, so it cannot race a session that is just starting. An ex-armed file is restored first, which stamps a fresh mtime, so it only becomes eligible after a further full TTL. Orphan lock files and dangling symlinks sweep on the same terms; any other unreadable file stays put and is reported.

Unset or garbled means no unlinking — deletion is the destructive verb, so a typo must not enable it. That is deliberately the opposite fallback direction from PROXIMO_REAP_GRACE, where a typo must not disable the race guard.

--dry-run runs the same probes and stops short of the unlink itself, so a preview cannot promise what the real run would refuse.

Unchanged

904 tools. No interface changes, no new dependencies. Fleet: 11,469 tests green.

v0.27.1 — installable again

Choose a tag to compare

@john-broadway john-broadway released this 30 Jul 09:19

A fresh pip install proximo-proxmox had been broken for two days, and nothing in this repo could see it.

pyproject.toml declared mcp>=1.2.0 with no upper bound. The MCP SDK published 2.0.0 on 2026-07-28, and 2.x removed mcp.server.fastmcp — the module proximo/server.py imports on its 29th line. From that release onward every new install off PyPI, of any proximo version, resolved mcp 2.0.0 and then failed to import the package at all. uvx proximo-proxmox, the zero-install path the README leads with, was broken the same way.

The fix

  • mcp is capped below 2, and every other runtime and adopter-facing requirement now bounds its major (httpx<1, plus the [a2a] / [http] / [mcp-http] extras). The cap is a hotfix, not a verdict on 2.x: porting off mcp.server.fastmcp is real work and is not this release.
  • A test now reads the metadata an adopter actually resolves against and fails on any unbounded major, so this cannot recur silently. The dev extra is exempt, because it resolves through the committed lockfile and nobody installs it to use proximo.

Why the suite stayed green through all of it

uv.lock and the hash-pinned requirements/*.txt hold mcp at a 1.x, so CI, the container image and every local run were fine. Those pins deliberately never enter the wheel, because PyPI consumers resolve their own dependencies — which is exactly the hole.

A lockfile protects the build; only a bound in the published metadata protects an adopter.

It was found the only way it could be: installing the published artifact into a clean virtualenv and running it.

Upgrading

pip install -U proximo-proxmox. No code, tool, or interface changed — the tool estate stays 904. If you are on 0.27.0 and it imports today, your environment already resolved a working mcp; this release makes that guaranteed rather than lucky.

v0.27.0 — local knowledge, and a write boundary that says what it is

Choose a tag to compare

@john-broadway john-broadway released this 30 Jul 08:12

Two additions, both opt-in and inert until you set their env var, plus the honesty repairs found by reading the code and the output rather than the tests. The tool estate grows 900 to 904. A default install's served surface does not change: the four new tools are opt-in, and autoscope prunes them when their env var is unset.

Local knowledge, so the model stops guessing

Two seams that let the server answer from something it already holds instead of a fresh round trip.

  • Tier-1 estate memory (PROXIMO_MEMORY=1). proximo_recall returns an age-stamped local map of the estate; proximo_baseline returns per-guest cpu/mem distribution rollups derived from rrddata, stored-first. Both are derived, local, and never a health verdict.
  • The wiki seam reader (PROXIMO_WIKI=1). proximo_wiki does BM25 search and proximo_wiki_read reads one section, over a local docs index. No documentation content ships: you build the index, and the contract is published in docs/SETUP.md so any builder that writes the pinned schema qualifies. Retrieved text is classified ADVERSARIAL, because a solved forum thread can carry "now run pve_delete_guest" as easily as a fix.
  • Said plainly, because it is the honest limit: an unfed memory map used to answer total: 0 beside a note explaining that zero was not a claim about the estate. A 4B local model answered "0" anyway, three runs of three at temperature 0, without ever calling the tool the note named. Removing the number left a hole and the model filled the hole with zero. So these tools now refuse rather than return anything answer-shaped. The 4B still sometimes emits "0". We stopped supplying the lie. We cannot stop a downstream model inventing one, and this is not a fix for that.

Write authority you can toggle, and that cannot outlive its session

  • proximo arm / proximo disarm swap the token the server reads: read-only by default, a pre-minted write token while armed. It performs the swap and discloses whether the arm is a REAL boundary or merely ADVISORY, because whether an arm restrains anyone is a file-ownership question, not a code one. It grants no capability the caller lacked — what is new is the disclosure.
  • proximo reap restores read-only for sessions that ended while armed. The kernel is the liveness oracle: a serving process holds a shared flock for its whole life, and reap tries an exclusive non-blocking lock, which can only succeed once every holder is gone. That survives SIGKILL where a heuristic would not.
  • New env: PROXIMO_ARM_SOURCE, PROXIMO_READONLY_SOURCE, PROXIMO_SESSION_DIR, PROXIMO_SESSION_KEY, PROXIMO_REAP_GRACE.

Fixed

Autoscope could serve a near-empty server. Two autoscope implementations, only one of which learned that memory and wiki are not data planes. With PROXIMO_MEMORY=1 and no detectable data plane the served registry narrowed from 904 tools to 5, announced on stderr only, which MCP clients do not surface. There is now one guard, shared.

Plus: disarm could report success while installing write authority; the boundary check judged permission bits and ignored ownership (permission bits do not bound the owner); a refusal claimed live write authority that did not exist; four wiki refusals named a tool that does not ship; a garbled PROXIMO_ARM_TTL contradicted its own enforcement.

From the independent pre-release review

A second lens over the whole release. The local tools demanded PVE configuration they never useproximo_recall, proximo_wiki and proximo_wiki_read operate on local SQLite but each opened with a PVE-strict service call, so a PBS-only box was served the tools and then refused with an env error naming a subsystem the operator never configured. The seam tests mocked that call, which is exactly why they missed it. audit_verify had the same defect — the PROVE pillar's own verification tool crashed on a box with no PVE config, though the ledger it verifies is local. Also: proximo_baseline's stored path demanded configuration it promised not to need, arm --json dropped dir_owner_uid, and doctor's scoping text kept a stale guard.

From a hostile first-contact pass

Six adopter personas ran the product cold on the smallest footprints — zero config, PBS-only, a small model's doorway — and tried to make it embarrass us.

  • proximo doctor --product {pve,pbs,pmg,pdm}. The doctor was hardcoded to PVE, so the setup guide's own "verify your boundary" step dead-ended a PBS-only operator. pmg now dispatches to its own doctor; pbs/pdm have no doctor tool yet and say exactly that, pointing at proximo mint --product <plane>.
  • A connection failure names what to check. DNS failure, refused connection and timeout used to reach the caller as a raw OS errno through the first tools the README recommends, while doctor degraded gracefully on the identical fault. Both now share one seam.
  • Refusals that name their remedy. The four fingerprint refusals now name that plane's env var and the expected digest shape; both allowlist denials name their variable; the memory and wiki readers no longer leak a bare sqlite error on a directory path; config reports every missing environment variable at once.
  • The tool search speaks the operator's nouns. proximo_find_tools returned nothing for "delete vm" because the catalog says guest — a small model could miss the most destructive tool in the surface. And ct_exec/ct_psql lost their MUTATION marker to summary truncation, the one line that model reads.
  • Counted numbers say which configuration they describe. "Serves 900" matched no real install; measured live it is 310 for a single-plane default and 896 with all four data planes and exec off, against 904 registered. Every token figure the docs print is now checked against live measurement in CI.

Upgrading

Nothing changes for a default install: both new seams are inert until PROXIMO_MEMORY=1 or PROXIMO_WIKI=1 is set, and the arm/disarm rail is opt-in configuration. Full detail in CHANGELOG.md.

v0.26.0 — the surface stops costing what it covers

Choose a tag to compare

@john-broadway john-broadway released this 29 Jul 00:23

[0.26.0] — 2026-07-28

The surface stops costing what it covers. Reported from outside, with a measurement:
Proximo was unusable with a local model. The full tool catalog crossed to every client on
every connection — ~348,000 tokens of schema before a single question, past a 200k window
on its own, and ~122,000 for a single auto-scoped plane. An 8k-32k local model died at
connection time. The report was correct, and measuring it confirmed it was worse than reported.

Nothing had ever measured this. The surface grew 365 → 493 → 603 → 715 → 900 with no gate
anywhere costing what a jump did to the client's context.

  • Schema slimming — 348k → 276k tokens, no capability change. 84,000 of those tokens were
    one sentence: the per-call target selector's description, identical on 899 of 900 tools.
    Another 24,000 were pydantic writing a title beside every parameter name that already said
    the same word. title is presentational in JSON Schema and never reaches validation.

  • Scoping, in the field-standard shape (GitHub's MCP server vocabulary, so it is one
    adopters already know). Most specific wins: PROXIMO_TOOLS (exact names) → PROXIMO_TOOLSETS
    (23 domain groups: pve.guests, pve.ceph, pbs.tape, pmg.quarantine, …) →
    PROXIMO_SURFACES (planes, unchanged) → auto-scope. A typo refuses startup rather than
    quietly serving a different set than you picked.

  • PROXIMO_TOOLSETS=dynamic — the whole surface on a small model. Three tools resident
    (proximo_find_tools / proximo_tool_schema / proximo_call) at ~555 tokens; the other
    ~311 stay callable, just not resident. Dispatch routes through the same internal path a direct
    call uses, so the PLAN gate, the PROVE ledger write and your token's ACL all still apply — a
    smaller doorway, not a looser one.

  • Counted lean list responses — the payload half of the same report. Schema bloat wastes
    context; response bloat corrupts answers: with the full 25-field guest listing (PSI pressure
    metrics, byte counters) in a 16k context, a 12B local model counted 19 guests on a 28-guest
    cluster — complete correct data, wrong answer. Lean rows alone were not enough (the same
    model then counted 24), so counting moved server-side: pve_list_guests and
    pve_cluster_resources now return {total, by_status|by_type, rows} with the rows in a
    curated identity/state field set by default (~4× and ~3× smaller on that same cluster). With
    the counted envelope the model answered 28/18/10 correctly, three runs out of three.
    fields='all' keeps the raw rows, fields='vmid,mem,…' picks columns, and a field no row
    carries refuses with the list of fields that ARE available — an error that teaches, never a
    silently empty answer.

Measured on a real 28-guest cluster: dynamic 4 tools / ~555 tokens · PROXIMO_TOOLS with
three names / ~1,040 · pve.guests 27 tools / ~8,900 · PROXIMO_SURFACES=pve 310 / ~97,000.
Toolsets reach roughly 32k-class models; dynamic is the mode that reaches ~8k.

PROVE now records the interval, not just the result. Mutations write an executing entry
before the call and a terminal entry after, sharing a derived intent id, so an operation that
died mid-flight is visible instead of invisible. Previously a SIGKILL between the call and the
outcome write left an executed mutation with no ledger entry at all; audit.in_flight()
now names what was running. Verified by killing a real mutation, not by simulating one. The
interval lives in the same hash-chained log, so the crash record is tamper-evident. Reads are
unchanged — a read that dies changed nothing. Refusals carry no intent: they never started.

Also: pve_doctor reports the active scoping layer across all four (it knew only
PROXIMO_SURFACES, and silently misreported boxes scoped any other way).

Recent: 0.25.0 closed the PMG plane and added a fourth transport (715 → 900 tools).

v0.25.0 — the PMG plane closes; native MCP-HTTP transport

Choose a tag to compare

@john-broadway john-broadway released this 18 Jul 17:30

[0.25.0] — 2026-07-18

The PMG plane closes — and a fourth transport arrives. 715 → 900 tools (Wave 9, 10
chunks). Every one of PMG's 425 live API methods is now accounted for by an exit-code-gated
whole-plane audit: 351 covered in code + 74 documented dispositions (33 legacy-name aliases,
30 directory stubs, 11 named exclusions), 0 undocumented. This is the last shallow plane —
Proximo now governs the full PVE + PBS + PMG surface through one trust core. Alongside it, the
first community-contributed transport: native MCP over Streamable HTTP.

Added

  • PMG node administration (43 tools). Network interface CRUD + apply/reload/revert, DNS,
    time, node config, certificate info, service lifecycle, subscription; tasks (stop/log),
    syslog/report/journal (adversarial-classified free text), backup file list/create/restore,
    postfix queue inspection + management, ClamAV/SpamAssassin signature updates.
  • PMG mail-plane config (66 tools). LDAP profiles + directory queries, fetchmail,
    domains/transport/mynetworks completion, TLS policy + inbound-TLS domains, DKIM signing,
    SpamAssassin custom scores, PBS remote config + node-side PBS backup jobs, ACME
    accounts/plugins + node cert order/renew/revoke + custom-cert upload, mimetypes, regextest.
  • PMG ruledb per-object reads + the global welcomelist + factory reset (15 tools, from the
    Wave 8 groundwork carried in).
  • PMG identity (pmg_identity.py, 34 tools). Auth realms, local users (role granted in
    the create call — rated by whether that role is admin-equivalent), TFA, and the six
    appliance-wide config singletons (admin/clamav/mail/spam-quarantine/virus-quarantine/
    webauthn) plus PMG cluster bootstrap/join — the join transmits a peer master's root
    credential in transit, held to a structural never-in-ledger guarantee.
  • PMG quarantine + statistics completion (11 tools), including the quarantine capability
    link — a bearer-credential URL that reaches the caller but is redacted from its own audit
    record (the trust core's first read-return redaction).
  • MCP over Streamable HTTP, natively (upstream FR #25, contributed by @alexdelprete) — a
    new optional proximo-mcp-http face (pip install 'proximo-proxmox[mcp-http]') serves the
    SAME FastMCP instance the stdio server runs over the MCP SDK's native Streamable HTTP
    transport, so networked MCP clients (Claude Desktop/Code on another machine, web clients) no
    longer need a third-party stdio→HTTP bridge that sits outside Proximo's perimeter. No adapter
    layer at all — it IS MCP, so the tool registry, trust spine (PLAN/PROVE/UNDO, the gates),
    PROXIMO_SURFACES scoping, and the Proxmox token scope are inherited by construction. Behind
    the shared proximo.webguard perimeter, identical to the A2A/HTTP faces: fail-closed public
    bind (PROXIMO_MCP_HTTP_TOKEN_FILE, refused without it on a non-localhost
    PROXIMO_MCP_HTTP_HOST), constant-time bearer on /mcp, Host/DNS-rebind allowlist
    (PROXIMO_MCP_HTTP_ALLOWED_HOSTS), and the cross-origin (CSRF) guard. Default
    127.0.0.1:41243, serving stateless (stateless_http=True — the upstream maintainer's
    call on the FR: multi-client behind a proxy is the deployment model, and nothing in the
    governed surface needs a session; opt out with PROXIMO_MCP_HTTP_STATELESS=0); opt-in
    plain-JSON responses with PROXIMO_MCP_HTTP_JSON=1. The SDK's own DNS-rebind layer is
    deliberately disabled in favor of the one authoritative webguard perimeter (two driftable
    allowlists is how holes happen); proven end-to-end by the official MCP client in
    tests/test_mcphttp_e2e.py, and merged after a two-lens adversarial review of the spine and
    perimeter.
  • The MCP-HTTP face's post-merge review findings, closed (contributed by @alexdelprete,
    PR #29): the transport-face seam contract moves from a regex scan to an AST scan
    structurally immune to the SDK-namespace false positives the regex needed lookbehinds for;
    the mutating-tool end-to-end (test_mcphttp_e2e.py) drives a real MCP client through the
    governed spine and asserts the plan and the ledger entry both fired; and the security docs
    learn the third face. Folded in with the seam scan hardened further to also track import
    aliases (from proximo import server as srv) and the dotted module path, plus an explicit
    honesty test asserting what a static scan still cannot catch — a fully dynamic reach
    (getattr/sys.modules), whose real defense stays code review, said plainly rather than
    papered over.

Security

  • Secret discipline across six new credential shapes — LDAP bind passwords, fetchmail
    passwords, PBS-remote password + encryption key, DKIM (server-generated, never returned),
    ACME EAB HMAC key + DNS-plugin credential blobs + uploaded cert private keys, local-user
    passwords, TFA recovery codes, OIDC client keys, and the cluster-join peer credential —
    each proven never to reach the tamper-evident ledger by raw-byte sweeps, with the guarantee
    verified as content-blind (a hostile server echo cannot smuggle a secret through a
    free-form response field).

v0.24.0 — Ceph + SDN deep: 603 → 715 tools

Choose a tag to compare

@john-broadway john-broadway released this 17 Jul 05:44

The hyperconverged release. The full Ceph plane (42 tools: OSD/mon/mgr/mds
lifecycle, pools, CephFS, flags, config, crush — destroy plans cite Ceph's own
cmd-safety verdict) and the deep SDN surface (70 tools: controllers, DNS, IPAMs,
fabrics, vnet-scoped firewall, IP mappings, prefix-lists, route-maps — with SDN
dry-run, the global lock handled as a never-ledgered capability token, and
rollback as a real undo for staged SDN config). Honest state: both new planes are
schema-built and mock-tested (9,182 tests), not yet live-proven — per-tool
docstrings say which is which. Adversarial review ran on every chunk and found
real defects every time; all fixed pre-release, review records in repo history.

v0.23.0 — the PBS plane closes

Choose a tag to compare

@john-broadway john-broadway released this 16 Jul 01:34

The PBS plane closes. 493 → 603 tools. Every management endpoint Proxmox Backup Server's
live API schema exposes is now either governed by a Proximo tool or on a documented, deliberate
exclusion list — and that claim is not prose: an exit-code-gated audit script walks the live
schema against every tool's calls (349 endpoints → 292 covered + 31 directory stubs + 26
documented exclusions + 0 undocumented). The exclusions are wire-protocol endpoints (the
backup/reader client protocol), console endpoints (a different trust category, gated on an
explicit ruling), browser auth handshakes, and node power — each named in the module docs.
Same discipline as 0.22.0: every tool built from the live upstream schema, every chunk
adversarially reviewed before landing, and what the reviews caught is fixed here too.

Added

  • PBS tape (56 tools) — the surface no other Proxmox MCP touches: drive/changer hardware
    config + scans; media pools; tape encryption keys (key material and passwords proven
    never to reach the audit ledger — raw-bytes tests; key delete rated HIGH with PBS's own
    "you can no longer access tapes using this key" wording); drive/changer operations
    (load/unload/eject/rewind/clean, label/barcode-label/format — HIGH, destroys tape
    contents, and the plan says the label-text check is opt-in protection, absent by default);
    media catalog, tape backup jobs, one-off backup, restore. Includes
    pbs_tape_media_destroy — upstream exposes it as a GET that destroys; Proximo gates it
    like the mutation it really is (verb is not the safety signal).
  • PBS S3 (8 tools) — client configs (secret-key never in ledger; access-key deliberately
    visible, AWS convention), bucket listing, endpoint sanity check, counter reset.
  • PBS client encryption keys (4 tools) — list/create/delete/toggle-archive.
  • PBS metrics servers (12 tools) — InfluxDB HTTP (token never in ledger — PBS's read API
    genuinely returns it, so Proximo strips it at the read layer) + UDP CRUD, unified views.
  • PBS admin + node odds (13 tools) — job-level GC/prune/sync/verify views, live traffic-
    control status, node config get/set (http-proxy credentials redacted), identity, RRD stats,
    diagnostic report (classified adversarial: free-text), version, and pull/push — governed
    datastore sync from/to remotes, where remove-vanished escalates the risk rating and the
    plan states exactly what gets deleted.
  • PBS datastore admin (17 tools) — the closers: backup-group list/delete (HIGH — a group
    delete takes ALL its snapshots), group notes, protected-status read, datastore RRD,
    active-operations, datastore usage, remote scan (read side of pull/push), namespace move
    (upstream defaults delete-source=true — disclosed), whole-datastore prune (schema-distinct
    from the per-group prune; Proximo defaults dry-run on, flipping upstream's default —
    documented), mount/unmount, s3-refresh.

Fixed

  • pbs_job_run recorded submitted for job runs that return nothing. The
    prune/sync/verify job-run endpoints return null, not a task UPID — the ledger now records
    the honest outcome (shipped since the tool's introduction; caught by this wave's review).
  • Empty delete=[] lists are now rejected loudly on every PBS updater instead of being
    silently dropped by the HTTP transport — a dry-run/execute parity gap: the plan disclosed a
    payload the wire never carried.

Security

  • Proxy URLs with @ in the password no longer leak the password tail into plans or the
    ledger; S3/tape/metrics secret reads are stripped at the read layer (never trust a
    documented secret-free response blindly).