Notes 16
Capabilities, vulnerabilities and disclosed behavior worth knowing. Nothing here works against the person who installs it, so notes never change the verdict.
Persistence
mediumNoteConfirmed on reviewhigh confidenceThe path install command, run by the user, appends an export line to the user's zsh/fish/bash profile; this is disclosed in the command's help and is a user-invoked step.
rc = Path("~/.zshrc").expanduser()vector:code
Reviewer's reasoning
The path install step appends an export line to the shell profile only when the user runs 'path install', which its help text discloses. It is a user-invoked step.
Persistence
mediumNoteConfirmed on reviewhigh confidenceThe service install command, run by the user, installs a unit that starts the console at login; the command discloses this in its output.
installed — console at http://{host}:{port}/ (starts at login)vector:code
Reviewer's reasoning
The service install is run by the user and its output discloses that the console starts at login. This is a user-invoked step with a disclosed effect.
Persistence
mediumNoteConfirmed on reviewlow confidenceThe save_config method writes the user's chosen memory configuration into agent-memory-os.json under the Hermes home, an undeclared but setup-driven write that the user triggers through hermes memory setup.
path.write_text(json.dumps(current, indent=2) + "\n", encoding="utf-8")
vector:code
Reviewer's reasoning
save_config writes the user's own chosen settings into a config file under the Hermes home, and it runs only when the user goes through Hermes setup. This is a user-driven, setup-scoped write.
Persistence
mediumNoteConfirmed on reviewmedium confidenceThe install_shim function writes a plugin shim into the Hermes plugins directory, a control-plane location, but only when the user runs the agent-memory hermes install command.
(target / "__init__.py").write_text(_SHIM_INIT, encoding="utf-8")
vector:code
Reviewer's reasoning
The shim is written only when the user runs the 'hermes install' command. It is a user-invoked, disclosed plugin registration.
Data exfiltration
mediumNoteConfirmed on reviewmedium confidencejoin_with_code sends this node's sync token inside the encrypted envelope to the peer URL the user passes to the join command; the credential goes to a service the user directs it to, so it is a note.
"sync_token": own_sync_token,
vector:code
Reviewer's reasoning
The sync token goes only to the peer URL the user passes to the join command, and that user chose the host. This is a credential sent to a host the user directed it to, so it is not abuse.
Persistence
mediumNoteConfirmed on reviewmedium confidenceThe service installer writes a per-user LaunchAgent or systemd unit that relaunches the web console at login; the user runs this install step, and the unit is the declared purpose, so it is an undeclared-but-documented persistence note.
path.write_text(render_launchd_plist(config))
vector:code
Reviewer's reasoning
The service installer writes a login unit only when the user runs the install step, and the unit is the declared console-service purpose. It is a user-invoked, disclosed step.
Data exfiltration
mediumNoteConfirmed on reviewmedium confidencesync push_to_peer exports the local memory bundle and POSTs it to a registered peer URL; this is the declared mesh sync feature, with the scope set by the peer policy, so it is a disclosed note.
response = _http(base_url.rstrip("/") + "/api/sync/import", token=peer_token, post=payload)vector:code
Reviewer's reasoning
The bundle is pushed only to a peer the user registered, and the scope is set by the peer policy. This is the declared mesh sync feature and a user-directed destination.
Credential access
mediumNoteConfirmed on reviewmedium confidence_http attaches the peer bearer token to requests for any http:// peer URL, so a peer token travels in plaintext over HTTP; this is a hygiene weakness the user opts into when registering the peer.
request.add_header("Authorization", f"Bearer {token}")vector:code
Reviewer's reasoning
The bearer token is attached to any http:// peer URL the user registered, so the credential goes to a host the user chose. Sending it in plaintext is a hygiene weakness, not abuse.
Supply chain
lowNoteConfirmed on reviewmedium confidenceThe Smithery launch command runs 'uv run --with agent-memory-os[mcp]', fetching the publisher's own PyPI package unpinned at launch; this is a note because it is the publisher's package and the user's client starts it.
'--with', 'agent-memory-os[mcp]'
vector:code
Reviewer's reasoning
The Smithery launch fetches the publisher's own PyPI package unpinned when the user's client starts the server, which is the publisher's own launch path and a carve-out under test 5. Nothing steers the agent against the user.
Supply chain
lowNoteConfirmed on reviewhigh confidenceThe update command runs pip install -U of the publisher's own agent-memory-os package with an unpinned version after user confirmation; this is a note on an unpinned install of the publisher's package.
cmd = [sys.executable, "-m", "pip", "install", "-U", "agent-memory-os[full]"]
vector:code
Reviewer's reasoning
The update command installs the publisher's own package only after the user is prompted unless --yes is passed, so the user opts into the step. This is an unpinned install of the publisher's own package, which is a note.
Privilege escalation
lowNoteConfirmed on reviewmedium confidenceThe team rename CLI exposes a --yes flag that skips the confirmation prompt; it is a user-invoked option and is noted only, not an agent-driven bypass.
rename: skip the confirmation prompt
vector:code
Reviewer's reasoning
The --yes flag on team rename is a user-invoked option described in its help text, so the user decides to skip the prompt. The agent is not steered to do so.
Privilege escalation
lowNoteConfirmed on reviewmedium confidenceThe update --team flag makes the artifact send the sync token to every opted-in peer to trigger a remote self-update of agent-memory-os; the user invokes this and the peer must opt in.
url.rstrip("/") + "/api/maintenance/update-run?confirm=update",vector:code
Reviewer's reasoning
The team update is user-invoked via the --team flag, and each peer must opt in with AGENT_MEMORY_ALLOW_TEAM_UPDATE=1 before it acts. The peer update is the artifact's own update path, so it is a note under the carve-outs and test 4.
Privilege escalation
lowNoteConfirmed on reviewmedium confidenceThe fleet console POSTs a self-update with a supplied confirm token to every managed node, which upgrades agent-memory-os and restarts each node; the user triggers this explicitly and the nodes' operators opted in by granting manage.
/api/maintenance/update-run?confirm=update
Reviewer's reasoning
The fleet update is triggered explicitly by the fleet admin who holds the console key, and each node is granted manage by its operator. The user invokes it, so it is a note under test 4.
Supply chain
lowNoteConfirmed on reviewmedium confidenceThe fleet console's trigger_on sends an update action to peer nodes that makes each node upgrade itself and restart; the update is gated by the operator's manage grant, so it is a disclosed note-level self-update of the publisher's own product.
itself and restarts; each node's operator opted in by granting 'manage').
vector:code
Reviewer's reasoning
The update action is run only when the fleet admin triggers it, and it is gated by each node's operator granting manage. The self-update is the publisher's own product and is disclosed in the code comments.
Supply chain
lowNoteConfirmed on reviewmedium confidencesystemd_self_update runs pip to upgrade the publisher's own package unpinned, then restarts itself; this is the artifact's own update path and is a note.
"pip", "install", "--upgrade", "agent-memory-os"
vector:code
Reviewer's reasoning
systemd_self_update upgrades the publisher's own package from the update path the user invokes, and the carve-out covers the artifact's own update path. It is not a bypass of consent.
Supply chain
lowNoteConfirmed on reviewmedium confidenceA peer holding the sync token can trigger this node's self-update from the package index and restart it when the operator has opted in; this is an update of the artifact's own code and is noted only.
Team-wide updates: a peer's sync token may trigger THIS
Decoded
# Team-wide updates: a peer's sync token may trigger THIS node's self-update only when the operator opted in.
trigger:conditionalvector:code
Reviewer's reasoning
The peer-triggered update is gated on the operator setting AGENT_MEMORY_ALLOW_TEAM_UPDATE, and the update_run path upgrades the artifact's own package and restarts its own console, which is the self-update carve-out in test 5. The claim matches the code, so the finding stays a note.