Runner/Authority Architecture
Secure credential handling for containerized AI agent deployments.
When AI agents run inside Docker containers, they can't use janee_exec on the host — the host process has no access to the container's filesystem. The Runner/Authority architecture solves this by splitting Janee into two cooperating processes.
Overview
How It Works
- Agent calls a tool via MCP (e.g.,
list_services,execute) - Runner receives the MCP request on its local port
- For non-exec tools: Runner forwards the call to the Authority over HTTP, which handles it with full credential access
- For exec tools (
janee_exec): Runner asks the Authority to authorize the execution, receives a grant with injected credentials and scrub values, then runs the command locally in the container where the agent's files live - After execution, Runner reports the result back to the Authority for audit logging
💡 Key insight: Credentials never leave the host, but commands run where the agent's code actually lives. The agent sees a normal Janee MCP server — it has no idea it's talking to a Runner.
Quick Start
1. Configure Janee on the host
Create your janee.yaml with services and capabilities as usual:
# ~/.janee/config.yaml
services:
github:
baseUrl: https://api.github.com
auth:
type: bearer
key: ghp_your_token_here
capabilities:
- name: gh-cli
service: github
mode: exec
allowCommands: [gh]
env:
GH_TOKEN: "{{credential}}"
timeout: 30000
2. Start the Authority on the host
# Generate a shared runner key
export JANEE_RUNNER_KEY=$(openssl rand -hex 32)
# Start Authority — serves both MCP and exec authorization
janee serve -t http -p 3100 --host 0.0.0.0 --runner-key "$JANEE_RUNNER_KEY"
The Authority exposes:
- Standard MCP endpoints (for proxied tool calls)
/v1/exec/authorize— grants exec permissions with credential injection/v1/exec/complete— receives execution reports for audit logging/v1/health— unauthenticated health check
3. Start the Runner in each container
janee serve -t http -p 3200 --host 127.0.0.1 \
--authority http://host.docker.internal:3100 \
--runner-key "$JANEE_RUNNER_KEY"
The Runner:
- Accepts MCP connections from the agent on port 3200
- Uses
--authorityto know where to forward calls - Authenticates to the Authority using
--runner-key
4. Point the agent at the Runner
export JANEE_URL=http://localhost:3200
The agent sees a normal Janee MCP server. It has no idea it's talking to a Runner.
Agent Identity Forwarding
When an agent connects via MCP, it sends a clientInfo.name field identifying itself (e.g., "cursor", "claude-desktop"). The Runner forwards this identity to the Authority, enabling:
- Per-agent access control — different agents can have different capabilities
- Audit attribution — every API call in the audit log shows which agent made it
- GitHub App tokens — the Authority can mint installation tokens scoped to the requesting agent
💡 Agent identity flows automatically through the Runner proxy. No configuration needed — the Runner reads the MCP clientInfo and includes it in every request to the Authority.
Working Directory (cwd)
The janee_exec tool accepts an optional cwd parameter that sets the working directory for command execution:
// Agent calls janee_exec with cwd
{
"tool": "janee_exec",
"arguments": {
"capability": "gh-cli",
"command": "gh repo view",
"cwd": "/home/agent/project"
}
}
This is especially useful in containerized environments where the agent works in a specific project directory. The Runner executes the command from that directory, so relative paths and git operations work as expected.
Automatic GIT_ASKPASS
When an exec capability uses git commands with a GitHub token (GH_TOKEN or GITHUB_TOKEN), Janee automatically creates a temporary GIT_ASKPASS script that provides HTTPS authentication. This means agents can run git push, git pull, and other remote operations without any extra configuration.
# This "just works" — no GIT_ASKPASS setup needed
capabilities:
- name: git-ops
service: github
mode: exec
allowCommands: [git]
env:
GH_TOKEN: "{{credential}}"
💡 The askpass script is created before execution and cleaned up automatically afterward. The agent never sees the token — it's injected into the git credential flow at the OS level.
Docker Compose Example
version: "3.8"
services:
authority:
image: node:20-slim
command: npx @true-and-useful/janee serve -t http -p 3100 --host 0.0.0.0 --runner-key ${JANEE_RUNNER_KEY}
volumes:
- ./janee-config:/root/.janee
ports:
- "3100:3100"
agent:
build: ./agent
environment:
- JANEE_URL=http://runner:3200
depends_on:
- runner
runner:
image: node:20-slim
command: npx @true-and-useful/janee serve -t http -p 3200 --host 0.0.0.0 --authority http://authority:3100 --runner-key ${JANEE_RUNNER_KEY}
depends_on:
- authority
Standalone Authority
If you don't need containerized exec but want centralized credential management for a team, you can run just the Authority:
# Authority with no runner-key — operates as a standard Janee server
janee serve -t http -p 3100 --host 0.0.0.0
Agents connect directly to the Authority over HTTP. All API calls are proxied with credentials injected server-side, and all calls are logged for audit.
Security Considerations
- Runner key rotation: Rotate the shared key periodically. Both Authority and Runner must use the same key.
- Network isolation: The Authority should only be reachable from trusted containers. Use Docker networks to restrict access.
- Credential scrubbing: The Authority tells the Runner which values to scrub from command output, preventing credential leaks in stdout/stderr.
- Exec allowlists: Only commands listed in
allowCommandscan be executed. Everything else is denied by default.