-
Notifications
You must be signed in to change notification settings - Fork 2
Addon System
This page explains the TMI addon system, which allows administrators to register webhook-based extensions that enhance threat modeling workflows.
What are addons?
Addons are built on TMI's Webhook-Integration, extending it beyond automatic event-driven notifications. While standard webhooks fire automatically in response to threat model changes, addons can be:
- Manually invoked by users through the Actions/Addons menus in TMI-UX — for both threat models and individual entities within them
- Triggered automatically by threat model changes (like standard webhooks)
- Called programmatically by external automation via the TMI API
Example addons:
- STRIDE analysis automation
- Compliance framework checking
- Threat intelligence enrichment
- Diagram validation
- Custom report generation
- AI-powered threat suggestions
Key characteristics:
- Administrator-registered: Only admins can create/delete addons
- User-invoked: Any authenticated user can trigger addons
- Asynchronous: Long-running operations with status updates
- Webhook-based: External services receive invocation requests
- Rate-limited: Prevents abuse with per-user quotas
flowchart TD
A[User Invokes Addon<br/><i>via TMI-UX menu, API call,<br/>or automated trigger</i>] --> B[TMI Server]
B --> C{Rate Limit Check<br/><i>Redis</i>}
C -->|Allowed| D[Create Invocation<br/><i>Redis</i>]
C -->|Exceeded| E[429 Too Many Requests]
D --> F[Addon Worker<br/>HTTP POST]
F --> G[External Service]
G --> H[Process Asynchronously<br/><i>Security analysis,<br/>augmentation, etc.</i>]
H --> I[POST /webhook-deliveries/{id}/status<br/><i>Progress updates</i>]
H --> J[POST/PUT/PATCH<br/>/threat_model/{id}/...<br/><i>Write results back to TMI</i>]
I --> K[Update Status<br/><i>Redis</i>]
K --> L[User Polls Status]
Components:
- Addons Table (database): Addon registrations
- Invocation Store (Redis): Ephemeral invocation state (7-day TTL)
- Rate Limiter (Redis): Per-user quotas
- Addon Worker: Delivers invocation requests to webhooks
- Status Update Handler: Receives status updates from external services
Most users will interact with addons through the TMI-UX interface rather than the API directly.
From a threat model: Open a threat model, then use the Actions menu (or Addons submenu) to see addons available for the threat model as a whole.
From an entity: When viewing an entity within a threat model (e.g., an asset, threat, or diagram), use the entity's Actions/Addons menu to invoke addons scoped to that entity type. Only addons whose objects list includes the entity type will appear.
Each addon displays its name and icon in the menu. When invoked, TMI-UX shows the addon's description and any parameter fields the addon requires. After invocation, you receive a delivery ID to track progress.
Note: Monitoring the status of addon invocations is not currently provided in TMI-UX. Use the API to poll invocation status (see Checking Invocation Status below).
List available addons:
GET /addons
# Filter by threat model
GET /addons?threat_model_id={threat_model_id}Response:
{
"addons": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "STRIDE Analysis",
"description": "Automated STRIDE threat analysis",
"icon": "material-symbols:security",
"objects": ["threat_model", "asset"],
"threat_model_id": null,
"created_at": "2025-01-15T10:00:00Z"
},
{
"id": "7d8f6e5c-4b3a-2190-8765-fedcba987654",
"name": "Compliance Checker",
"description": "Check against NIST/ISO frameworks",
"icon": "material-symbols:shield_lock",
"objects": ["threat_model"],
"created_at": "2025-01-15T11:00:00Z"
}
],
"total": 2,
"limit": 50,
"offset": 0
}Trigger an addon to process your threat model:
POST /addons/{addon_id}/invoke
{
"threat_model_id": "threat-model-uuid",
"object_type": "asset", # Optional
"object_id": "asset-uuid", # Optional
"data": { # Custom data, max 1KB JSON-serialized
"analysis_type": "full",
"include_recommendations": true
}
}Response (202 Accepted):
{
"delivery_id": "abc-123-def-456",
"status": "pending",
"created_at": "2025-01-15T12:00:00Z"
}Poll for status updates:
GET /webhook-deliveries/{delivery_id}Response:
{
"id": "abc-123-def-456",
"subscription_id": "webhook-subscription-uuid",
"event_type": "addon.invoked",
"addon_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "in_progress",
"status_percent": 75,
"status_message": "Analyzing assets...",
"created_at": "2025-01-15T12:00:00Z",
"last_activity_at": "2025-01-15T12:02:30Z",
"attempts": 1
}Status values:
-
pending: Queued for processing -
in_progress: Currently processing -
delivered: Successfully delivered (auto-complete mode) -
completed: Successfully finished (set via async callback) -
failed: Processing failed
Note: There is no public endpoint for listing all deliveries. Administrators can list all deliveries via
GET /admin/webhooks/deliveries(see Monitoring Invocations below).
Default quotas per user:
- Active invocations: 3 concurrent (pending or in_progress)
- Hourly rate: 10 invocations per hour
Rate limit error (429 Too Many Requests):
When you exceed the active invocation limit, the response includes details about blocking invocations:
{
"error": "rate_limit_exceeded",
"error_description": "Active invocation limit reached: 3/3 concurrent invocations.",
"details": {
"context": {
"limit": 3,
"current": 3,
"retry_after": 542,
"blocking_invocations": [
{
"delivery_id": "abc-123",
"addon_id": "def-456",
"status": "in_progress",
"created_at": "2025-01-15T12:00:00Z",
"expires_at": "2025-01-15T12:15:00Z",
"seconds_remaining": 542
}
]
},
"suggestion": "Wait for an existing invocation to complete, or retry after 542 seconds when the oldest will timeout."
}
}The response also includes a Retry-After HTTP header with the recommended wait time in seconds.
Contact your administrator if you need higher limits.
Before creating addons, you need:
-
Administrator privileges (configured in YAML):
administrators: # User admin by provider ID (preferred) - provider: "google" provider_id: "101155414856250184779" email: "admin@example.com" subject_type: "user" # Group-based admin - provider: "microsoft" group_name: "security-team" subject_type: "group"
-
Active webhook subscription (admin endpoint):
POST /admin/webhooks/subscriptions { "name": "STRIDE Analyzer Service", "url": "https://analyzer.example.com/webhooks/tmi", "events": ["addon.invoked"], # At least one event required "secret": "your-hmac-secret" }
Administrators can register addons through the TMI-UX admin interface at /admin/addons. The form includes the following fields:
| Field | Purpose |
|---|---|
| name | Displayed in the Actions/Addons invocation menu in TMI-UX |
| webhook_id | Links the addon to an existing webhook subscription |
| description | Explains what the addon does; shown to users before invocation |
| icon | Displayed next to the name in the menu. Must be a Material Symbols reference in the format material-symbols:<icon_name> (e.g., material-symbols:security) |
| objects | Entity types the addon applies to (e.g., threat_model, asset). Controls which menus the addon appears in |
| threat_model_id | Optional. Scopes the addon to a specific threat model |
| parameters | Optional. Typed parameter declarations that TMI-UX renders as form fields when users invoke the addon |
Create addon registration:
POST /addons
{
"name": "STRIDE Analysis",
"webhook_id": "webhook-uuid",
"description": "Automated STRIDE threat analysis",
"icon": "material-symbols:security",
"objects": ["threat_model", "asset"],
"threat_model_id": null, # Optional: scope to specific threat model
"parameters": [ # Optional: typed parameter declarations
{
"name": "analysis_type",
"type": "enum",
"description": "Type of analysis to perform",
"required": true,
"enum_values": ["full", "quick", "targeted"],
"default_value": "full"
}
]
}Response (201 Created):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "STRIDE Analysis",
"webhook_id": "webhook-uuid",
"description": "Automated STRIDE threat analysis",
"icon": "material-symbols:security",
"objects": ["threat_model", "asset"],
"parameters": [
{
"name": "analysis_type",
"type": "enum",
"description": "Type of analysis to perform",
"required": true,
"enum_values": ["full", "quick", "targeted"],
"default_value": "full"
}
],
"created_at": "2025-01-15T10:00:00Z"
}Field validation:
icon (optional):
- Must reference an actual Material Symbols icon in the format
material-symbols:icon_name(e.g.,material-symbols:security,material-symbols:shield_lock) - Max length: 60 characters
objects (optional):
- Valid types:
threat_model,diagram,asset,threat,document,note,repository,metadata,survey,survey_response - When set, the object type is validated at invocation time (the invocation request
object_typemust be in the addon'sobjectslist)
threat_model_id (optional):
- Scope addon to specific threat model
- Only visible/invocable within that threat model
parameters (optional):
- Up to 20 typed parameter declarations for client UI generation
- Each parameter has a
name,type, and type-specific configuration - Supported types:
enum,boolean,string,number,metadata_key - Parameters can be marked
required; required parameters are validated at invocation time - Enum parameters must include
enum_values;metadata_keyparameters must includemetadata_key - String parameters support
string_max_lengthandstring_validation_regexconstraints - Number parameters support
number_minandnumber_maxconstraints
DELETE /addons/{addon_id}Response: 204 No Content (success)
Deletion rules:
- Blocked if active invocations exist (status: pending or in_progress)
- Error (409 Conflict): "Cannot delete addon - X active invocations exist"
- Allowed once all invocations complete or fail
- Cascaded when webhook is deleted (ON DELETE CASCADE)
Set custom quotas for specific users via the admin API:
List all custom quotas:
GET /admin/quotas/addonsGet quota for a specific user:
GET /admin/quotas/addons/{user_id}Set or update quota for a user:
PUT /admin/quotas/addons/{user_id}
{
"max_active_invocations": 5,
"max_invocations_per_hour": 50
}Delete custom quota (reverts to defaults):
DELETE /admin/quotas/addons/{user_id}For detailed operator configuration including database schema, Redis monitoring, and backup procedures, see the Addon Configuration Guide.
Administrators can view all webhook deliveries (including addon invocations):
GET /admin/webhooks/deliveries
# Returns all deliveries across all subscriptions
# Filter by subscription
GET /admin/webhooks/deliveries?subscription_id={subscription_id}Individual delivery details:
GET /admin/webhooks/deliveries/{delivery_id}Note: Addon invocations are stored as webhook delivery records in Redis with a 7-day TTL. They can be identified by their
event_typeofaddon.invokedand the presence of anaddon_idfield.
Your webhook service must:
- Accept POST requests at the registered URL
- Verify HMAC signatures for security
- Respond with 200 OK within 30 seconds
- Process asynchronously (don't block response)
- Choose a callback mode (auto-complete or async callbacks)
TMI supports two callback modes, controlled by the X-TMI-Callback response header:
Auto-Complete Mode (Default)
When your webhook returns a 2xx response without the X-TMI-Callback header, TMI automatically marks the invocation as delivered. Use this when:
- Your webhook handles work synchronously
- You don't need to report progress
- The invocation is "fire and forget"
# Auto-complete mode - invocation marked complete immediately
return '', 200Async Callback Mode
When your webhook returns X-TMI-Callback: async, TMI marks the invocation as in_progress and expects callbacks. Use this when:
- Processing takes significant time
- You want to report progress percentages
- You need to report success/failure after processing
# Async mode - you must call back with status updates
return '', 200, {'X-TMI-Callback': 'async'}Important: If using async mode without calling back, the invocation times out after 15 minutes and is marked failed.
When a user invokes your addon, you receive:
POST /webhooks/tmi
Content-Type: application/json
X-Webhook-Event: addon.invoked
X-Webhook-Delivery-Id: abc-123-def-456
X-Webhook-Subscription-Id: subscription-uuid
X-Webhook-Signature: sha256=abc123...
User-Agent: TMI-Webhook/1.0
{
"event_type": "addon.invoked",
"threat_model_id": "threat-model-uuid",
"object_type": "asset",
"object_id": "asset-uuid",
"timestamp": "2025-01-15T12:00:00Z",
"data": {
"addon_id": "550e8400-e29b-41d4-a716-446655440000",
"user_data": {
"analysis_type": "full",
"include_recommendations": true
}
}
}Always verify HMAC signatures:
import hmac
import hashlib
def verify_signature(payload_bytes, signature, secret):
expected = hmac.new(
secret.encode('utf-8'),
payload_bytes,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
@app.route('/webhooks/tmi', methods=['POST'])
def handle_invocation():
signature = request.headers.get('X-Webhook-Signature')
payload_bytes = request.get_data()
if not verify_signature(payload_bytes, signature, WEBHOOK_SECRET):
return 'Unauthorized', 401
# Process invocation
delivery_id = request.headers.get('X-Webhook-Delivery-Id')
invocation = request.json
queue.enqueue(process_invocation, delivery_id, invocation)
return '', 200Call back to TMI to update status:
import requests
import hmac
import hashlib
import json
def update_status(delivery_id, status, percent, message=''):
payload = {
'status': status,
'status_percent': percent,
'status_message': message
}
payload_str = json.dumps(payload)
# Generate HMAC signature
signature = hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
payload_str.encode('utf-8'),
hashlib.sha256
).hexdigest()
response = requests.post(
f'https://api.tmi.dev/webhook-deliveries/{delivery_id}/status',
data=payload_str,
headers={
'Content-Type': 'application/json',
'X-Webhook-Signature': f'sha256={signature}'
}
)
return response.status_code == 200
# Usage during processing
update_status(delivery_id, 'in_progress', 10, 'Starting analysis...')
# ... do work ...
update_status(delivery_id, 'in_progress', 50, 'Analyzing assets...')
# ... do more work ...
update_status(delivery_id, 'completed', 100, 'Analysis complete')Status update validation:
-
status: Must bein_progress,completed, orfailed -
status_percent: Must be 0-100 -
status_message: Optional, max 255 characters
Valid transitions:
-
pending→delivered(auto-complete mode: webhook returns 2xx withoutX-TMI-Callbackheader) -
pending→in_progress→completed(async callback mode) -
pending→in_progress→failed(async callback mode) - Cannot transition from
delivered,completed, orfailedto any other status
Complete Python example:
from flask import Flask, request
import hmac
import hashlib
import json
import requests
app = Flask(__name__)
WEBHOOK_SECRET = 'your-webhook-secret'
@app.route('/webhooks/tmi', methods=['POST'])
def handle_invocation():
# Verify signature
signature = request.headers.get('X-Webhook-Signature')
payload_bytes = request.get_data()
if not verify_signature(payload_bytes, signature, WEBHOOK_SECRET):
return 'Unauthorized', 401
# Queue for async processing
delivery_id = request.headers.get('X-Webhook-Delivery-Id')
invocation = request.json
queue.enqueue(process_invocation, delivery_id, invocation)
return '', 200
def process_invocation(delivery_id, invocation):
threat_model_id = invocation['threat_model_id']
user_data = invocation['data'].get('user_data', {})
try:
# Start processing
update_status(delivery_id, 'in_progress', 0, 'Starting STRIDE analysis')
# Fetch threat model data from TMI API
threat_model = fetch_threat_model(threat_model_id)
# Perform analysis
update_status(delivery_id, 'in_progress', 25, 'Analyzing threats')
threats = analyze_stride(threat_model)
update_status(delivery_id, 'in_progress', 50, 'Analyzing assets')
asset_risks = analyze_assets(threat_model)
update_status(delivery_id, 'in_progress', 75, 'Generating report')
report = generate_report(threats, asset_risks)
# Complete
update_status(delivery_id, 'completed', 100, 'Analysis complete')
except Exception as e:
# Report failure
update_status(delivery_id, 'failed', 0, f'Error: {str(e)}')
def fetch_threat_model(threat_model_id):
response = requests.get(
f'https://api.tmi.dev/api/v1/threat-models/{threat_model_id}',
headers={'Authorization': f'Bearer {TMI_TOKEN}'}
)
response.raise_for_status()
return response.json()
if __name__ == '__main__':
app.run(port=5000)-
Local development:
# Expose local service with ngrok ngrok http 5000 # Use ngrok URL for webhook registration
-
Register webhook and addon:
# Create webhook (admin token required) curl -X POST https://api.tmi.dev/admin/webhooks/subscriptions \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d '{"name":"Test","url":"https://abc.ngrok.io/webhooks/tmi","events":["addon.invoked"],"secret":"test-secret-min16"}' # Create addon (admin token required) curl -X POST https://api.tmi.dev/addons \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d '{"name":"Test Addon","webhook_id":"webhook-uuid"}'
-
Invoke and monitor:
# Invoke addon curl -X POST https://api.tmi.dev/addons/{addon_id}/invoke \ -H "Authorization: Bearer $TOKEN" \ -d '{"threat_model_id":"tm-uuid","data":{}}' # Check status curl https://api.tmi.dev/webhook-deliveries/{delivery_id} \ -H "Authorization: Bearer $TOKEN"
- Check status regularly: Poll invocations endpoint for updates
- Handle rate limits: Don't invoke excessively
- Provide context: Use payload to give addon context
- Monitor failures: Check failed invocations for errors
- Vet addon services: Only register trusted webhook services
- Set appropriate quotas: Balance usage and system load
- Monitor performance: Track invocation success rates
- Clean up unused addons: Delete addons no longer needed
- Document addons: Provide users with addon documentation
- Respond quickly: Return 200 OK within 30 seconds
- Process asynchronously: Don't block webhook response
- Update status frequently: Keep users informed of progress
- Handle errors gracefully: Report failures with helpful messages
- Implement timeouts: Don't let processing run indefinitely
- Verify signatures: Always validate HMAC signatures
- Use idempotency: Handle duplicate invocations gracefully
Check:
- Addon exists and is accessible
- Rate limits not exceeded
- Webhook subscription is
active - Webhook service is reachable
Debug:
# Check addon
GET /addons/{addon_id}
# Check webhook status (admin only)
GET /admin/webhooks/subscriptions/{webhook_id}
# Check invocation
GET /webhook-deliveries/{delivery_id}Causes:
- Invalid HMAC signature on status update
- Invalid status transition
- Invocation expired (7-day TTL)
- Network issues from addon service
Debug:
# Check server logs
grep "invocation" /var/log/tmi/server.log
# Verify signature generation
# Test status update manually
curl -X POST https://api.tmi.dev/webhook-deliveries/{id}/status \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: sha256=..." \
-d '{"status":"completed","status_percent":100}'Investigate:
# Check failed deliveries (admin only)
GET /admin/webhooks/deliveries
# Review individual delivery error messages
GET /admin/webhooks/deliveries/{delivery_id}Common causes:
- Webhook service errors
- Timeout processing
- Invalid data from TMI API
- External dependency failures
- Webhook-Integration - Webhook system overview
- Issue-Tracker-Integration - Integration patterns
- REST-API-Reference - Complete API documentation
- Addon Configuration Guide - Operator configuration, database schema, Redis monitoring
- Addon Design Document - Technical design specification
- Using TMI for Threat Modeling
- Accessing TMI
- Authentication
- Creating Your First Threat Model
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Timmy AI Assistant
- Metadata and Extensions
- Planning Your Deployment
- Terraform Deployment (AWS, OCI, GCP, Azure)
- Deploying TMI Server
- OCI Container Deployment
- Certificate Automation
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Component Integration
- Post-Deployment
- Branding and Customization
- Monitoring and Health
- Cloud Logging
- Database Operations
- Security Operations
- Performance and Scaling
- Maintenance Tasks
- Getting Started with Development
- Architecture and Design
- API Integration
- Testing
- Contributing
- Extending TMI
- Dependency Upgrade Plans
- DFD Graphing Library Reference
- Migration Instructions