Core Concepts
Monstrum Concepts and Terminology
This document defines all abstract concepts and technical terms in the Monstrum platform. It is recommended to read through this document before exploring the architecture docs or source code.
Table of Contents
- Design Paradigm
- Core Concept Distinctions
- Resource Model
- Permission Model
- Tool System
- Execution Architecture
- Sessions and Messages
- Memory System
- Prompt Management
- Event System
- Workflows
- Tasks and Scheduling
- Plugin System
- SDK
- Gateways and Adapters
- Users and Multi-Tenancy
- Audit and Cost
- LLM Provider
- Design Principles Quick Reference
Design Paradigm
From Tool to Autonomous Actor
We have always treated AI as a tool — pursuing smarter and more powerful capabilities. But in production environments, an AI Agent is no longer a passive tool; it is an independent autonomous actor: it makes decisions, invokes tools, accesses data, interacts with external systems, and even delegates tasks to other Agents.
People in society require governance — identity verification, behavioral boundaries, legal constraints, and behavioral audits. As autonomous actors in the digital world, Bots equally require a complete governance framework:
| Person | Bot | Monstrum Implementation |
|---|---|---|
| ID card | Identity | Bot ID + Name + Description + Workspace |
| Legal constraints | Permission policies | RolePermissions (allowed operations + parameter scopes) |
| Work authorization | Resource binding | BotResource (which systems to bind, which identity to use) |
| Keys / passwords | Credentials | Credential (encrypted storage, Bot never sees plaintext) |
| Salary budget | Token budget | monthly_token_budget + cost tracking |
| Behavioral records | Audit logs | ActionLog (full chain, tamper-proof) |
| Experience / memory | Bot memory | MemoryEntry (partitioned storage, automatic extraction) |
This is not an analogy — this is Monstrum’s core design paradigm. A Bot is a governed autonomous actor, not an LLM wrapper.
Platform-Enforced, Not AI Self-Discipline
AI Agents are capable but untrustworthy — they hallucinate, can be injected, and may exceed their authority. You cannot rely on AI self-discipline for security.
Monstrum’s core thesis: Security is guaranteed by platform architecture, not by relying on AI’s self-restraint.
- LLM says “I won’t access that repository”? → Not enough. The platform makes it so the Bot cannot even see the tools for that repository
- LLM says “I won’t leak the API Key”? → Not enough. The platform never gives plaintext credentials to the LLM
- LLM says “I’ll only operate within authorized scope”? → Not enough. The platform validates parameters against boundaries after every tool call
Three governance principles are derived from this thesis:
Three Governance Principles
1. Least Privilege — Invisible = Non-existent
A Bot can only see tools that have been explicitly authorized. Unbound Resources are completely invisible to the Bot — not “visible but forbidden to call,” but the LLM simply does not know they exist. This is more thorough than traditional RBAC’s “access denied”: the attack surface shrinks from “attempting to exceed authority” to zero.
2. Credential Isolation — LLM Never Touches Secrets
Credentials are stored encrypted. At execution time, the platform decrypts and injects them into the Executor layer; the LLM never has access to any plaintext throughout its entire lifecycle. Even if the LLM is attacked via prompt injection, it cannot leak credentials.
3. Fail-Closed — Errors Mean Stop
When any component fails, the result is “the Bot can do nothing,” rather than “unrestricted access”:
- ToolResolver fails → returns empty tool list (Bot has no tools available)
- Guardian fails → request denied
- EventDispatcher pre-delivery re-check fails → delivery blocked
Permission Design: Declarative + Two-Layer Filtering + Delegation Without Escalation
Declarative over imperative. Plugin developers don’t need to write permission-checking code — they just declare “which parameters to check and how to match”:
ScopeDimension:
key: repos → "check the repos parameter"
param_paths: [repo] → "extract value from the repo field in tool parameters"
match_mode: pattern → "match using fnmatch"
Platform automatically: extract parameters → match scope_constraints → allow/deny
Two independent filtering layers. Either layer alone is sufficient to prevent privilege escalation. Even if one layer is bypassed, the other still takes effect:
Pre-LLM (ToolResolver) Post-LLM (Guardian)
┌───────────────────────────┐ ┌───────────────────────────┐
│ Filter tool visibility │ │ Validate call parameters │
│ No binding = LLM can't see │ → │ Parameter out of scope = │
│ Error → return empty list │ │ deny execution │
└───────────────────────────┘ │ Error → deny request │
└───────────────────────────┘
Delegation without escalation. When Bot A delegates a task to Bot B, Bot B’s effective permissions are the intersection of Bot A’s delegation constraints and Bot B’s own permissions — they can never exceed either party. This prevents Confused Deputy attacks.
Philosophy of Two Tool Categories
A Bot’s tools are divided into two categories with fundamentally different governance strategies:
| External Resource Tools | Bot Self-Owned Capability Tools | |
|---|---|---|
| Essence | Bot accesses external systems on behalf of users | Bot operates on its own data |
| Analogy | An employee accessing GitHub with a company account | An employee writing memos in their own notebook |
| Governance needs | Full permission chain (Guardian validation) | Ownership naturally guarantees access; does not go through Guardian |
| Examples | SSH, MCP, GitHub | Memory, Scheduling, Workflows, Progress reporting |
The criterion for this distinction is simple: Who owns the data? Operating on the Bot’s own data → self-owned capability; operating on external systems or other Bots → external resource.
Core Concept Distinctions
There are several groups of easily confused concepts in Monstrum that need to be clearly distinguished.
Bot vs Agent vs LLM
| Concept | What is it | Entity in Monstrum |
|---|---|---|
| LLM | Large Language Model, a pure reasoning engine. Receives prompts and tool definitions, returns text or tool calls. Does not execute any operations itself. | LLMProvider (API configuration), ModelInfo (model metadata). The LLM is the Bot’s “brain,” but is not equivalent to the Bot. |
| Bot | A governed AI entity on the Monstrum platform. Has identity (name, description), permissions (BotResource bindings), configuration (BotRuntimeConfig), memory, and budget. The object of platform-enforced governance. | Bot model. One Bot = one LLM configuration + a set of resource bindings + permission policies + runtime state. A Bot is not directly equivalent to an LLM — it is the “shell” for the LLM, giving the LLM identity, tools, and constraints. |
| Agent | An AI program with autonomous decision-making capabilities. Has two meanings in Monstrum (see below). | Depends on context. |
Two Meanings of “Agent”
In the Monstrum codebase, the word “Agent” appears in two different contexts:
| Context | Meaning | Code Location | Description |
|---|---|---|---|
| AgentRuntime | The Bot’s LLM execution engine — responsible for the LLM call loop (reasoning → tool call → result → continue) | services/runner/agent/runtime.py | This is “Agent” as a design pattern: an LLM program that can autonomously loop through tool calls. Every Bot executes tasks through AgentRuntime. |
Monstrum Agent (type_id=monstrum-agent) | An independent entity for external AI Agents connecting to the platform via WebSocket | services/core/api/agents.py, agent_ws/ | Here “Agent” refers to an external AI program that connects to the platform via WebSocket and registers tools. Agent is an independent platform entity (AgentTable), authenticated via agent_key, not managed as a Resource. ResourceType monstrum-agent is used for the Agent’s own capability declarations. |
Summary:
- LLM = reasoning engine (calls API, returns text/tool calls)
- Bot = platform-governed AI entity (LLM + permissions + tools + memory + budget)
- AgentRuntime = the Bot’s internal execution loop (the code that “lets the LLM autonomously loop through tool calls”)
- Monstrum Agent = an external AI program that connects to the platform via WebSocket as an independent entity (
AgentTable, authenticated viaagent_key, with its own tool registration and management API)
Session vs Task
Two Bot execution modes:
| Session | Task | |
|---|---|---|
| Trigger | User sends an IM message | API call / Scheduled trigger / Bot-to-Bot delegation / Event trigger |
| Interaction | Multi-turn conversation with context | Single execution, independent context |
| Lifecycle | Recycled after 30 minutes of inactivity | Ends upon completion |
| Memory | MemoryContext (three-layer cache, scope-partitioned) | AgentRuntime loads global + task memory |
| Persistence | Optional (/chathis toggle) | Checkpoint-recoverable |
Executor vs Plugin
| Executor | Plugin | |
|---|---|---|
| Essence | Code implementation of tool execution | A complete integration package (declaration + implementation) |
| Contains | A single ExecutorBase subclass | monstrum.yaml (declaration) + executor.py (Executor) |
| Registration | ExecutorRegistry | PluginManager (auto-scan + register Executor) |
In other words: Plugin = ResourceType declaration + Executor implementation. Built-in types also have Executors, but they are not loaded through the Plugin mechanism.
Resource Model
Monstrum’s resource management is a three-layer abstraction: Declare capabilities → Instantiate connections → Authorize bindings.
ResourceType
Capability declaration. Defines what an integration “can do.”
| Field | Description |
|---|---|
id | Unique identifier (e.g., ssh, mcp, github) |
name | Display name |
tools: ToolDef[] | Tool definitions callable by the LLM |
scope_dimensions: ScopeDimension[] | Parameter-level permission check rules |
auth_methods: AuthMethodDef[] | Supported credential acquisition methods |
credential_schema: FieldDef[] | Credential field definitions (frontend auto-renders forms) |
config_schema: FieldDef[] | Connection configuration field definitions |
tool_discovery | Tool discovery mode: static / dynamic / configured |
builtin | Whether built-in (cannot be deleted) |
Built-in types (6):
| type_id | Display Name | Tool Discovery | Description |
|---|---|---|---|
ssh | SSH | static | Remote command execution, asyncssh |
mcp | MCP | dynamic | Model Context Protocol tool server (Streamable HTTP) |
bot | Bot | static | Bot-to-Bot task delegation |
web | Web | configured | Multi-engine search + HTTP fetching |
monstrum-agent | Monstrum Agent | dynamic | External Agent WebSocket connection, Agent’s own capability declarations |
web3 | Web3 (EVM) | static | EVM chain operations (balance, transfer, contract calls, events, gas estimation) |
MCP vs Monstrum Agent
Two dynamic tool discovery mechanisms with different directions and architectures:
| MCP | Monstrum Agent | |
|---|---|---|
| Direction | Platform connects to tool server | External Agent connects to platform |
| Transport | Streamable HTTP | WebSocket (/ws/agent) |
| Authentication | OAuth 2.1 / None | agent_key (Agent independent entity authentication) |
| Tool registration | Platform actively discovers | Agent actively registers (grouped by group, source=agent/mcp/plugin) |
| type_id | mcp | monstrum-agent |
| Display name | MCP | Monstrum Agent |
| Entity model | Resource (ResourceType instance) | Independent entity (AgentTable), not managed as Resource |
| Management API | Resource CRUD | Dedicated API at services/core/api/agents.py |
Monstrum Agent key components:
AgentConnectionManager— Connection management (indexed byagent_id), authentication, tool registrationToolCatalog— Dynamic tool storage (source distinguishes agent/mcp/plugin)ToolExecutor._try_execute_via_agent()— Forward tool calls via WebSocket (replaces the deleted AgentExecutor)
Resource (Resource Instance)
A concrete external system connection. A single ResourceType can have multiple Resource instances.
| Field | Description |
|---|---|
id | UUID |
workspace_id | Owning workspace |
type_id | Corresponding ResourceType |
name | User-defined name (e.g., “Production Server”) |
config | Connection configuration (JSON, schema defined by ResourceType) |
status | CONNECTED / DISCONNECTED / AUTH_EXPIRED / AUTH_REQUIRED |
ResourceCredential
Encrypted authentication information. The LLM can never access plaintext.
| Field | Description |
|---|---|
id | UUID |
resource_id | Owning Resource |
auth_method | Authentication method (oauth2_auth_code / api_key / token / ssh_key / basic) |
encrypted_fields | Encrypted credential values (visible only at DB layer) |
display_fields | Non-sensitive fields (displayable in frontend) |
auto_refresh | Whether auto-refresh is supported (OAuth Token) |
discovered_tools | Dynamically discovered tool definitions (MCP / Monstrum Agent only) |
discovery_status | Tool discovery status: success / error / pending |
BotResource (Resource Binding)
The authorization relationship between a Bot and a Resource. This is the core carrier of permissions.
| Field | Description |
|---|---|
id | Binding ID |
bot_id | Authorized Bot |
resource_id | Bound Resource |
credential_id | Which credential to use |
permissions | RolePermissions — permissions effective at runtime (see below) |
is_active | Whether enabled |
Tool Discovery Modes (ToolDiscovery)
The tool_discovery field of ResourceType determines how tools are discovered:
| Mode | Description | Representative Types |
|---|---|---|
| static | Tool definitions are declared in the ResourceType, do not change | SSH, Bot, Web3 |
| dynamic | Tools are discovered at runtime (auto-detected after connection), persisted to ResourceCredential | MCP, Monstrum Agent |
| configured | Tool definitions are fixed, but behavior is determined by config_schema configuration | Web (search engine selectable) |
Permission Model
Two independent security filtering layers, either of which alone is sufficient to prevent privilege escalation.
RolePermissions (Runtime Permissions)
Flat permissions directly attached to BotResource, serving as the sole data source for runtime validation.
| Field | Description |
|---|---|
allowed_operations | Operation glob list (e.g., ["issue.*", "pr.read"]) |
allowed_tools | Tool glob list (for dynamic types, e.g., ["get_menu", "list_*"]) |
scope_constraints | Parameter-level constraints (e.g., {"repos": ["org/repo-*"]}) |
delegate | Delegation constraints DelegateConstraints (see below) |
ScopeDimension (Permission Dimension Declaration)
Declarative rules — plugin developers only need to declare “what to check,” and the platform automatically enforces it.
| Field | Description |
|---|---|
key | The key in the scope dictionary (e.g., repos, hosts, commands) |
param_paths | JSON paths to extract values from tool parameters (tried in order) |
match_mode | Matching method: pattern (fnmatch) / path (glob) / exact |
error_template | Error message template when denied ({value} placeholder) |
DelegateConstraints (Delegation Constraints)
Constrains the effective permissions of the called Bot during Bot-to-Bot calls. Prevents Confused Deputy attacks.
| Field | Description |
|---|---|
allowed_tools | Tools visible to the called Bot (glob, Pre-LLM filtering) |
scope_constraints | Intersected with the called Bot’s own scope (Post-LLM validation) |
Role (Role Template)
RBAC role with preset permission combinations. Can be assigned to Bots, supports inheritance.
| Field | Description |
|---|---|
id | UUID |
name | Role name (unique within workspace) |
parent_role_id | Inherits from parent role |
permissions | RolePermissionConfig — hierarchical permission configuration |
Two-Layer Filtering Mechanism
Pre-LLM: ToolResolver
Input: BotResource binding list
Output: Tool list visible to the LLM (ToolResolution)
Principle: No binding = no tools; LLM is completely unaware of unauthorized tools
On error: Returns empty list (Fail-closed)
Post-LLM: Guardian
Input: LLM-returned tool call + parameters
Output: Allow/Deny (BotResourcePermissionResponse)
Principle: Validates parameter legality according to ScopeDimension declarations
On error: Deny request (Fail-closed)
Tool System
ToolDef (Tool Definition)
An LLM-callable tool declaration, defined in ResourceType.
| Field | Description |
|---|---|
name | Tool name (e.g., github_list_issues) |
description | Description visible to the LLM |
operation | Associated operation (e.g., issue.read) |
input_schema | JSON Schema parameter definition |
ToolCatalog (Tool Directory)
A data-driven global tool index. Loads all ResourceType tool definitions from the DB at startup.
| Data | Description |
|---|---|
_tool_map | tool_name → (type_id, operation) |
_type_tools | type_id → [tool_names] |
_scope_dims | type_id → [ScopeDimension] |
_dynamic_tools | resource_id → [ToolDef] (dynamically discovered tools) |
ToolResolution (Tool Resolution Result)
The output of ToolResolver. Contains the tool list visible to the LLM and internal routing information.
| Field | Description |
|---|---|
tools | LLM tool definition list (may have qualified name prefix) |
_resource_map | qualified_name → resource_id |
_binding_map | qualified_name → binding_id |
_name_map | qualified_name → original_tool_name |
resource_groups | Tool summary grouped by Resource (injected into system prompt) |
Qualified name rules:
- Single binding: tool name unchanged (
github_list_issues) - Multiple bindings of same type:
{resource_name}__{tool_name}(Work__github_list_issues)
Two Tool Categories
| External Resource Tools | Bot Self-Owned Capability Tools | |
|---|---|---|
| Essence | Bot accesses external systems | Bot operates on its own data |
| Examples | SSH / MCP / GitHub | Memory, Scheduling, Workflows, Progress |
| Pre-LLM | BotResource binding filtering | BotRuntimeConfig toggle / always injected |
| Post-LLM | Guardian scope validation | Does not go through Guardian (ownership naturally guarantees access) |
| Requires Resource | Yes | No |
Bot Self Sentinel (Bot Self-Owned Tool Sentinel)
Bot self-owned tools use a special prefix for routing, bypassing Guardian:
Format: __bot__:{capability}:{operation}
Example: __bot__:memory:write, __bot__:schedule:create
ToolResolver → returns sentinel name
AgentRuntime → detects __bot__ prefix → calls bot_self_tools.py handler
| Tool | Control Switch |
|---|---|
memory_write / memory_delete | Always injected |
memory_load_context / memory_unload_context | Always injected |
task_progress | Always injected |
schedule_create / schedule_list / schedule_delete | BotRuntimeConfig.self_schedule |
send_to_channel | BotRuntimeConfig.self_channel |
Execution Architecture
AgentRuntime (Agent Execution Engine)
The core LLM call loop. Receives tasks, builds system prompts, and loops through LLM and tool calls until completion.
Responsibilities:
- Build system prompt (base template + memory + Agent Mode + Skills)
- Call ToolResolver to filter the tool list (Pre-LLM)
- Call LLM → get tool calls or text response
- Call ToolExecutor to execute tools (including Guardian Post-LLM validation)
- Loop until the LLM returns a final text response, reaches max_iterations, or times out
- Trigger memory extraction after task completion
Agent Mode (Reasoning Mode):
| Mode | Behavior |
|---|---|
reactive | Execute directly without outputting a plan (default) |
planning | Output a numbered plan first, then execute step by step |
adaptive | Plan → Execute → Reflect → Adjust plan |
ToolExecutor
The middle layer that orchestrates Guardian validation + Executor execution + audit logging.
Execution flow:
1. Guardian.check_bot_resource_permission() → permission check + credential decryption
2. ExecutorRegistry.execute_tool() → route to the specific Executor
3. DelegateConstraints secondary validation (if delegation is involved)
4. AuditorClient.log() → record operation log
5. EventBus.publish() → publish RunnerEvent
Note: Sentinel routing (
__bot__:*and workflow tools) is handled upstream in AgentRuntime / SessionManager, which directly callsbot_self_toolsorworkflow_toolswithout going through ToolExecutor. ToolExecutor only handles external resource tools.
ExecutorBase (Executor Base Class)
The abstract base class for all Executors.
| Property/Method | Description |
|---|---|
resource_type | The resource type handled (e.g., ssh) |
supported_operations | List of supported operations |
execute(request) | Execute a tool call → ExecuteResult |
_get_token(request, field) | Unified credential value retrieval |
get_sdk_functions() | Returns Platform SDK functions |
ExecuteRequest (Execution Request)
The complete context passed to an Executor.
| Field | Description |
|---|---|
bot_id / task_id | Caller identifier |
operation | Operation name |
params | Tool parameters |
tool_name | Original tool name (used for routing when multiple tools share the same operation) |
credential_fields | Credentials decrypted by Guardian |
resource_config | Resource connection configuration |
resource_id | Resource ID (used for dynamic type routing) |
delegate | Delegation constraints |
credential_refresh | Async refresh callback on 401 |
ExecuteResult (Execution Result)
| Field | Description |
|---|---|
success | Whether successful |
data | Return data |
error | Error message |
status | SUCCESS / ERROR / SCOPE_VIOLATION |
ExecutorRegistry (Executor Registry)
A global singleton that manages all Executor instances. Routes tool calls to the corresponding Executor by type_id.
RunnerState (Runner Global State)
A service-level singleton that holds references to all runtime components.
| Field | Description |
|---|---|
llm_provider | Default LLM Provider |
core_client / guardian_client / auditor_client | Inter-service clients |
session_manager | Session manager |
event_dispatcher | Event dispatcher |
agent_connection_manager | Agent WebSocket connection manager |
adapter_manager | Gateway adapter manager (cross-service message delivery) |
workspace_prompts | Workspace-level prompt cache |
active_tasks | Running async tasks (execution_id → asyncio.Task) |
active_executors | Running workflow executors (execution_id → WorkflowExecutor) |
Guardian / PolicyDecisionPoint (Permission Decision Engine)
Post-LLM parameter-level permission validation.
Two-level authorization:
- Coarse-grained (
can_access()): Is the operation inallowed_operations? - Fine-grained (
authorize()): Do the parameters conform toscope_constraints?
Scope Checker: Built-in checkers (github, gitlab, ssh, web, bot, gmail, notion, monstrum-agent, etc.) + declarative generic checker (check_scope_declarative()).
Sessions and Messages
Session
An active conversation between a user and a Bot.
| Field | Description |
|---|---|
id | Session UUID |
source | Channel type (web / slack / feishu / discord / telegram / webhook) |
channel_id | Channel or user ID |
bot_id | The Bot in conversation |
chat_type | private (direct message) / group (group chat) |
config_id | Gateway configuration ID (for precise routing) |
Session Key: (config_id|source, channel_id, bot_id) — config_id takes priority (the same Bot can be bound to multiple Gateways).
Lifecycle:
- First message → Create Session (load persisted history)
- Each message → SessionWorker FIFO processing
- 30 minutes idle → Trigger memory extraction → Save session → Recycle Session
MessageEnvelope
Unified internal message format. All external channel messages are first converted to Envelopes before processing.
| Field | Description |
|---|---|
id | Message UUID |
channel_type | Channel type |
channel_id | Channel ID |
bot_id | Target Bot |
sender_id / sender_name | Sender identifier and display name |
content | Message content |
chat_type | private / group |
config_id | Gateway configuration ID |
workspace_id | Workspace |
SessionWorker (Session Worker Coroutine)
One background coroutine per Session, processing messages sequentially via a FIFO queue. Responsible for calling AgentRuntime and delivering responses.
ResponseDelivery
Exponential backoff retry mechanism for delivering responses back to external channels (via Gateway adapters).
MessageDeduplicator
Sliding window deduplication to prevent duplicate messages caused by Gateway webhook retries.
Session Persistence
Channel-level conversation persistence (toggled by administrators via the /chathis command).
| Concept | Description |
|---|---|
ChannelConversation | DB table, unique constraint on (config_id, channel_id, bot_id) |
| Save timing | When Session expires |
| Load timing | When Session is created |
| Compression threshold | Triggered at 80 messages |
| Compression strategy | LLM summarizes old messages → keep latest 50 messages + summary |
Memory System
MemoryEntry (Memory Entry)
A single memory entry.
| Field | Description |
|---|---|
id | UUID |
category | preference / rule / event / knowledge |
content | Memory content |
source | user (manual) / auto (LLM-extracted) / system (system-generated) |
importance | Importance score from 1-10 |
origin | Source scope (see below) |
Origin (Memory Scope)
Memories are managed by partition based on the origin field, which determines in what context the memory is visible.
| origin value | Meaning | Visible scope |
|---|---|---|
NULL | Global memory | All contexts |
channel:{config_id}:{channel_id} | Channel memory | Sessions in that IM channel |
task:{task_id} | Task memory | During that task’s execution |
resource:{resource_id} | Resource memory | Contexts where that Resource is bound |
schedule:{schedule_id} | Schedule memory | When that scheduled task fires |
workflow:{workflow_id} | Workflow memory | When that workflow executes |
MemoryContext
Three-layer memory cache in Session mode.
| Layer | Content | Load timing |
|---|---|---|
_base | Global + current scope memories | When Session is created |
_resource | Memories related to bound Resources | After ToolResolver completes |
_injected | Dynamically loaded cross-scope read-only memories | When Bot calls memory_load_context |
Key mechanisms:
invalidate()— marks base as needing reload aftermemory_writeinject(scope)/unload(scope)— load/unload cross-scope memories at runtimeget_all()— merges all three layers, sorted by importance descending
MemoryExtractor
LLM-driven automatic memory extraction.
Trigger timing: Task completion / Session expiration (when auto_memory is enabled)
Process:
- Format conversation history
- Inject current memories as reference
- LLM analysis → output JSON (category + content + importance)
- Atomically replace
source=automemory entries (delete all then insert)
Prompt Management
Prompt Key
9 centrally managed prompts, defined in services/runner/prompts.py.
| Key | Purpose | Supported Variables |
|---|---|---|
default_task_system | Task mode system prompt | {bot_name}, {bot_description} |
default_session_system | Session mode system prompt | {bot_name}, {bot_description} |
group_chat | Group chat supplementary prompt | — |
planning | Planning reasoning mode instructions | — |
adaptive | Adaptive reasoning mode instructions | — |
memory_extraction | Memory extraction prompt | {current_section}, {conversation} |
memory_extraction_system | Memory extraction system role | — |
conversation_summary | Conversation compression prompt | {conversation_text} |
conversation_summary_system | Conversation compression system role | — |
Three-Layer Resolution
Priority: Bot-level (runtime_config.prompts) > Workspace-level (DB table) > Code default (PROMPT_DEFAULTS)
get_prompt(bot_config, key, workspace_prompts=...) → str
System Prompt Assembly Order
Session mode (SessionManager) and Task mode (AgentRuntime) have different assembly paths:
Session mode:
1. Bot.system_prompt (identity description) or default_session_system template
2. group_chat supplementary prompt (only in group chat)
3. ## Available Resources (resource group summary from ToolResolver output)
4. ## Bot Memories (MemoryContext formatted by scope partition)
5. Skill instructions (Markdown content loaded from DB)
Task mode:
1. Bot.system_prompt (identity description) or default_task_system template
2. ## Available Resources (resource group summary from ToolResolver output)
3. ## Bot Memories (global + task + resource memories)
4. Agent Mode instructions (planning / adaptive, if configured)
5. Skill instructions
Difference: The
group_chatprompt is only injected in group chat scenarios in Session mode; Agent Mode instructions (planning/adaptive) are only injected in Task mode.
PromptEngine (7-Layer Assembly Engine)
services/runner/prompt_engine.py — A structured prompt assembler replacing simple string concatenation.
7 layers (in priority order):
| Layer | Description |
|---|---|
| Platform | Platform base system instructions |
| Workspace | Workspace-level prompt templates (DB) |
| Identity | Bot custom system_prompt / description |
| Scene | Scene instructions (group chat / task mode, etc.) |
| Resources | ## Available Resources resource group summary |
| Skills | Skill instructions (enabled Skills) |
| Memories | ## Bot Memories, with recency decay scoring and token budget eviction |
Event System
PlatformEvent
Events covering the full lifecycle.
| Field | Description |
|---|---|
event_type | Event type (e.g., task.completed, schedule.fired) |
source_type | Source type: task / workflow / schedule / session / bot |
source_id | Source ID |
workspace_id | Workspace |
data | Event payload |
Event type categories:
| Prefix | Source | Examples |
|---|---|---|
task.* | AgentRuntime | task.completed, task.failed |
workflow.* | WorkflowExecutor | workflow.completed, workflow.failed |
schedule.* | SchedulerService | schedule.fired |
session.* | SessionManager | session.created, session.expired |
runner.* | ToolExecutor | runner.github.issue.created (see RunnerEvent format explanation below) |
custom.* | Bot (emit_event) | User-defined |
RunnerEvent (Execution Event)
Fine-grained events published after tool execution.
| Field | Description |
|---|---|
capability | Resource type (e.g., github) |
operation | Operation name (e.g., issue.create) |
result | SUCCESS / FAILURE / ERROR / DENIED |
event_type | Auto-generated (see below) |
event_type generation rules: runner.{capability}.{transformed_operation}
The last segment of the operation is transformed based on the result (not simply appending the result):
issue.create+ SUCCESS →issue.created→ event_type =runner.github.issue.createdissue.create+ FAILURE →issue.create_failed→ event_type =runner.github.issue.create_failedexec.run+ SUCCESS →exec.ran→ event_type =runner.ssh.exec.ran
EventSubscription
A subscription created by a Bot via the subscribe_event tool.
| Field | Description |
|---|---|
pattern | fnmatch matching pattern (e.g., task.*, schedule.fired) |
bot_id | Subscriber Bot |
instruction | Instruction injected when the event fires |
target_type | Delivery target: bot (default) / workflow |
workflow_id | Target workflow (when target_type=workflow) |
EventDispatcher
The event routing bridge. Listens to EventBus, matches subscriptions, and delivers to targets.
Delivery strategy:
- Bot target: Preferentially inject into active Session → if no Session, create a Task
- Workflow target: Directly trigger workflow execution
Security mechanism: Re-checks binding permissions before delivery (bindings may have been revoked after subscription creation).
WorkflowTrigger
A persisted event→workflow mapping (stored in DB, loaded at startup).
| Field | Description |
|---|---|
event_pattern | fnmatch pattern |
workflow_id | Target workflow |
instruction | Instruction passed to the workflow |
EventBus
Publish/subscribe bus. Supports both RunnerEvent and PlatformEvent payloads.
Workflows
Workflow (Workflow Definition)
A visual DAG workflow.
| Field | Description |
|---|---|
id | UUID |
name | Workflow name |
bot_id | Owning Bot |
status | DRAFT / ACTIVE / DISABLED |
steps | Step list (Step[]) |
edges | Edge list (Edge[]) |
Step (Workflow Step)
| Field | Description |
|---|---|
id | UUID |
type | Step type (see below) |
config | Step configuration (varies by type) |
input_mapping | Variable mapping (e.g., {{step1.output.field}}) |
timeout_seconds | Step timeout |
max_retries | Maximum retry count |
StepType (Step Types):
| Type | Description |
|---|---|
START | Entry node |
END | Exit node |
RESOURCE_CALL | Call a resource tool |
CONDITION | Conditional branch (AST safe expression evaluation) |
PARALLEL | Parallel branches (fail-fast by default) |
WAIT | Delay wait |
APPROVAL | Manual approval (pauses execution, awaits approval) |
LLM_CALL | Single LLM call |
FOR_EACH | Loop iteration |
SUB_WORKFLOW | Sub-workflow |
SCRIPT | Script execution (future extension) |
Edge (Connection)
A directed connection between steps.
| Field | Description |
|---|---|
source_step_id | Source step |
target_step_id | Target step |
condition | Condition expression (used for conditional branches) |
WorkflowExecution
| Field | Description |
|---|---|
id | Execution ID |
workflow_id | Workflow ID |
status | PENDING / RUNNING / SUCCESS / FAILED / CANCELLED / TIMEOUT / WAITING_APPROVAL |
triggered_by | manual / schedule / trigger / api |
WorkflowExecutor
Safety mechanisms:
- DAG cycle detection: DFS three-color marking
- Timeout budget: min(global deadline, step timeout)
- Parallel Fail-Fast:
asyncio.wait(FIRST_EXCEPTION)+ cancel sibling branches - Safe expressions: AST whitelist visitor (forbids
eval()) - Variable pipeline: Inter-step variable passing,
ast.literal_evalfor value parsing
Trigger methods:
| Method | Entry Point |
|---|---|
| API direct trigger | POST /workflows/{id}/execute |
| Scheduled trigger | Schedule.config target_type: "workflow" |
| Event trigger | WorkflowTrigger + EventDispatcher |
Tasks and Scheduling
Task
An asynchronous work unit assigned to a Bot for execution.
| Field | Description |
|---|---|
id | UUID |
bot_id | Executing Bot |
instruction | Task instruction |
status | PENDING / RUNNING / COMPLETED / FAILED / CANCELLED |
constraints | Execution constraints (TaskConstraints) |
output | Task result |
token_usage | Token consumption (TokenUsage) |
parent_task_id | Parent task (Bot-to-Bot delegation chain) |
call_depth | Call depth |
checkpoint | Checkpoint state (recoverable) |
progress | Progress {percentage, message} |
TaskConstraints
| Field | Description |
|---|---|
max_tokens | Token limit |
timeout | Timeout in seconds |
allowed_tools | Tool whitelist |
Schedule (Scheduled Task)
Cron-driven scheduled tasks.
| Field | Description |
|---|---|
bot_id | Executing Bot |
cron_expr | Cron expression |
timezone | Timezone |
input_template | Task template when triggered |
config | JSON configuration dictionary |
target_type | @property, reads config["target_type"], defaults to task, optionally workflow |
workflow_id | @property, reads config["workflow_id"] (when type is workflow) |
Plugin System
PluginManifest (Plugin Manifest)
The complete declaration of a plugin. A single monstrum.yaml file defines everything.
| Field | Description |
|---|---|
id | Plugin ID |
name / version | Name and version |
resource_type | ResourceType declaration (tools, permissions, credentials, configuration) |
executor | Executor module/class reference |
dependencies | Python dependencies |
PluginManager
Orchestrates the complete plugin lifecycle: scan plugins/ → parse Manifest → persist ResourceType to DB → load/register Executor → reload ToolCatalog.
PluginLoader
Dynamically loads Executor classes via importlib. Supports auto-discovery (the sole ExecutorBase subclass in the directory) and explicit specification (module + class_name).
Built-in vs Plugin
| Built-in | Plugin | |
|---|---|---|
| Location | services/runner/executors/ | plugins/{name}/ |
| Loading | Hard-coded registration at platform startup | PluginManager auto-discovery |
builtin | True | False |
| Deletable | No | Yes |
| Declaration | builtin_resource_types.py | monstrum.yaml |
SDK
The monstrum_sdk/ package provides two paths for accessing platform capabilities.
PluginClient
Tool-level calls that go through the full Guardian permission chain. Suitable for Skills, Workflow steps, and cross-plugin composition.
client = get_plugin_client("github", bot_id, task_id)
await client.list_issues(repo="org/repo") # Auto-builds tool_name, goes through Guardian
Platform SDK
Direct access to built-in Executor capabilities, bypassing Guardian. Suitable for trusted code within the platform.
from monstrum_sdk import platform
await platform.ssh.run(host="dev-1", command="uptime", credential="key-1")
await platform.mcp.call_tool(tool="get_menu", params={...})
await platform.bot.execute_task(bot_id="b1", instruction="...")
await platform.events.emit(name="custom.deploy", data={...})
Namespaces: ssh, mcp, bot, web, web3, oauth, events
HttpExecutorBase (HTTP Executor Base Class)
A common base class for HTTP API plugins.
| Feature | Description |
|---|---|
_http_get/post/patch/delete | Standard HTTP methods |
_build_auth_headers | Automatically build authentication headers |
| 401 auto-refresh | credential_refresh callback |
_paginate() | Link header pagination |
PluginClient vs Platform SDK
| PluginClient | Platform SDK | |
|---|---|---|
| Permission check | Full Guardian chain | None (caller is responsible) |
| Use case | Skills / Workflow / cross-plugin | Trusted code within the platform |
| Credential source | Decrypted and injected by Guardian | Explicitly passed by caller |
| Audit logging | Automatically recorded | Not recorded |
Gateways and Adapters
GatewayConfig (Gateway Configuration)
Configuration for connecting an external channel.
| Field | Description |
|---|---|
id | Configuration ID (i.e., config_id) |
source_type | Channel type (slack / feishu / telegram / discord / webhook / webchat) |
bot_id | Bot to route to |
config | Channel-specific configuration (API Token, Webhook URL, etc.) |
Adapter
Channel protocol adapter. Responsible for receiving external messages → converting to MessageEnvelope → delivering responses.
Built-in adapters: Slack, Feishu, Telegram, Discord, Webhook, Web Chat
AdapterManager
Adapter lifecycle management. Supports registration/deregistration/lookup of adapters by config_id. Injected cross-service into RunnerState for use by features like send_to_channel.
Users and Multi-Tenancy
User
Platform user account.
| Field | Description |
|---|---|
id | UUID |
email / username | Login credentials |
display_name | Display name |
locale | Language preference |
Workspace
Tenant isolation space. All entities (Bots, Resources, audit logs) are isolated by Workspace.
| Field | Description |
|---|---|
id | UUID |
name / slug | Name and URL identifier |
settings | Configuration (default LLM model, Token budget, etc.) |
WorkspaceMember
| Role | Permissions |
|---|---|
OWNER | Highest privileges |
ADMIN | Administrative privileges |
MEMBER | Standard member |
VIEWER | Read-only |
BotGroup
Logical grouping of Bots. A Workspace can have multiple BotGroups, and each Bot can belong to multiple Groups.
| Field | Description |
|---|---|
id | UUID |
workspace_id | Owning workspace |
name | Group name (unique within workspace) |
description | Description |
icon | Display icon |
Tenant Isolation (TenantContext)
Multi-tenant data isolation mechanism with dual guarantees at both the application layer and database layer.
| Component | Description |
|---|---|
TenantContext | contextvars.ContextVar, async-safe workspace context |
| PostgreSQL RLS | Row-Level Security policies enabled on core tables |
TenantCache | Thread-safe in-memory cache with per-tenant isolated namespaces |
TenantLogFilter | Log filter that automatically injects tenant_id |
create_tenant_task() | Propagates tenant context across asyncio.create_task() boundaries |
Audit and Cost
ActionLog (Operation Log)
Full-chain audit entries.
| Field | Description |
|---|---|
task_id / bot_id | Associated entities |
operation | Operation type (see below) |
result | Result summary |
action_detail | Full details (JSON) |
permission_granted | Whether permission was granted |
prompt_tokens / completion_tokens | Token consumption (for LLM requests) |
model | Model used |
duration_ms | Execution duration |
execution_location | Execution location: "cloud" / "agent:{name}" (Agent routing audit) |
OperationType (Operation Types):
| Type | Description |
|---|---|
TOOL_CALL | Tool call |
LLM_REQUEST | LLM request |
PERMISSION_CHECK | Permission check |
TASK_START / TASK_COMPLETE / TASK_FAIL | Task lifecycle |
CREDENTIAL_ACCESS | Credential access |
SCHEDULE_TRIGGERED / SCHEDULE_COMPLETED / SCHEDULE_FAILED | Schedule lifecycle |
TokenStatistics
| Field | Description |
|---|---|
total_prompt / total_completion | Cumulative tokens |
total_tasks | Total tasks |
estimated_cost_usd | Estimated cost |
budget_remaining | Remaining budget |
is_budget_exceeded | Whether budget is exceeded |
Budget Enforcement
monthly_token_budget (BotRuntimeConfig) → accumulates after each LLM request → automatically terminates execution when budget is exceeded.
LLM Provider
LLMProvider
LLM API integration configuration.
| Field | Description |
|---|---|
id | UUID |
provider_type | Provider type (see below) |
base_url | Custom API endpoint |
models | Available model list (ModelInfo[]) |
default_model | Default model |
is_default | Whether this is the workspace default |
encrypted_api_key | Encrypted API Key |
Supported Provider types:
- International:
ANTHROPIC,OPENAI,GEMINI,MISTRAL - China domestic:
DEEPSEEK,QWEN,KIMI,GLM,DOUBAO,HUNYUAN,ERNIE - Local:
OLLAMA,VLLM,LMSTUDIO - Custom:
CUSTOM_OPENAI(any service compatible with the OpenAI API)
ModelInfo (Model Information)
| Field | Description |
|---|---|
id | Model identifier (e.g., claude-sonnet-4-5-20250929) |
input_price | Input price (USD/million tokens) |
output_price | Output price (USD/million tokens) |
context_window | Context window size |
Design Principles Quick Reference
For detailed discussion, see the Design Paradigm section.
| Principle | One-liner |
|---|---|
| Bot is an autonomous actor | Not an LLM wrapper, but the governance shell the platform puts around the LLM (identity + permissions + credentials + memory + budget + audit) |
| Platform-enforced, not AI self-discipline | Security is guaranteed by architecture, not by relying on LLM self-restraint |
| Least privilege | Invisible = non-existent. Unauthorized tools are completely invisible to the LLM |
| Credential isolation | LLM never touches plaintext credentials; injection happens at the execution layer |
| Fail-Closed | Any component failure → Bot can do nothing, rather than being unrestricted |
| Declarative permissions | Plugins declare ScopeDimension, the platform’s universal engine checks automatically |
| Two independent filtering layers | Pre-LLM (visibility) + Post-LLM (parameter validation); either layer alone is sufficient to prevent escalation |
| Delegation without escalation | Bot-to-Bot calls: child Bot effective permissions = delegation constraints ∩ own permissions |
| Bot self-owned vs external resource | Operating on own data does not go through Guardian; operating on external systems goes through the full permission chain |
| Workspace isolation | All entities are isolated by workspace; event delivery checks consistency |
| Single process, splittable | Runs merged, but maintains Local Client abstraction for future microservice decomposition |