Architecture Overview
This document describes the internal architecture of OpenPact, providing a high-level overview of the components and their interactions.
Component Diagram
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Discord │ │ Telegram │ │ Slack │
│ Client │ │ Client │ │ Client │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────────────┐
│ Chat Provider Interface │
│ (internal/chat) │
│ - Unified message/command handling │
│ - Provider: Discord, Telegram, Slack │
└────────────────────────────────────┬─────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Orchestrator │
│ (internal/orchestrator) │
│ - Request routing │
│ - Per-channel session management │
│ - Source context injection │
└───────────────┬────────────────────┴────────────────────┬────────────┘
│ │
▼ ▼
┌───────────────────────────┐ ┌────────────────────────────────┐
│ AI Engine │ │ MCP Server │
│ (internal/engine) │ │ (internal/mcp) │
│ - OpenCode adapter │◄─────────►│ - Tool registration │
│ │ Tools │ - Request handling │
│ - Provider abstraction │ │ - Response formatting │
└───────────────────────────┘ └─────────────────┬──────────────┘
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ Workspace Tools │ │ Integration Tools │ │ Script Engine │
│ │ │ │ │ (internal/starlark)│
│ - workspace_read │ │ - calendar_read │ │ - Sandboxed exec │
│ - workspace_write │ │ - vault_* │ │ - Secret injection │
│ - workspace_list │ │ - github_* │ │ - HTTP functions │
│ - memory_read/write│ │ - web_fetch │ │ - script_run │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
Core Packages
cmd/openpact
The main application entry point. Handles:
- CLI command parsing
- Configuration loading
- Service initialization
- Graceful shutdown
internal/orchestrator
The central coordination layer that:
- Receives messages from all chat providers (Discord, Telegram, Slack)
- Routes requests to the AI engine via session-based messaging
- Manages per-channel sessions (
<WorkspacePath>/secure/data/channel_sessions.json) -- each provider:channel pair gets its own session - Injects source context (
[via telegram, channel:X, user:Y]) into messages - Manages conversation context (SOUL/USER/MEMORY injection)
- Implements the
admin.SessionAPIinterface for the Admin UI - Coordinates MCP tool calls
internal/engine
Provides adapters for different AI backends. Communicates with the OpenCode server via REST API.
// Engine interface
type Engine interface {
Start(ctx context.Context) error // Connect to opencode serve
Stop() error // No-op (process managed externally)
Send(ctx context.Context, sessionID string, messages []Message) (<-chan Response, error)
SetSystemPrompt(prompt string)
// Session management (proxied to OpenCode server)
CreateSession() (*Session, error)
ListSessions() ([]Session, error)
GetSession(id string) (*Session, error)
DeleteSession(id string) error
AbortSession(id string) error
GetMessages(sessionID string, limit int) ([]MessageInfo, error)
}
The engine connects to an externally-managed opencode serve instance over HTTP. In Docker, the entrypoint launches OpenCode as openpact-ai with a restart loop; the engine is a pure HTTP client. OpenCode manages all session storage (SQLite) internally.
Implementations:
- OpenCode: Supports 75+ LLM providers
internal/mcp
Model Context Protocol server implementation:
// Tool interface
type Tool interface {
Name() string
Description() string
Schema() Schema
Execute(ctx context.Context, params map[string]any) (any, error)
}
Built-in tool categories:
- Workspace: File read/write operations
- Memory: Persistent memory management
- Chat: Unified messaging across all providers (
chat_send) - Calendar: Google Calendar integration
- Vault: Obsidian vault access
- GitHub: Issue and PR management
- Web: HTTP fetching
- Script: Starlark script execution
internal/starlark
Sandboxed script execution engine:
- Loader: Script discovery and parsing
- Sandbox: Secure execution environment
- Secrets: Secret injection and redaction
Security features:
- No filesystem access
- HTTP-only networking
- Execution timeouts
- Automatic secret redaction
internal/config
Configuration management:
type Config struct {
WorkspacePath string
MemoryFile string
SoulFile string
UserFile string
Engine EngineConfig
MCP MCPConfig
Server ServerConfig
Logging LogConfig
}
Supports:
- YAML configuration files
- Environment variable overrides
- Secure secret handling
internal/chat
Defines the generic chat.Provider interface that all messaging platforms implement. Includes MessageHandler and CommandHandler callback types.
internal/providers/discord
Discord bot integration (implements chat.Provider):
- WebSocket connection management
- Message event handling
- Slash commands (
/new,/sessions,/switch) for session management - Channel and user allowlisting
internal/providers/telegram
Telegram bot integration (implements chat.Provider):
- Long-polling update handling (no webhook needed)
- Native
/commandsupport - 4096-character message splitting
- User allowlisting by ID or username
internal/providers/slack
Slack bot integration (implements chat.Provider):
- Socket Mode connection (no public URL needed)
- Events API message handling
- Slash commands (
/openpact-new,/openpact-sessions,/openpact-switch) - User and channel allowlisting
internal/admin
Admin UI backend:
- JWT authentication
- Auth session management (login/logout/refresh)
- AI session management (create/list/switch/delete/chat via WebSocket)
- Script approval workflow
- Secrets management API
internal/health
Health check endpoints:
/health- Liveness check/ready- Readiness check/metrics- Prometheus metrics
internal/logging
Structured logging:
- JSON and text formats
- Configurable log levels
- Request ID tracking
internal/ratelimit
Request rate limiting:
- Token bucket algorithm
- Per-user limits
- Configurable thresholds
Data Flow
Message Processing
- Receive: Chat provider (Discord, Telegram, or Slack) receives user message or command
- Session: Orchestrator gets (or creates) the per-channel session for this provider:channel pair
- Context: Load SOUL, USER, and MEMORY files as system prompt; prepend source context (
[via telegram, channel:X, user:Y]) - Send:
POST /session/:id/messageto the OpenCode server with the enriched message - Process: OpenCode routes to the configured AI provider, which generates a response
- Stream: Response is streamed back through the response channel
- Respond: Send response back through the originating chat provider (or Admin UI WebSocket)
Tool Execution
- Request: AI requests tool via MCP protocol
- Validate: Server validates tool name and parameters
- Authorize: Check tool is in allowed list
- Execute: Run tool implementation
- Redact: Scan output for secrets
- Return: Send result to AI
Script Execution
- Load: Script loaded from scripts directory
- Parse: Starlark parser validates syntax
- Inject: Secrets injected into environment
- Execute: Run in sandboxed interpreter
- Redact: Remove secrets from output
- Return: Return sanitized result
Security Architecture
Principle of Least Privilege
┌────────────────────────────────────────────────────┐
│ AI Model │
│ - No direct network access │
│ - No filesystem access │
│ - Only sees tool results │
└─────────────────────────┬──────────────────────────┘
│ MCP Protocol
▼
┌────────────────────────────────────────────────────┐
│ OpenPact MCP │
│ - Tool allowlisting │
│ - Secret redaction │
│ - Rate limiting │
└─────────────────────────┬──────────────────────────┘
│ Internal Calls
▼
┌────────────────────────────────────────────────────┐
│ Tool Implementations │
│ - Sandboxed execution │
│ - Scoped permissions │
│ - Audit logging │
└────────────────────────────────────────────────────┘
Secret Handling
- Secrets stored in
secure/data/(AI has zero access) - Never passed to AI directly
- Injected into scripts at runtime
- Automatically redacted from all outputs
Docker Security Model
Uses two-user privilege separation:
- Root user: Initial container setup only
- App user: Runtime execution with minimal privileges
Extension Points
Adding New Tools
- Implement the
Toolinterface - Register in
internal/mcp/tools.go - Add to configuration allowlist
Adding New Engines
- Implement the
Engineinterface - Add adapter in
internal/engine/ - Register in engine factory
Adding Starlark Built-ins
- Add function in
internal/starlark/sandbox.go - Register in built-in module
- Document in Starlark reference