Skip to content

Prometheus v0.9.18

Choose a tag to compare

@github-actions github-actions released this 02 Nov 07:14
· 24 commits to main since this release
f7a68a4

Prometheus 0.9.18 — beta

0.9.18 fixes two security issues: a model's shell and file tools could read this machine's private keys and change what the daemon starts at boot, and an agent profile's tool list hid tools from the model without stopping it calling them. It updates authlib, dnspython, joserfc, pyasn1 and graphql-core for published advisories. It also:

  • lets a paired app check it reached the Prometheus it paired with before it sends its token;
  • lets voice use a speech server on another machine, with local fallback;
  • uses a llama.cpp server started with an API key;
  • says which local AI app answered setup's look, and why none is usable;
  • keeps your account name, your machine's layout and a purged chat's facts out of what the model is sent and keeps;
  • fixes five computer-use problems on Linux desktops;
  • installs on a Mac with Python 3.14 without a compiler.

Security

  • A model's shell and file tools can't read the node keys or change what starts the daemon (GHSA-9ff8-vhh2-r2cw).
    • Before: the floors from GHSA-wq93-7fvg-53pj (the macOS sandbox profile, the Linux AppArmor profile, the permission gate's always-denied list) left out the node keys (node/node.key and node/instance.key, which paired apps pin), the MCP server store (data/mcp_servers.json, started at boot), the xAI sign-in tokens (xai_oauth.json), the cron jobs' PATH shims (data/job-python/), lcm.db (each conversation's workspace and profile), config_pins.yaml, the file checkpoints, the custom agent profiles, and what starts Prometheus: its LaunchAgent plist, its systemd unit and drop-in folder, and the install it runs from. Your cron jobs, the daemon's worker and coding-run launches and command hooks ran as login shells, outside the floors, so a login file a model's shell had written ran there.
    • Now: the keys, the MCP store and the xAI tokens can't be read or written, and the rest can't be written, by a shell or a file tool, wherever PROMETHEUS_NODE_DIR, PROMETHEUS_DATA_DIR or PROMETHEUS_CONFIG_DIR put them. A file tool's write to a profile asks the owner. The startup files covered are the plist, the unit and its drop-ins, the deploy's venvs and clone (~/prometheus-venvs, ~/prometheus-deploy/scripts and src), a pipx or uv tool install, and the install the daemon runs from. Reading and running them still works. Each stored MCP server is checked again before it starts, and one that fails is skipped and named in the log. A checkpoint never copies a link or Prometheus's own files into its store. A restore writes only inside the conversation's own workspace, never through a link, never into Prometheus's own files and never with setuid, setgid or sticky bits.
    • Behaviour change: your cron jobs, worker and coding-run launches and command hooks run bash --noprofile --norc -c, and BASH_ENV and ENV are dropped, even from a hook's env_allowlist. A job or hook that relied on its login profile for PATH or a variable must set that up itself, for example with source ~/.profile at the start of the command. A stored MCP server saved before a check was added may need saving again.
    • Known limit: files your own programs read later (your shell's login files, an editor's or another tool's config and plugins) are yours, not Prometheus's, and the floor does not cover them.
    • Linux: install the new AppArmor profile and reload it (sudo cp deploy/apparmor/prometheus-bash /etc/apparmor.d/prometheus-bash then sudo apparmor_parser -r -W /etc/apparmor.d/prometheus-bash). Until you do, the shell keeps its old floor and the daemon reports the profile as stale at startup, with that command. An install outside the profile's default places is named at startup. macOS needs nothing.
  • An agent profile limits what a conversation can run (GHSA-qgfv-m38j-2c6w, #860). Before, a model that named a tool its profile left out (research has no file writes; exclude_tools: [bash] drops bash) ran it, under the same approvals and floors as the full profile. Now such a call is refused with an error naming the profile. A subagent gets only the tools both its type and the conversation's profile allow, task_create runs a shell command (local_bash, poll) only when the profile includes bash, and a profile that leaves any tool out starts no worker process (task_create local_agent, sessions_spawn); its subagents keep the limits instead. Deferred tools the profile allows, and tools left out only by max_tool_schemas, still run.
  • Dependencies in the lock, for published advisories: authlib 1.9.1 and dnspython 2.9.0 (#867; CVE-2026-96760, CVE-2026-114614 to CVE-2026-114618, CVE-2026-114620, CVE-2026-114605), joserfc 1.7.7 (#875; CVE-2026-119284), pyasn1 0.6.5 (CVE-2026-119194 to CVE-2026-119197) and graphql-core 3.3.1 (CVE-2026-119286). All reach Prometheus only through the evals extra, and pyasn1 also through the google provider.

Identity and pairing (#864, #865, #866)

  • Identity check: POST /api/identity/challenge lets a paired app check it reached the Prometheus it paired with before it sends its token. The daemon signs the app's 32-byte nonce with its node key, binding the instance id, the node key, and on the TLS port the certificate's SPKI digest. No token, no cookie, nothing written; hello's rate limit. FOUNDATION is now Version 4: the node key may sign identity challenges, and is still never proof of the user (the bearer token is).
  • Pairing: every route a device joins through (an approved request, an owner code, same-Mac pairing) now hands it instance_id and node_pub, the key it pins for the identity check. A request that sends match_code_version: 2 gets a 4-digit code that also covers the node key, so the owner's check confirms it; requests that don't send it get the same code as before. Contract pairing/0.2.
  • Status: GET /api/status gains name and fp, the same values GET /api/hello answers, beside node_pub and instance_id, so a client paired with several Prometheus instances can tell them apart. Additive only; /health still carries no identity.

Voice on another machine (#862, #876)

  • Speech-to-text and text-to-speech can use a voice server on another machine. Set stt.url (an OpenAI-compatible /v1/audio/transcriptions server) or tts.url (/v1/audio/speech); naming just the server (http://127.0.0.1:8001) is enough, and whisper.url is accepted for stt.url. The key comes from the variable named in api_key_env, else PROMETHEUS_STT_API_KEY / PROMETHEUS_TTS_API_KEY, read from the environment or the env file and never from prometheus.yaml. With tts.voice empty, now the default, the server picks its own voice.
  • If the server can't be reached, that request runs on the local engines and leaves one log line. A refused key or a 400 (an unknown voice or model) also falls back to local, but logs an ERROR. oara doctor shows whether each configured server answers. With no url set, voice runs locally as before. This covers Telegram and Discord voice notes, Telegram voice replies, oara --voice and the tts tool.

Models and setup

  • llama.cpp with an API key (#877): a llama.cpp server started with --api-key can be used as the main model, in a router rule or fallback, and as a named backend. Name the variable that holds the key in api_key_env (the key itself goes in the env file); it is sent as a bearer token on every request, including the probes, and never logged. A named variable that is not set is an error naming it. With no key set nothing changes. An api_key_env on an Ollama entry is still refused, since Ollama takes no key.
  • Setup says what it found (#868): when Prometheus looks for an AI app on this computer (GET /api/setup/detect) or on one you name (POST /api/setup/probe), it says which app answered (Ollama, LM Studio, llama.cpp, vLLM or another OpenAI-compatible app), which models it serves, and why none is usable when none is: too short a context window, nothing loaded, no chat model, needs a key, or no answer. Configure checks the chosen model the same way. A model needs a window of at least 24,576 tokens. GET /api/setup/status lists local_probe in features.
  • Doctor (#861): the Config check looks for prometheus.yaml where the daemon does (--config, then the checkout's config/prometheus.yaml, then $PROMETHEUS_CONFIG_DIR, default ~/.prometheus) and names the file it found. Before, a daemon running on ~/.prometheus/prometheus.yaml logged "config/prometheus.yaml not found" at every start.
  • Config (#873): five keys nothing read are gone from the shipped prometheus.yaml: model.fallback_provider and model.fallback_url (a fallback model is set under model.fallback), and infrastructure.gpu_host, mini_host and mini_port. oara setup no longer writes model.fallback_url, and the env file oara init writes no longer suggests PROMETHEUS_TELEGRAM_CHAT_IDS; the chats Telegram answers in are gateway.allowed_chat_ids. An existing config with these keys keeps working.

What the model is sent and what is kept

  • No account name or machine layout in every prompt (#853, #856): hosted models are sent paths under your home folder as ~/… instead of absolute paths, and tool descriptions no longer name a person (the brain-vault tool says "the owner's second brain"). They no longer receive this machine's hardware, services and network (the anatomy section, which could include tailnet peer names) in every prompt; they get a pointer to the anatomy tool and call it when a task needs the layout. anatomy.include_in_hosted_prompt: true restores the old behaviour. Local models are unchanged.
  • A purged chat's facts go too (#854): purging a chat now also retracts what was learned from it. A fact learned only from that chat is deleted; a fact another chat also supports stays without it as a source; the wiki is rebuilt, insight pages that used a removed fact are deleted, and filed answers that mention one are listed for review and never touched. The preview gives each removal's reason. A failed retraction purges nothing.
  • Daily briefing (#859): with gateway.briefing_chat_id unset, the briefing goes to your private chat (gateway.owner_user_ids, else the private chats in gateway.allowed_chat_ids), then to TELEGRAM_CHAT_ID, and otherwise nowhere, with an error. Before, it went to the first allowed chat, which could be a group. A briefing_chat_id you set is used as before.

Computer use (#847–#852)

  • No thumbnail is taken of a window that may show a GTK4 password field: GTK4 reports a password entry as an unnamed text field, which the password check missed, so a picture of a GTK4 sign-in window was sent to Beacon. Any unnamed text field now skips the picture.
  • A text field is no longer described by the text it holds. GTK4 names a text view by its contents, so a document's text reached the desktop prompt, the action log and every Beacon. Such fields are now named by their kind alone.
  • An app whose process name Linux cuts at 15 characters (GNOME Terminal is "gnome-terminal-") can be used after you pick it.
  • A GTK4 menu button ("Main menu", "Open", "View") can be clicked by name: only the toggle that acts is offered, not the wrapper around it.
  • When an app pick ends without being turned off (the daemon restarted, or the pick expired after 8 hours), the action log says so, and a reconnecting Beacon no longer shows the app as picked.

Also

  • Install: a Mac on Python 3.14 installs oara-prometheus without a compiler. watchdog has no macOS wheel for 3.14, so pip and uv built it from source there and needed Apple's command line tools. It is now skipped on exactly that platform, and a file_watch task checks its folder twice a second instead of being notified. Linux, and Macs on Python 3.11 to 3.13, keep watchdog as before. CI now checks that a Mac installs from wheels alone (#872).
  • Docs: a contract for pieces (docs/contracts/pieces.md, pieces/0.1), the services Prometheus connects out to, such as a voice box or a model server on another computer: found on the local network or at an address you type, approved once, its key pinned and checked, with fallback when it stops answering. Nothing is built from it yet.
  • Tests: the worker end-listener test no longer fails now and then under full-suite load (#783, #863).

Upgrading from 0.9.17

  • Linux with the AppArmor profile: install the new profile and reload it BEFORE restarting the daemon. Until you do, the daemon reports a STALE PROFILE at ERROR:
    sudo cp deploy/apparmor/prometheus-bash /etc/apparmor.d/prometheus-bash
    sudo apparmor_parser -r -W /etc/apparmor.d/prometheus-bash
    
  • Cron jobs and command hooks no longer run as a login shell. If one relied on ~/.profile or ~/.bash_profile for PATH or a variable, add source ~/.profile at the start of its command, use an absolute path, or (for a hook) name the variable in its env_allowlist.
  • Stored MCP servers are checked again at boot. One that fails is skipped and named in the log; fix what the log names and save it again (POST /api/mcp/servers).
  • Agent profiles now limit what a conversation can run. A custom profile that leaves a tool out really removes it, and a profile that leaves any tool out starts no worker process.