Skip to content

feat(tools): add Podman as a supported terminal backend - #8158

Closed
ksze wants to merge 2 commits into
NousResearch:mainfrom
ksze:feat/podman-integration
Closed

feat(tools): add Podman as a supported terminal backend#8158
ksze wants to merge 2 commits into
NousResearch:mainfrom
ksze:feat/podman-integration

Conversation

@ksze

@ksze ksze commented Apr 12, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds support for Podman as a terminal backend. Very similar to the Docker backend, but with explicit options for rootless vs rootful, user namespace remapping, and Podman with extended privileges.

Related Issue

#4084

Fixes #

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • Refactored a few helper functions common between Docker and Podman backends into tools/environments/utils.py
  • Added a tools/environments/podman.py module, with a PodmanEnvironment class
  • Added "podman" as valid terminal backend env_type option in tools/terminal_tool.py
  • Added Podman-specific config options to hermes_cli/config.py
  • Updated all usages of _create_environment function to support Podman as a terminal backend
  • Updated documentation - basically every relevant places that talk about Docker was supported, we add Podman with some Podman-specific explanation where applicable.

How to Test

  1. Install Podman in addition to Hermes
  2. Configure Hermes to use Podman as the terminal backend, in a similar way to how you would configure Docker as the terminal backend.
  3. Ask Hermes to run something in the terminal.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform:

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

@teknium1

Copy link
Copy Markdown
Contributor

Hey @ksze, thanks for taking this on — Podman support is something we've wanted and the core architecture here is solid. Inheriting from DockerEnvironment and extracting shared helpers into utils.py is the right approach.

A few things to address before we can move forward:

1. Unrelated changes need to be removed

The branch has accumulated a lot of changes unrelated to Podman — likely from your fork diverging from main over time. These need to be stripped out so the PR only contains Podman work. The big ones:

  • Deleted gateway platform setup functions (Signal, Email, SMS, DingTalk, Feishu, WeCom, WeCom Callback) — these are removed from _GATEWAY_PLATFORMS in setup.py and their helper functions deleted. These are real platforms that exist on main.
  • Removed Mistral TTS provider from setup.py
  • Removed watch_patterns from terminal tool schema, replaced with check_interval — this is a separate feature
  • Removed watcher_user_id/watcher_user_name from process session watchers
  • Rewrote _curses_prompt_choice to inline curses instead of using the shared curses_radiolist from curses_ui
  • Rewrote _offer_launch_chat from os.execvp to direct cmd_chat() call
  • Replaced get_anthropic_key() with raw env var checks in doctor.py and status.py
  • Rewrote path security in skills_tool.py — removed validate_within_dir / has_traversal_component imports
  • Replaced token estimation import in skills_tool.py with an inlined function
  • Changed interrupt mechanism in code_execution_tool.py (is_interrupted_interrupt_event)
  • Removed test classes from test_auxiliary_client.py (model default elimination, payment fallback, connection error tests)
  • Changed MiniMax model lists and disabled their API health checks
  • Removed XIAOMI_API_KEY from doctor.py
  • package-lock.json changes — massive npm dependency additions (playwright, puppeteer plugins, express, etc.)
  • Removed _resolve_hermes_chat_argv helper, changed get_current_session_key in approval.py

The cleanest path: rebase onto current main and ensure only Podman-related hunks survive. Or start a fresh branch from main and re-apply just the Podman changes.

2. Runtime bugs in the Podman code

all() misuseall() takes a single iterable, not two arguments. This appears in 3 places:

# Bug (terminal_tool.py, code_execution_tool.py, file_tools.py):
if isinstance(podman_extra_args, list) and all(podman_extra_args, lambda x: isinstance(x, str)):

# Fix:
if isinstance(podman_extra_args, list) and all(isinstance(x, str) for x in podman_extra_args):

.filter() doesn't exist on Python lists (setup.py):

# Bug:
extra_caps.split(" ").filter(lambda x: len(x) > 0)

# Fix:
[x for x in extra_caps.split(" ") if x]

Missing comma in _PODMAN_SEARCH_PATHS (podman.py) — the first two strings concatenate:

# Bug:
_PODMAN_SEARCH_PATHS = [
    "/usr/bin/podman"        # ← no comma!
    "/usr/local/bin/podman",
    ...
]

# Fix:
_PODMAN_SEARCH_PATHS = [
    "/usr/bin/podman",
    "/usr/local/bin/podman",
    ...
]

Typo: podman_privilged (missing 'e') — appears in terminal_tool.py, code_execution_tool.py, and file_tools.py. Not a functional bug since it's consistent, but should be podman_privileged.

3. Code duplication

The Podman config extraction block is copy-pasted identically into 3 files (terminal_tool.py, code_execution_tool.py, file_tools.py). Consider extracting it into a shared helper — utils.py which you already created would be a natural home for it.

4. About the test failures

You mentioned test failures on vanilla main — that's likely an environment issue (missing optional deps, or pytest-xdist parallel mode causing hangs). Try running with:

python -m pytest tests/ -n0 -q

Or if xdist isn't installed:

python -m pytest tests/ -o "addopts=" -q

The Podman work itself — the PodmanEnvironment class, the utils.py extraction, the config plumbing, setup wizard section, doctor checks, and docs updates — is all headed in the right direction. Once the unrelated changes are stripped and the bugs above are fixed, this should be in good shape. Happy to help if you hit any snags.

malaiwah pushed a commit to malaiwah/hermes-agent that referenced this pull request Apr 12, 2026
Add native Podman support as an alternative to Docker for sandboxed
command execution, implementing the feature requested in NousResearch#4084.

Architecture:
- PodmanEnvironment extends DockerEnvironment via hook methods
  (_resolve_cli_binary, _ensure_cli_available, _get_security_args,
  _get_extra_run_args, _build_run_cmd) — no code duplication
- DockerEnvironment gains 5 overridable hooks for extensibility;
  existing Docker behavior is unchanged
- Podman-specific options: rootful/rootless, --userns mapping,
  --privileged, extra capabilities, extra CLI args

Files changed:
- tools/environments/podman.py (new) — PodmanEnvironment class
- tools/environments/docker.py — add hook methods for subclassing
- tools/terminal_tool.py — wire podman env_type, config keys
- tools/code_execution_tool.py — podman image selection + config
- tools/file_tools.py — podman image selection + config
- gateway/run.py — podman env var mappings from config.yaml
- cli.py — podman defaults and env var mapping
- hermes_cli/doctor.py — podman availability check
- batch_runner.py — podman image pull/check using find_podman()
- tests/tools/test_podman_environment.py (new) — 15 unit tests

Fixes all bugs from PR NousResearch#8158 review:
- all() two-arg misuse → generator expression
- .filter() on list → list comprehension (not needed here)
- Missing comma in search paths → verified in tests
- podman_privilged typo → podman_privileged throughout
- No code duplication — single podman config block per file

Based on the approach in NousResearch#8158 by @ksze, with architectural feedback
from @teknium1's review.

Closes NousResearch#4084

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@malaiwah

Copy link
Copy Markdown
Contributor

Hey @ksze — thanks for pioneering this. I've submitted #8391 as a clean reimplementation that builds on your approach and addresses @teknium1's feedback. The core idea of extending DockerEnvironment is preserved, but with proper inheritance via hook methods instead of bypassing __init__.

Your work on the setup wizard flow, doctor checks, and config plumbing was particularly helpful as reference. Credited in the PR description and commit message.

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have backend/docker Docker container execution comp/tools Tool registry, model_tools, toolsets labels Apr 28, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Note: PR #8391 appears to be a cleaner reimplementation addressing review feedback on this PR.

@benbarclay

Copy link
Copy Markdown
Collaborator

Hi @ksze — thanks for the work on this, but closing because Podman support landed via a different (lighter) approach a few days after this PR opened, and the design here is no longer the right fit.

Timeline:

What landed instead: a drop-in approach — find_docker() in tools/environments/docker.py checks HERMES_DOCKER_BINARY first, then docker on PATH, then podman on PATH. Since Podman is CLI-compatible with Docker for our use-case, no separate PodmanEnvironment class is needed and the existing docker_* config keys all work transparently for Podman.

Status of the features this PR offered:

  • ✅ Rootless: covered via docker_run_as_host_user: true (passes --user to the runtime).
  • ✅ Binary discovery: covered via find_docker() fallback or HERMES_DOCKER_BINARY override.
  • ✅ Extra flags (security-opt, userns, etc.): covered via docker_extra_args — accepts arbitrary --userns keep-id, --security-opt, etc.
  • ⚠️ First-class userns config key: not present. If users want this as a named knob with validation rather than via docker_extra_args, a small focused PR (~20 LOC in docker.py) would be welcome — but it should extend docker.py, not introduce a parallel runtime class.

The 4,023-line standalone PodmanEnvironment design here would now leave the codebase with two execution-isolation classes to keep in sync (each docker-side feature would need a parallel podman-side feature), a duplicated config surface (podman_* mirroring docker_*), and parallel docs in every page that mentions container backends — for behavior that's already reachable through the existing path.

Same applies to #8391 — closing both. If you'd like to pick up the userns config knob or any other gap as a focused PR, please open a fresh one. Thanks again for taking the time on this one.

@benbarclay benbarclay closed this May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend/docker Docker container execution comp/tools Tool registry, model_tools, toolsets P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants