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

┌─────────────────────────────────────────────────┐ │ Host Machine │ │ │ │ ┌──────────────────────────────────────────┐ │ │ │ Authority (janee serve --runner-key) │ │ │ │ - Holds credentials & secrets │ │ │ │ - Enforces exec policy (allowlists) │ │ │ │ - Proxies API requests with credentials │ │ │ │ - Issues exec grants to Runners │ │ │ └────────────────────┬─────────────────────┘ │ │ │ :3100 │ ├───────────────────────┼─────────────────────────┤ │ Container │ │ │ │ │ │ ┌────────────────────▼─────────────────────┐ │ │ │ Runner (janee serve --authority) │ │ │ │ - Serves MCP to the agent │ │ │ │ - Forwards tool calls to Authority │ │ │ │ - Runs janee_exec locally (in-container) │ │ │ └────────────────────▲─────────────────────┘ │ │ │ :3200 │ │ ┌────────────────────┴─────────────────────┐ │ │ │ Agent (Claude, Codex, custom) │ │ │ │ JANEE_URL=http://localhost:3200 │ │ │ └──────────────────────────────────────────┘ │ └─────────────────────────────────────────────────┘

How It Works

  1. Agent calls a tool via MCP (e.g., list_services, execute)
  2. Runner receives the MCP request on its local port
  3. For non-exec tools: Runner forwards the call to the Authority over HTTP, which handles it with full credential access
  4. 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
  5. 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 --authority to 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 allowCommands can be executed. Everything else is denied by default.