β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β‘β β β β β’ β β β β β β β β β β β β β β β
β β β β β β β β β β β β β β β β β’β β β β β β β β β β β β β ⠻⣦β‘β β’Έβ£β β β β β β β β β β β β β β
β β β β ⣠⣦⣀β£β£β£€β£€β£β‘β β£β£ β‘β β β β β β β €β β β£β£β£»β£Ώβ£Άβ£Ύβ£Ώβ£¦β£β’Ώβ£β β β β β β β β β β β β β
β β β β Έβ Ώβ’Ώβ£Ώβ£Ώβ£Ώβ£―β£β£Ώβ£Ώβ£Ώβ£Ώβ£β£β β β β β β β£ β£Άβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£·β£€β‘β β β β β β β β β β
β β β β β β β β β’Ώβ£Ώβ£Ώβ‘Ώβ’Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£β ’β β’ β‘Ύβ’»β£Ώβ£Ώβ£Ώβ£Ώβ‘β β β β β β’Ώβ£Ώβ£Ώβ£―β‘»β£Ώβ‘β β β β β β β β β
β β β β β β β β β β β β β β β β’Ώβ£Ώβ£Ώβ£Ώβ£·β£β β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘β β β β β β’Έβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£·β£β‘β β β β β β β
β β β β β β β β β β β β β β β β β£Ώβ£Ώβ£Ώβ£·β£β’§β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£β β β β β’β β β β β Ώβ£Ώβ£Ώβ£Ώβ‘β β β β β β
β β β β β β β β β β β β β β β β β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘β β’»β‘β’Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£§β£β£β β β’Άβ£β£β β β β’»β Ώβ β β β β β β
β β β β β β β β β β β β β β β β£Έβ£Ώβ£Ώβ£Ώβ£Ώβ£Ύβ β β β »β£β£β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘Ώβ£·β£¦β‘β β β β β β β
β β β β β ²β£β β β£β‘€β €β β β β’ β£Ώβ£Ώβ£Ώβ‘Ώβ£Ώβ β β β β Ίβ’⣑⣴⣿⣿⣿⣿⣿⣿⣿⑿Ⓙ⣿⣿⣿⣢⣿⣿⣿⣢⣢β‘β β β
β β β β ⒠⣿⣴⣿⣷⣢⣦⣀β‘β β’Έβ£Ώβ£Ώβ£Ώβ β β β β β’⣴⣿⣿⣿⣿⣿β β’Ώβ£Ώβ£Ώβ£Ώβ£·β β Ήβ£Ώβ£Ώβ Ώβ Ώβ β »β Ώβ£Ώβ β β β
β β β β£ β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£·β£―β‘β’Έβ£Ώβ£Ώβ£Ώβ β β β β’β Ύβ£»β£Ώβ£Ώβ£Ώβ β β β β£Ώβ£Ώβ£Ώβ£Ώβ‘β β β£β£β‘β β’ β‘β β β β β
β β β’Έβ£β£½β£Ώβ£―β β β’Ήβ£Ώβ£Ώβ£Ώβ‘β Όβ£Ώβ£Ώβ£Ώβ£β β β β β’°β£Ώβ£Ώβ£Ώβ£Ώβ‘β β β β£Έβ£Ώβ£Ώβ£Ώβ‘β β’β£€β£Όβ£Ώβ£·β£Ύβ£·β‘β β β β
β β’β£Ύβ£Ώβ‘Ώβ β β β β’Έβ£Ώβ£Ώβ£Ώβ£Ώβ‘Ⓙ⣿⣿⣿⣦β β β β’Ίβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£β β β£Ώβ£Ώβ£Ώβ£Ώβ‘β ⣿⣿⣿⣿⠿⣿⣿⑿⣦β β β
β β’»β£Ώβ β β β β β’ β£Ώβ£Ώβ£Ώβ‘β‘Ώβ β β’»β£Ώβ£Ώβ£Ώβ£·β£€β‘β β£·β »β£Ώβ£Ώβ£Ώβ£Ώβ£·β£Όβ£Ώβ£Ώβ£Ώβ£Ώβ£β£Ύβ£Ώβ£Ώβ£Ώβ β β’Όβ£Ώβ£Ώβ£Ώβ£β β
β β β β β β β β β’Έβ£Ώβ£Ώβ£Ώβ‘β β β β β β’Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£·β£Ύβ£β‘β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ β£Ύβ£Ώβ£Ώβ£Ώβ£β β β β β »β£Ώβ‘·β
β β β β β β β β β’Έβ£Ώβ£Ώβ£Ώβ£·β£β β β β β β β »β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘β β β£β£Ώβ£Ώβ£Ώβ‘β β β β β β β
β β β β β β β β β β »β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Άβ£€β£€β£€β£β£ β£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ‘Ώβ β β β’β£Ώβ£Ώβ£Ώβ‘β β β β β β β
β β β β β β β β β β β β Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£β£ β£€β£€β£Άβ£Ώβ£Ώβ£Ώβ β β β β β β β β
β β β β β β β’β£ β£€β£β β β’Άβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£Ώβ£β‘β β β β β β β β β
β’β£β β£ β£β‘ β ⣿⣿⣿⣿⣢⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣴⣿⣷⣦β£β£β’Ώβ‘½β’»β£¦
β »β Άβ Ύβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ Ώβ
Agentic Frameworks Done The Right Way
One config file. True isolation. Full control.
Check out the official blog post or on medium
Most AI agent frameworks run everything in a single process with application-level permission checks. If the agent can execute code, it can access anything the process can access. Security is an afterthought.
Hydra agents run in containers. Each agent is isolated at the OS level. They can only see what you explicitly mount. Bash commands execute inside the container, not on your host. Agent's cant break things even if they wanted to.
ββββββββββββββββ
β hydra exec β β You are here (interactive CLI)
ββββββββ¬ββββββββ
β
ββββββββββββββββββΌββββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β Container β β Container β β Container β
β β β β β β
β Claude Code β β Claude Code β β Claude Code β
β (Agent SDK)β β (Agent SDK)β β (Agent SDK)β
β β β β β β
β /workspace/ β β /workspace/ β β /workspace/ β
β β agent/ β β β agent/ β β β agent/ β
β β extra/* β β β extra/* β β β extra/* β
β β ipc/ β β β ipc/ β β β ipc/ β
βββββββββββββββ βββββββββββββββ βββββββββββββββ
main dev other
Each container: isolated filesystem, own CLAUDE.md,
own session state, own mem0 memory, own IPC namespace.
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Optional: hydra up (background orchestrator) β
β β
β β’ Telegram bot listener β
β β’ Task scheduler (cron, interval, once) β
β β’ IPC message routing β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Other key differences:
| Typical Agent Framework | Hydra | |
|---|---|---|
| Security model | Permission checks in code or prompt | OS-level container isolation |
| Bash commands | Runs on host | Runs in ephemeral container |
| File access | Full Access?? | Only mounted paths exist in the container. Can be read-only |
| Agent isolation | Shared memory, one process | Separate containers, resource allocation |
| Configuration | Multiple files, env vars | Single hydra.yaml |
| Runtime | Claude API wrapper | Claude Agent SDK (Claude Code) |
- Create your agents via the Hydra CLI or
hydra.yaml - Assign agent resources such as folder mounts, tools, MCP servers, etc.
- Enter interactive sessions inside the containers (running claude code!)
- Exit when finished and the container is stopped, memory and progress remains.
Each agent has its own:
CLAUDE.mdfile (persistent memory/instructions)- Persistent vector memory via
mem0(self hosted or cloud) - Session state (conversation context)
- Filesystem (isolated from other agents)
- IPC namespace (can't send messages as other agents)
The /setup skill handles config creation, .env, agent setup, and container build interactively:
git clone https://github.com/RickConsole/hydra.git
cd hydra
npm install && npm run build && npm link
./container/build.sh
claude # then run /setup inside the sessiongit clone https://github.com/RickConsole/hydra.git
cd hydra
npm install && npm run build && npm link
./container/build.sh
# Add your API key to secrets.env (outside project root, never mounted)
mkdir -p ~/.config/hydra
echo "ANTHROPIC_API_KEY=sk-ant-..." > ~/.config/hydra/secrets.env
# Create a minimal hydra.yaml
cat <<'EOF' > hydra.yaml
version: "1"
project: my-project
agents:
- name: Main
folder: main
EOF
# Create your first agent and start a session
hydra agent create --name "Main" --folder main
hydra exec mainEverything lives in one file: hydra.yaml.
The smallest working config β just a version and one agent:
version: "1"
project: my-project
agents:
- name: Main
folder: mainversion: "1"
project: hydra
# Agents (each runs in its own container)
agents:
- name: Main Assistant
folder: main
container:
mounts:
- host_path: ~/projects
container_path: projects
readonly: false
- name: Dev Helper
folder: dev
bot: mydevbot # Connect to Telegram
chat_id: "-1009876540010" # Telegram chat ID
container:
timeout: 600000
mounts:
- host_path: ~/src
container_path: src
readonly: false
secrets: # Per-agent secrets from secrets.env
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
# Telegram bots
bots:
mydevbot:
name: MyDevBot
token: env:TELEGRAM_BOT_TOKEN # References .env variable
platform: telegram
# Self-hosted memory (requires `hydra infra up`)
memory:
provider: qdrant
qdrant_url: http://localhost:6333
ollama_url: http://localhost:11434
# Security (mount allowlist lives separately for tamper-proofing)
security:
mounts:
non_main_readonly: true
blocked_patterns:
- .ssh
- .gnupg
- password
- secretSensitive values in hydra.yaml use env:VAR_NAME syntax to reference the host environment:
token: env:TELEGRAM_BOT_TOKENThese are resolved at config load time from whatever is in your shell environment or .env file. This is for Hydra's own config (bot tokens, etc.) β not for what gets passed into containers.
Agent containers receive secrets from ~/.config/hydra/secrets.env β a dedicated file that lives outside the project root and is never mounted into any container. This follows the same tamper-proof principle as the mount allowlist.
# ~/.config/hydra/secrets.env
ANTHROPIC_API_KEY=sk-ant-...
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
CUSTOM_TOKEN=abc123A set of base variables (ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, MEM0_API_KEY, QDRANT_URL, OLLAMA_URL, OPENAI_API_KEY, LITELLM_API_KEY) are injected into every container if present. Additional secrets are opt-in per agent:
agents:
- name: Deploy Bot
folder: deploy
container:
secrets:
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEYOnly the keys listed in secrets (plus the base vars) are injected. An agent can't access secrets it hasn't been granted β even if they exist in secrets.env.
Mounts require approval from two independent config files:
hydra.yamlβ declares what an agent wants mounted (lives in the project)~/.config/hydra/mount-allowlist.jsonβ declares what's allowed to be mounted (lives outside the project)
Both must agree for a mount to work. Even if an agent modifies hydra.yaml, it can't grant itself access to paths that aren't pre-approved in the external allowlist. The allowlist is never mounted into containers and can't be reached by agents.
// ~/.config/hydra/mount-allowlist.json
{
"allowedRoots": [
{ "path": "~/src", "allowReadWrite": true, "description": "Source code" }
],
"blockedPatterns": [".ssh", ".gnupg", ".aws", "credentials"],
"nonMainReadOnly": true
}If the allowlist file doesn't exist, all additional mounts are blocked by default.
| Command | Description |
|---|---|
hydra up [-f] |
Start orchestrator (daemon; -f for foreground) |
hydra down |
Stop orchestrator and infrastructure |
hydra status |
Show orchestrator/agent status |
hydra exec <agent> [args] |
Interactive Claude Code session in agent container |
hydra agents |
List configured agents |
hydra agent create --name "X" --folder x |
Create new agent (or run without flags for interactive) |
hydra tasks |
List scheduled tasks |
hydra logs [agent] [-n N] |
Tail orchestrator or agent logs |
hydra config validate [path] |
Validate hydra.yaml |
Claude Code skills: Run claude in the project root, then use /setup for first-time installation or /add-agent to create agents with Telegram, mounts, and secrets.
- CLI (
hydra exec) β Direct interactive sessions with agents - Telegram β Multimodal support (images, PDFs, etc)
- Node.js 20+
- Docker + Docker Compose (for self-hosted memory)
- Anthropic API key or Claude Code OAuth
- Telegram bot token (optional)
Container secrets go in ~/.config/hydra/secrets.env. Host-side config (bot tokens, etc.) can use .env in the project root or your shell environment.
| Variable | Where | Description |
|---|---|---|
ANTHROPIC_API_KEY |
secrets.env |
API key for Claude (injected into all containers) |
CLAUDE_CODE_OAUTH_TOKEN |
secrets.env |
OAuth token from Claude Code (auto-managed) |
QDRANT_URL |
secrets.env |
Qdrant vector DB URL (default: http://localhost:6333) |
OLLAMA_URL |
secrets.env |
Ollama embedding server URL (default: http://localhost:11434) |
LITELLM_API_KEY |
secrets.env |
LiteLLM Proxy API key |
- Container isolation β Each agent runs in a separate container
- Explicit mounts β Agents can only see mounted directories
- Scoped secrets β Agents only receive secrets they're granted in
hydra.yaml; source file lives outside project root - IPC namespacing β Agents can only send messages to their own chats
- External allowlist β Mount permissions live outside agent reach
- Main privilege β Only the
mainagent can register new agents
See docs/SECURITY.md for the full security model.
Hydra is designed to be small enough to understand and modify. PRs welcome for:
- Security fixes
- Bug fixes
- Clear improvements to core functionality
For new features, consider whether they belong in core or as optional extensions.
Shoutout to Nanoclaw (https://github.com/qwibitai/nanoclaw) which was the inspiration for agentic containerization.
AGPL-3.0
