# NousResearch/hermes-agent · Gateway staging fix for local media files

## Open now

- Nothing open on GitHub.

These items address a bug where the gateway silently drops safe local media files from certain working directories (e.g. /root) instead of staging them, along with corresponding fixes to ensure proper staging and delivery.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #39063. Verify it addresses your case.
- **Recommended:** read PR #39063 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-39063`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #38106: \[Bug\]: Gateway silently drops safe local MEDIA files from working dirs (e.g. /root) instead of staging them

## Prior & competing PRs

- **#39063: fix(gateway): deliver $HOME deliverables on root-run gateways**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `gateway/platforms/base.py`, `tests/gateway/test_platform_base.py`
  - fix hunks:
  - `gateway/platforms/base.py`

    ```diff
    @@ -967,14 +967,35 @@ def _media_delivery_denied_paths() -> List[Path]:
     
     
     def _path_under_denied_prefix(resolved: Path) -> bool:
    -    """Return True if ``resolved`` lives under a deny-listed system path."""
    +    """Return True if ``resolved`` lives under a deny-listed system path.
    +
    +    One narrow exception: when a denied prefix IS the running user's own home,
    +    the home itself is not treated as denied. ``/root`` is on the system-path
    +    denylist so that a non-root gateway can't deliver another user's home, but
    +    on a root-run gateway ``$HOME=/root`` and the operator's own deliverables
    +    (``/root/work/proposal.docx``) live directly under it. The credential
    +    sub-directories inside home (``~/.ssh``, ``~/.aws``, ...) and Hermes
    +    secrets (``~/.hermes/.env``, ``auth.json``) are *separate, more-specific*
    +    denied paths, so they stay blocked regardless of this exception — it can
    +    only un-block a plain file sitting in the running user's home tree, never a
    +    credential location or another user's home.
    +    """
    +    try:
    +        home = Path(os.path.expanduser("~")).resolve(strict=False)
    +    except (OSError, RuntimeError, ValueError):
    +        home = None
         for denied in _media_delivery_denied_paths():
             try:
                 resolved_denied = denied.expanduser().resolve(strict=False)
             except (OSError, RuntimeError, ValueError):
                 continue
    -        if _path_is_within(resolved, resolved_denied) or resolved == resolved_denied:
    -            return True
    +        if not (_path_is_within(resolved, resolved_denied) or resolved == resolved_denied):
    +            continue
    +        # Allow the running user's own home tree; its credential sub-dirs are
    +        # caught by their own (more-specific) denylist entries above.
    +        if home is not None and resolved_denied == home:
    +            continue
    +        return True
         return False
     
     
    ```
  - `tests/gateway/test_platform_base.py`

    ```diff
    @@ -954,6 +954,120 @@ def test_filter_passes_default_files_through(self, tmp_path, monkeypatch):
             out = BasePlatformAdapter.filter_local_delivery_paths([str(notes)])
             assert out == [str(notes.resolve())]
     
    +    def test_root_home_deliverable_is_accepted(self, tmp_path, monkeypatch):
    +        """The motivating bug (#38106): a root-run gateway has ``$HOME=/root``,
    +        which is on the system-prefix denylist. A plain deliverable the agent
    +        produced in its working dir (``/root/work/proposal.docx``) must still
    +        deliver — the home itself is not a credential location.
    +        """
    +        self._patch_roots(monkeypatch)
    +
    +        fake_home = tmp_path / "root"
    +        workdir = fake_home / "work"
    +        workdir.mkdir(parents=True)
    +        doc = workdir / "proposal.docx"
    +        doc.write_bytes(b"PK\x03\x04")
    +        monkeypatch.setenv("HOME", str(fake_home))
    +        # $HOME is itself on the denied-prefix list, mirroring /root.
    +        monkeypatch.setattr(
    +            "gateway.platforms.base._MEDIA_DELIVERY_DENIED_PREFIXES",
    +            (str(fake_home),),
    +        )
    +
    +        assert (
    +            BasePlatformAdapter.validate_media_delivery_path(str(doc))
    +            == str(doc.resolve())
    +        )
    +
    +    def test_root_home_credential_subdir_still_blocked(self, tmp_path, monkeypatch):
    +        """The $HOME exception must NOT un-block credential sub-dirs inside
    +        home. ``/root/.ssh/id_rsa`` stays denied because ``~/.ssh`` is a
    +        separate, more-specific denylist entry — even when $HOME is itself a
    +        denied prefix.
    +        """
    +        self._patch_roots(monkeypatch)
    +
    +        fake_home = tmp_path / "root"
    +        ssh_dir = fake_home / ".ssh"
    +        ssh_dir.mkdir(parents=True)
    +        key = ssh_dir / "id_rsa"
    +        key.write_bytes(b"[redacted:private-key]
    ```
- **#38108: fix(gateway): stage safe local MEDIA files before delivery**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#39063`

## Fix status

- Merged to main in PR #39063 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-04`.
