High-Performance AI Agent Runtime
HotPlex transforms terminal AI tools (Claude Code, OpenCode) into production services. Built with Go using the Cli-as-a-Service paradigm, it eliminates CLI startup latency through persistent process pooling and ensures execution safety via PGID isolation and Regex WAF. The system supports WebSocket/HTTP/SSE communication with Python and TypeScript SDKs. At the application layer, HotPlex integrates with Slack and Feishu, supporting streaming output, interactive cards, and multi-bot protocols.
Quick Start · Features · Architecture · Docs · Discussions · 简体中文
- Quick Start
- Core Concepts
- Project Structure
- Features
- Architecture
- Usage Examples
- Development Guide
- Documentation
- Contributing
# One-line installation
curl -sL https://raw.githubusercontent.com/hrygo/hotplex/main/install.sh | bash
# Or build from source
make build
# Start with Slack
export HOTPLEX_SLACK_BOT_USER_ID=B12345
export HOTPLEX_SLACK_BOT_TOKEN=xoxb-...
export HOTPLEX_SLACK_APP_TOKEN=xapp-...
./hotplexd --config configs/server.yaml --config-dir configs/base
# Or start with WebSocket gateway only
./hotplexd --config configs/server.yaml| Component | Version | Notes |
|---|---|---|
| Go | 1.25+ | Runtime & SDK |
| AI CLI | Claude Code or OpenCode | Execution target |
| Docker | 24.0+ | Optional, for container deployment |
# 1. Clone and build
git clone https://github.com/hrygo/hotplex.git
cd hotplex
make build
# 2. Copy environment template
cp .env.example .env
# 3. Configure your AI CLI
# Ensure Claude Code or OpenCode is in PATH
# 4. Run the daemon
./hotplexd --config configs/server.yamlUnderstanding these concepts is essential for effective HotPlex development.
HotPlex maintains long-lived CLI processes instead of spawning fresh instances per request. This eliminates:
- Cold start latency (typically 2-5 seconds per invocation)
- Context loss between requests
- Resource waste from repeated initialization
Request 1 → CLI Process 1 (spawned, persistent)
Request 2 → CLI Process 1 (reused, instant)
Request 3 → CLI Process 1 (reused, instant)
The Runner component handles bidirectional communication between:
- Upstream: User requests (WebSocket/HTTP/ChatApp events)
- Downstream: CLI stdin/stdout/stderr streams
// Each session has dedicated I/O channels
type Session struct {
Stdin io.Writer
Stdout io.Reader
Stderr io.Reader
Events chan *Event // Internal event bus
}Process Group ID (PGID) isolation ensures clean termination:
- CLI processes are spawned with
Setpgid: true - Termination sends signal to entire process group (
kill -PGID) - No orphaned or zombie processes
Web Application Firewall layer intercepts dangerous commands before they reach the CLI:
- Block patterns:
rm -rf /,mkfs,dd,:(){:|:&};: - Configurable via
security.danger_wafin config - Works alongside CLI's native tool restrictions (
AllowedTools)
Unified interface for multi-platform bot integration:
type ChatAdapter interface {
// Platform-specific event handling
HandleEvent(event Event) error
// Unified message format
SendMessage(msg *ChatMessage) error
}Advanced platforms implement streaming and message management:
type MessageOperations interface {
StartStream(ctx, channelID, threadTS) (messageTS, error)
AppendStream(ctx, channelID, messageTS, content) error
StopStream(ctx, channelID, messageTS) error
UpdateMessage(ctx, channelID, messageTS, msg) error
DeleteMessage(ctx, channelID, messageTS) error
}hotplex/
├── cmd/
│ └── hotplexd/ # Daemon entrypoint
├── internal/ # Core implementation (private)
│ ├── engine/ # Session pool & runner
│ ├── server/ # WebSocket & HTTP gateway
│ ├── security/ # WAF & isolation
│ ├── config/ # Configuration loading
│ ├── sys/ # OS signals
│ ├── telemetry/ # OpenTelemetry
│ └── ...
├── brain/ # Native Brain orchestration
├── cache/ # Caching layer
├── provider/ # AI provider adapters
│ ├── claudecode/ # Claude Code protocol
│ ├── opencode/ # OpenCode protocol
│ └── ...
├── chatapps/ # Platform adapters
│ ├── slack/ # Slack Bot
│ ├── feishu/ # Feishu Bot
│ └── base/ # Common interfaces
├── types/ # Public type definitions
├── event/ # Event system
├── plugins/ # Extension points
│ └── storage/ # Message persistence
├── sdks/ # Language bindings
│ ├── go/ # Go SDK (embedded)
│ ├── python/ # Python SDK
│ └── typescript/ # TypeScript SDK
├── docker/ # Container definitions
├── configs/ # Configuration examples
└── docs/ # Architecture docs
| Directory | Purpose | Public API |
|---|---|---|
types/ |
Core types & interfaces | ✅ Yes |
event/ |
Event definitions | ✅ Yes |
hotplex.go |
SDK entry point | ✅ Yes |
internal/engine/ |
Session management | ❌ Internal |
internal/server/ |
Network protocols | ❌ Internal |
provider/ |
CLI adapters |
| Feature | Description | Use Case |
|---|---|---|
| 🔄 Session Pooling | Long-lived CLI processes with instant reconnection | High-frequency AI interactions |
| 🌊 Full-Duplex Streaming | Sub-second token delivery via Go channels | Real-time UI updates |
| 🛡️ Regex WAF | Block destructive commands (rm -rf /, mkfs, etc.) |
Security hardening |
| 🔒 PGID Isolation | Clean process termination, no zombies | Production reliability |
| 💬 Multi-Platform | Slack · Feishu | Team communication |
| 📦 Go SDK | Embed directly in your Go app with zero overhead | Custom integrations |
| 🔌 WebSocket Gateway | Language-agnostic access via hotplexd daemon |
Web frontend |
| 📊 OpenTelemetry | Built-in metrics and tracing support | Observability |
| 🐳 Docker 1+n | 1 Base + n Stacks (node, python, java, rust, full) |
Multi-language |
┌─────────────────────────────────────────────────────────────────┐
│ ChatApps Layer │
│ Slack Bot · Feishu Bot · Web │
│ (Event → ChatMessage → Session ID) │
└────────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────────▼────────────────────────────────────┐
│ WebSocket Gateway │
│ hotplexd daemon / Go SDK / HTTP │
│ (Protocol translation, Rate limiting, Auth) │
└────────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────────▼────────────────────────────────────┐
│ Engine / Runner │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Session Pool (map[SessionID]*Session) │ │
│ │ - Lifecycle management │ │
│ │ - Idle timeout & GC │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Runner (I/O Multiplexer) │ │
│ │ - stdin/stdout/stderr piping │ │
│ │ - Event serialization │ │
│ └─────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Claude │ │ OpenCode│ │ Custom │
│ CLI │ │ CLI │ │ Provider│
└─────────┘ └─────────┘ └─────────┘
User → Gateway → Session Pool → Runner → CLI
CLI → Runner → Gateway → User (streaming)
| Layer | Implementation | Configuration |
|---|---|---|
| Tool Governance | AllowedTools config |
security.allowed_tools |
| Danger WAF | Regex interception | security.danger_waf |
| Process Isolation | PGID-based termination | Automatic |
| Filesystem Jail | WorkDir lockdown |
engine.work_dir |
| Container Sandbox | Docker (BaaS) | docker/ |
import (
"context"
"fmt"
"time"
"github.com/hrygo/hotplex"
"github.com/hrygo/hotplex/types"
)
func main() {
// Initialize engine
engine, err := hotplex.NewEngine(hotplex.EngineOptions{
Timeout: 5 * time.Minute,
IdleTimeout: 30 * time.Minute,
})
if err != nil {
panic(err)
}
defer engine.Close()
// Execute prompt
cfg := &types.Config{
WorkDir: "/path/to/project",
SessionID: "user-session-123",
}
engine.Execute(context.Background(), cfg, "Explain this function", func(eventType string, data any) error {
switch eventType {
case "message":
if msg, ok := data.(*types.StreamMessage); ok {
fmt.Print(msg.Content) // Streaming output
}
case "error":
if errMsg, ok := data.(string); ok {
fmt.Printf("Error: %s\n", errMsg)
}
case "usage":
if stats, ok := data.(*types.UsageStats); ok {
fmt.Printf("Tokens: %d input, %d output\n", stats.InputTokens, stats.OutputTokens)
}
}
return nil
})
}# configs/base/slack.yaml
platform: slack
mode: socket
provider:
type: claude-code
default_model: sonnet
allowed_tools:
- Read
- Edit
- Glob
- Grep
- Bash
engine:
work_dir: ~/projects/hotplex
timeout: 30m
idle_timeout: 1h
security:
owner:
primary: ${HOTPLEX_SLACK_PRIMARY_OWNER}
policy: trusted
assistant:
bot_user_id: ${HOTPLEX_SLACK_BOT_USER_ID}
dm_policy: allow
group_policy: multibot// Connect
const ws = new WebSocket('ws://localhost:8080/ws/v1/agent');
// Listen for messages
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'message':
console.log(data.content);
break;
case 'error':
console.error(data.error);
break;
case 'done':
console.log('Execution complete');
break;
}
};
// Execute prompt
ws.send(JSON.stringify({
type: 'execute',
session_id: 'optional-session-id',
prompt: 'List files in current directory'
}));curl -N -X POST http://localhost:8080/api/v1/execute \
-H "Content-Type: application/json" \
-d '{"prompt": "Hello, AI!", "session_id": "session-123"}'# Run tests
make test
# Run with race detector
make test-race
# Build binary
make build
# Run linter
make lint
# Build Docker images
make docker-build
# Start Docker stack
make docker-up- Implement the adapter interface in
chatapps/<platform>/:
type Adapter struct {
client *platform.Client
engine *engine.Engine
}
// Implement base.ChatAdapter interface
var _ base.ChatAdapter = (*Adapter)(nil)
func (a *Adapter) HandleEvent(event base.Event) error {
// Platform-specific event parsing
}
func (a *Adapter) SendMessage(msg *base.ChatMessage) error {
// Platform-specific message sending
}- Register in
chatapps/setup.go:
func init() {
registry.Register("platform-name", NewAdapter)
}- Add configuration in
configs/base/:
platform: platform-name
mode: socket # or http
# ... platform-specific config- Implement
provider/<name>/parser.go:
type Parser struct{}
func (p *Parser) ParseStream(line string) (*types.StreamMessage, error) {
// Provider-specific output parsing
}- Register in
provider/factory.go:
func init() {
providers.Register("provider-name", NewProvider)
}| Guide | Description |
|---|---|
| 🚀 Deployment | Docker, production setup |
| 💬 ChatApps | Slack & Feishu integration |
| 🛠 Go SDK | SDK reference |
| 🔒 Security | WAF, isolation |
| 📊 Observability | Metrics, tracing |
| ⚙️ Configuration | Full config reference |
We welcome contributions! Please follow these steps:
# 1. Fork and clone
git clone https://github.com/hrygo/hotplex.git
# 2. Create a feature branch
git checkout -b feat/your-feature
# 3. Make changes and test
make test
make lint
# 4. Commit with conventional format
git commit -m "feat(engine): add session priority support"
# 5. Submit PR
gh pr create --fill<type>(<scope>): <description>
Types: feat, fix, refactor, docs, test, chore
Scope: engine, server, chatapps, provider, etc.
- Follow Uber Go Style Guide
- All interfaces require compile-time verification
- Run
make test-racebefore submitting
MIT License © 2024-present HotPlex Contributors