Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

P2Enjoy/hitl

Repository files navigation

HITL — Cryptographically Signed Human-in-the-Loop Permissions

Ensures that when an AI agent requests permission for a sensitive action, the approval is cryptographically proven to come from a verified human — not from the agent itself, an automation script, or a replay attack.

The Problem

AI agents operating autonomously can self-approve their own tool actions. Even "human-in-the-loop" systems that prompt for approval can be bypassed if the approval check happens within the agent's own process. There's no cryptographic proof that a human actually responded.

The Solution

┌─────────────────────────────────────────────────────────────────────┐
│ Trust Chain │
│ │
│ Keycloak OAuth ──► verified human accounts only │
│ │ │
│ ▼ │
│ Browser Extension ──► holds Ed25519 keypair in memory │
│ (chrome.storage.session, extractable:false) │
│ │ │
│ ▼ │
│ User clicks Approve ──► extension signs the challenge │
│ │ │
│ ▼ │
│ Tool verifies signature against Keycloak-registered public key │
│ │ │
│ ▼ │
│ APPROVED — cryptographic proof that a registered human responded │
└─────────────────────────────────────────────────────────────────────┘

The agent cannot forge a valid signature. Only the browser extension, which holds the private key (never extractable, cleared on browser close), can sign challenges.

Architecture

Tool/CLI/MCP/Skill
 │
 │ POST /sign {challenge}
 ▼
Native Messaging Host (localhost:7331)
 │
 │ NativeMessaging stdio protocol
 ▼
Extension Background Service Worker
 │
 │ Show popup to user
 ▼
User: Approve / Deny
 │
 │ Ed25519 sign(challenge) ← private key stays in SubtleCrypto
 ▼
Extension → NativeHost → Tool
 │
 │ GET /admin/realms/hitl/users/{id}
 ▼
 Keycloak Admin API
 │
 │ returns ed25519_public_key attribute
 ▼
 Tool verifies signature
 │
 ▼
 APPROVED or DENIED

Security Properties

Property How it's enforced
Only verified humans can approve Keycloak realm — no bot/service accounts allowed
Agent cannot self-approve Private key never leaves the browser extension (extractable:false)
Key cleared when browser closes chrome.storage.session — in-memory only
Replay attacks prevented Per-challenge nonce tracked in Redis + timestamp TTL
Key bound to authenticated user Public key registered in Keycloak at OAuth login
Approval is action-specific Challenge includes the action description, nonce, timestamp

Known Limitations

  • If the browser host OS is fully compromised (OS-level keylogger), the private key can be captured at signing time
  • If Keycloak is compromised, public keys can be swapped
  • The 5-minute TTL (configurable) means the human must be present; long-running agents need to re-request

Repository Structure

hitl/
├── CLAUDE.md # Agent/developer instructions
├── README.md # This file
├── .env.example # Environment variable template
├── docker-compose.yml # Keycloak + Redis
├── oauth/ # Keycloak realm config + init scripts
├── extension/ # Browser extension (TypeScript, MV3, Chrome+Firefox)
├── cli/ # Demo CLI: hitl request --action "..."
├── tool/ # @require_human_approval decorator demo
├── skill/ # Claude Code /hitl-approve slash command
└── mcp/ # FastMCP server with request_approval tool

Quick Start

Prerequisites

  • Docker + Docker Compose
  • Node.js 20+
  • Python 3.11+
  • uv Python package manager
  • Chrome 113+ or Firefox 115+

1. Start Infrastructure

cp .env.example .env
# Edit .env if needed (defaults work for local dev)
docker compose up -d

Keycloak will be available at http://localhost:8080. The realm hitl is imported automatically on first start.

2. Build & Load the Extension

cd extension
npm install
npm run build

Chrome: Go to chrome://extensions → Enable "Developer mode" → "Load unpacked" → select extension/dist-chrome/

Firefox: Go to about:debugging → "This Firefox" → "Load Temporary Add-on" → select extension/dist-firefox/manifest.json

3. Register Native Messaging Host

cd extension
node signing-host/install.js

This installs the native messaging host manifest so the extension can communicate with the local HTTP relay.

4. Install Python Packages

cd /path/to/hitl
uv pip install -e cli/ tool/ skill/ mcp/

5. Log In via the Extension

Click the extension icon in the browser toolbar → "Login with Keycloak" → register with a Keycloak account. Your Ed25519 keypair is generated and your public key is stored in Keycloak.

6. Test the Golden Path

hitl request --action "delete /tmp/testfile"

A popup appears in the browser. Click Approve. The terminal prints:

APPROVED — signature verified
User: alice@example.com
Challenge: a3f1b2... (nonce truncated)

Package Documentation

Development

# Run all tests
cd extension && npm test
cd cli && uv run pytest
cd tool && uv run pytest
cd mcp && uv run pytest
# Lint
cd extension && npm run lint
cd cli && uv run ruff check . && uv run mypy hitl_cli/

License

Apache 2.0 — see LICENSE.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

Contributors

AltStyle によって変換されたページ (->オリジナル) /