Skip to content

fix(gateway): deliver profile-scoped cache media on symlinked HERMES_HOME - #54060

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-be61e505
Jun 28, 2026
Merged

fix(gateway): deliver profile-scoped cache media on symlinked HERMES_HOME#54060
teknium1 merged 1 commit into
mainfrom
hermes/hermes-be61e505

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Generated images under a profile gateway's cache (profiles/<name>/cache/images/...) now deliver natively to Telegram/Discord even when HERMES_HOME is symlinked under a denied system prefix.

Root cause: In the reporter's deployment HERMES_HOME=/opt/data (symlink → /root/.hermes) and HOME=/opt/data/home. The model emits /opt/data/profiles/<profile>/cache/images/foo.png, which resolve()s to /root/.hermes/profiles/<profile>/cache/images/.... Two gates then both fail:

  1. The static MEDIA_DELIVERY_SAFE_ROOTS only covers the active HERMES_HOME's top-level cache/images — not per-profile cache dirs.
  2. The resolved path lands under the /root deny prefix, and the root-home exception only fires when the denied prefix is $HOME (here $HOME=/opt/data/home, not /root).

validate_media_delivery_path returns None → gateway logs Skipping unsafe MEDIA directive path → silent drop.

This is a reopened regression: #34485 added top-level cache/images, #38108 added the $HOME exception — neither covers the profile-scoped + symlink case.

Changes

  • gateway/platforms/base.py: _media_delivery_allowed_roots() now also enumerates per-profile cache roots — <root>/profiles/*/cache/{images,audio,videos,documents,screenshots} — at check time (so profiles created after startup are covered). Allowlist match runs before the denylist, so the profile artifact delivers regardless of the /root interaction.
  • tests/gateway/test_platform_base.py: regression test for the symlinked-HERMES_HOME + profile layout, plus a guard that profile-dir credentials (auth.json) stay blocked.

Validation

Path (reporter's layout) Before After
profiles/<name>/cache/images/gen.png ❌ dropped (None) ✅ delivers
profiles/<name>/auth.json blocked blocked
/etc/passwd blocked blocked

93/93 media tests in tests/gateway/test_platform_base.py pass. New test confirmed to fail without the fix (genuine guard).

Fixes #31733.

Infographic

profile-scoped-media-delivery

…HOME

Generated images under a profile gateway's cache (profiles/<name>/cache/
images/...) were silently dropped from Telegram/Discord delivery when
HERMES_HOME is symlinked under a denied prefix (e.g. /opt/data ->
/root/.hermes) and $HOME is not that prefix. The resolved path lands
under /root (a system denylist prefix), the root-home exception only
fires when the denied prefix IS $HOME, and the static safe-roots list
only covers the active HERMES_HOME's top-level cache — not per-profile
cache dirs. Both gates fail, so validate_media_delivery_path returns
None and the gateway logs 'Skipping unsafe MEDIA directive path'.

_media_delivery_allowed_roots() now also enumerates per-profile cache
roots (<root>/profiles/*/cache/{images,audio,videos,documents,
screenshots}) at check time. Allowlist match runs before the denylist,
so the profile artifact delivers regardless of the /root interaction;
profile-dir credentials (auth.json) stay blocked since they aren't
under a cache subdir.

Reopened regression of #34485/#38108, neither of which covered the
profile-scoped symlink case. Fixes #31733.

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Approved

Adds per-profile cache roots to the media delivery allowlist, fixing the issue where profile gateways with symlinked HERMES_HOME silently dropped deliverable artifacts. The _profile_cache_roots() function dynamically enumerates <root>/profiles/<name>/cache/{images,audio,...} so profiles created after startup are covered. Tests verify both the positive case (profile-scoped paths under denied prefixes are allowed) and the negative case (credentials directly in profile dirs are still blocked).


Reviewed by Hermes Agent

@teknium1
teknium1 merged commit 90d25ad into main Jun 28, 2026
30 checks passed
@teknium1
teknium1 deleted the hermes/hermes-be61e505 branch June 28, 2026 08:07
@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery P1 High — major feature broken, no workaround sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Jun 28, 2026
pai-scaffolde pushed a commit to pai-scaffolde/hermes-agent that referenced this pull request Jun 28, 2026
…HOME (NousResearch#54060)

Generated images under a profile gateway's cache (profiles/<name>/cache/
images/...) were silently dropped from Telegram/Discord delivery when
HERMES_HOME is symlinked under a denied prefix (e.g. /opt/data ->
/root/.hermes) and $HOME is not that prefix. The resolved path lands
under /root (a system denylist prefix), the root-home exception only
fires when the denied prefix IS $HOME, and the static safe-roots list
only covers the active HERMES_HOME's top-level cache — not per-profile
cache dirs. Both gates fail, so validate_media_delivery_path returns
None and the gateway logs 'Skipping unsafe MEDIA directive path'.

_media_delivery_allowed_roots() now also enumerates per-profile cache
roots (<root>/profiles/*/cache/{images,audio,videos,documents,
screenshots}) at check time. Allowlist match runs before the denylist,
so the profile artifact delivers regardless of the /root interaction;
profile-dir credentials (auth.json) stay blocked since they aren't
under a cache subdir.

Reopened regression of NousResearch#34485/NousResearch#38108, neither of which covered the
profile-scoped symlink case. Fixes NousResearch#31733.
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…HOME (NousResearch#54060)

Generated images under a profile gateway's cache (profiles/<name>/cache/
images/...) were silently dropped from Telegram/Discord delivery when
HERMES_HOME is symlinked under a denied prefix (e.g. /opt/data ->
/root/.hermes) and $HOME is not that prefix. The resolved path lands
under /root (a system denylist prefix), the root-home exception only
fires when the denied prefix IS $HOME, and the static safe-roots list
only covers the active HERMES_HOME's top-level cache — not per-profile
cache dirs. Both gates fail, so validate_media_delivery_path returns
None and the gateway logs 'Skipping unsafe MEDIA directive path'.

_media_delivery_allowed_roots() now also enumerates per-profile cache
roots (<root>/profiles/*/cache/{images,audio,videos,documents,
screenshots}) at check time. Allowlist match runs before the denylist,
so the profile artifact delivers regardless of the /root interaction;
profile-dir credentials (auth.json) stay blocked since they aren't
under a cache subdir.

Reopened regression of NousResearch#34485/NousResearch#38108, neither of which covered the
profile-scoped symlink case. Fixes NousResearch#31733.
Jasper6439 pushed a commit to Jasper6439/hermes-agent that referenced this pull request Jul 5, 2026
…HOME (NousResearch#54060)

Generated images under a profile gateway's cache (profiles/<name>/cache/
images/...) were silently dropped from Telegram/Discord delivery when
HERMES_HOME is symlinked under a denied prefix (e.g. /opt/data ->
/root/.hermes) and $HOME is not that prefix. The resolved path lands
under /root (a system denylist prefix), the root-home exception only
fires when the denied prefix IS $HOME, and the static safe-roots list
only covers the active HERMES_HOME's top-level cache — not per-profile
cache dirs. Both gates fail, so validate_media_delivery_path returns
None and the gateway logs 'Skipping unsafe MEDIA directive path'.

_media_delivery_allowed_roots() now also enumerates per-profile cache
roots (<root>/profiles/*/cache/{images,audio,videos,documents,
screenshots}) at check time. Allowlist match runs before the denylist,
so the profile artifact delivers regardless of the /root interaction;
profile-dir credentials (auth.json) stay blocked since they aren't
under a cache subdir.

Reopened regression of NousResearch#34485/NousResearch#38108, neither of which covered the
profile-scoped symlink case. Fixes NousResearch#31733.
habarmc1223-sudo pushed a commit to habarmc1223-sudo/hermes-agent-fluxmem that referenced this pull request Jul 8, 2026
…HOME (NousResearch#54060)

Generated images under a profile gateway's cache (profiles/<name>/cache/
images/...) were silently dropped from Telegram/Discord delivery when
HERMES_HOME is symlinked under a denied prefix (e.g. /opt/data ->
/root/.hermes) and $HOME is not that prefix. The resolved path lands
under /root (a system denylist prefix), the root-home exception only
fires when the denied prefix IS $HOME, and the static safe-roots list
only covers the active HERMES_HOME's top-level cache — not per-profile
cache dirs. Both gates fail, so validate_media_delivery_path returns
None and the gateway logs 'Skipping unsafe MEDIA directive path'.

_media_delivery_allowed_roots() now also enumerates per-profile cache
roots (<root>/profiles/*/cache/{images,audio,videos,documents,
screenshots}) at check time. Allowlist match runs before the denylist,
so the profile artifact delivers regardless of the /root interaction;
profile-dir credentials (auth.json) stay blocked since they aren't
under a cache subdir.

Reopened regression of NousResearch#34485/NousResearch#38108, neither of which covered the
profile-scoped symlink case. Fixes NousResearch#31733.
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…HOME (NousResearch#54060)

Generated images under a profile gateway's cache (profiles/<name>/cache/
images/...) were silently dropped from Telegram/Discord delivery when
HERMES_HOME is symlinked under a denied prefix (e.g. /opt/data ->
/root/.hermes) and $HOME is not that prefix. The resolved path lands
under /root (a system denylist prefix), the root-home exception only
fires when the denied prefix IS $HOME, and the static safe-roots list
only covers the active HERMES_HOME's top-level cache — not per-profile
cache dirs. Both gates fail, so validate_media_delivery_path returns
None and the gateway logs 'Skipping unsafe MEDIA directive path'.

_media_delivery_allowed_roots() now also enumerates per-profile cache
roots (<root>/profiles/*/cache/{images,audio,videos,documents,
screenshots}) at check time. Allowlist match runs before the denylist,
so the profile artifact delivers regardless of the /root interaction;
profile-dir credentials (auth.json) stay blocked since they aren't
under a cache subdir.

Reopened regression of NousResearch#34485/NousResearch#38108, neither of which covered the
profile-scoped symlink case. Fixes NousResearch#31733.
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…HOME (NousResearch#54060)

Generated images under a profile gateway's cache (profiles/<name>/cache/
images/...) were silently dropped from Telegram/Discord delivery when
HERMES_HOME is symlinked under a denied prefix (e.g. /opt/data ->
/root/.hermes) and $HOME is not that prefix. The resolved path lands
under /root (a system denylist prefix), the root-home exception only
fires when the denied prefix IS $HOME, and the static safe-roots list
only covers the active HERMES_HOME's top-level cache — not per-profile
cache dirs. Both gates fail, so validate_media_delivery_path returns
None and the gateway logs 'Skipping unsafe MEDIA directive path'.

_media_delivery_allowed_roots() now also enumerates per-profile cache
roots (<root>/profiles/*/cache/{images,audio,videos,documents,
screenshots}) at check time. Allowlist match runs before the denylist,
so the profile artifact delivers regardless of the /root interaction;
profile-dir credentials (auth.json) stay blocked since they aren't
under a cache subdir.

Reopened regression of NousResearch#34485/NousResearch#38108, neither of which covered the
profile-scoped symlink case. Fixes NousResearch#31733.
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…HOME (NousResearch#54060)

Generated images under a profile gateway's cache (profiles/<name>/cache/
images/...) were silently dropped from Telegram/Discord delivery when
HERMES_HOME is symlinked under a denied prefix (e.g. /opt/data ->
/root/.hermes) and $HOME is not that prefix. The resolved path lands
under /root (a system denylist prefix), the root-home exception only
fires when the denied prefix IS $HOME, and the static safe-roots list
only covers the active HERMES_HOME's top-level cache — not per-profile
cache dirs. Both gates fail, so validate_media_delivery_path returns
None and the gateway logs 'Skipping unsafe MEDIA directive path'.

_media_delivery_allowed_roots() now also enumerates per-profile cache
roots (<root>/profiles/*/cache/{images,audio,videos,documents,
screenshots}) at check time. Allowlist match runs before the denylist,
so the profile artifact delivers regardless of the /root interaction;
profile-dir credentials (auth.json) stay blocked since they aren't
under a cache subdir.

Reopened regression of NousResearch#34485/NousResearch#38108, neither of which covered the
profile-scoped symlink case. Fixes NousResearch#31733.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P1 High — major feature broken, no workaround sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generated images under cache/images may not deliver via Telegram MEDIA, while image_cache works

3 participants