fix(secrets): secret-source re-pull no longer latches an empty snapshot or wipes sibling profiles
Symptom (#102041): under a multiplex gateway the default profile's vault/1Password/
Bitwarden/plugin-sourced credentials vanished for the rest of the process after the first
cron fire or the post-discovery plugin refresh; with the key already in the process env
(systemd EnvironmentFile=) the scope was empty from boot. Every get_secret() read then
failed closed ("No usable credentials", every Telegram sender rejected).
Why: _apply_external_secret_sources marked the home applied after any real fetch, but only
snapshotted names in report.provenance — the NEWLY applied ones. On a re-apply the previous
apply's own write-back makes every key `skipped_existing`, so the snapshot latched to {} and
_hydrate_profile_secret_sources returned that empty snapshot forever. Separately,
reset_secret_source_cache() was process-wide, so one profile's cron re-pull dropped every
sibling's hydrated snapshot (1aa62ceb45 isolated the routed reload but not the reset).
Change:
- env_loader: snapshot every name a source supplied (provenance + skipped_existing) from
the home's effective environment, so a shadowed re-apply keeps the values it had.
- reset_secret_source_cache(hermes_home=None): optional per-home reset; global clear kept
for tests/config edits.
- cron per-fire re-pull and plugins._refresh_secret_sources_after_discovery reset + reload
only the home they resolve to.
- Two invariant tests (red on base) in tests/test_env_loader_secret_sources.py; docs note in
secret-source-plugin.md.
Reported-by: luochen1990
Addresses #102041
This commit is contained in:
@@ -1275,8 +1275,13 @@ class PluginManager(PluginLoaderMixin, PluginDispatchMixin, PluginLedgerMixin):
|
||||
if not enabled_names:
|
||||
return
|
||||
try:
|
||||
reset_secret_source_cache()
|
||||
load_hermes_dotenv()
|
||||
# Reset and reload the SAME home the process (or routed turn) resolves to: under multiplex this
|
||||
# runs at gateway boot after sibling profiles may already have hydrated, and a global clear
|
||||
# wiped their snapshots; a routed discovery must rebuild the profile it just dropped.
|
||||
from hermes_constants import get_hermes_home
|
||||
home = get_hermes_home()
|
||||
reset_secret_source_cache(home)
|
||||
load_hermes_dotenv(hermes_home=home)
|
||||
logger.debug("Re-applied secret sources after plugin discovery for: %s",
|
||||
", ".join(sorted(enabled_names)))
|
||||
except Exception as exc:
|
||||
|
||||
Reference in New Issue
Block a user