Files
emozilla f413c2ee9a fix(secrets): delete old secret-store copies instead of moving them
The agent can write cache/ and runtime/, so a file there may be a link or a
forged record. Hermes now deletes a Bitwarden or 1Password cache in cache/, or
a Photon sidecar record in runtime/, on first use without reading it, and
reads and writes only the copy at the top of the profile home. All four stores
are regenerated on use.

- Remove utils.adopt_legacy_file and the paths built on it.
- home_data_layout: split SECRET_CACHE_STORES into SECRET_CACHE_FILES and
  LEGACY_SECRET_CACHE_PATHS.
- Backups leave out the four top-level names; the old slashed entries never
  matched a root entry.
- The dashboard Files API takes its hidden names from the shared list.
2026-10-30 13:46:07 -04:00

26 lines
1.3 KiB
Python

"""Machine-specific state excluded from portable home transfers."""
from __future__ import annotations
# Dependency generations, downloaded tools, and partial transfers belong to this machine.
PM_RUNTIME_ROOT_DIRS = frozenset({"installs", "tools", "cache"})
# Bitwarden / 1Password secret-value caches and the Photon sidecar auth token, as POSIX paths relative to
# a home. They sit at its top, where a sandbox can hide a file by path (a writable folder above a hidden
# file can be renamed). Hermes regenerates them on use, so backups leave them out.
SECRET_CACHE_FILES = ("bws_cache.json", "bws_cache.enc.json", "op_cache.json", "photon-sidecar.json")
# Where earlier releases kept the same files, in folders the agent can write. Hermes deletes a copy
# there unread on first use.
LEGACY_SECRET_CACHE_PATHS = (
"cache/bws_cache.json", "cache/bws_cache.enc.json", "cache/op_cache.json", "runtime/photon-sidecar.json",
)
# Credential stores for every guard that lists them.
SECRET_CACHE_STORES = (*SECRET_CACHE_FILES, *LEGACY_SECRET_CACHE_PATHS)
def profile_root_entry(parts: tuple[str, ...]) -> str | None:
"""Locate a home-root entry without excluding same-named skill/plugin data."""
if len(parts) >= 3 and parts[0] == "profiles":
return parts[2]
return parts[0] if parts else None